How to add a custom directive div after nth ng-repeat item - html

In my code i have a bunch of items rendered by ng-repeat.
These items use the bootstrap class col-md-3
and i'd like to add a feature which allows the user to see a large detail panel by clicking on one of these items.
However i'd like to accomplish it by having only one detail panel in my DOM and moving it around.
The html is like this:
<div ng-repeat="thing in thingsCtrl.things">
<thing-widget class="col-md-3"></thing-widget>
</div>
<thing-detail ng-show="thingsCtrl.showDetail" class="col-md-12"> </thing-detail>
any chance to accomplish?
Thanks in advance.

You could try moving the thing-detail inside the ng-repeat and use $index to add it or not.
<div ng-repeat="thing in thingsCtrl.things">
<thing-widget class="col-md-3"></thing-widget>
<thing-detail ng-if="$index === nth" ng-show="thingsCtrl.showDetail" class="col-md-12"> </thing-detail>
</div>

Related

Angular - DragDropModule not working inside templates

I would like to use DragDropModule for my angular application so I can move panels that are stored inside templates (for practical purposes as well as for recursive plotting of child elements).
The problem that I have is that cdkDrag can't find the correct cdkDropList to drop into if cdkDrag is hidden within a template and is not directly nested under the HTML element. Example:
<div
cdkDropList
[cdkDropListData]="expanded.activities"
(cdkDropListDropped)="dropActivity($event)">
<div *ngFor="let activity of expanded.activities">
<ng-container [ngTemplateOutlet]="orangeProgramActivity"></ng-container>
</div>
</div>
<ng-template #orangeProgramActivity>
<div cdkDrag>This is just a test</div>
</ng-template>
With this code example, the orangeProgramActivity can be dragged anywhere but doesn't drop into the correct dropList as cdkDrag keyword can't find any droplist within its own template.
In the second example, everything works correctly and the item gets dropped into correct dropList:
<div
cdkDropList
[cdkDropListData]="expanded.activities"
(cdkDropListDropped)="dropActivity($event)">
<div *ngFor="let activity of expanded.activities">
<div cdkDrag>This is just a test</div>
</div>
</div>
I would like to achieve the same functionality as in the second example, but with the use of templates because I really need them for recursion. Unfortunately, I can't reveal the whole code as my employer wouldn't be happy with that.
All I need is some static reference for my cdkDrag that is inside a template, to point onto a correct element with dropList that is outside of the template.
These are the only solutions that I found on the internet and they don't seem to work for me:
Material 7 Drag and Drop ng-template incompatibility
CdkDragDrop and ngTemplateOutlet
This is my first question on Stack Overflow and I'm new to angular, so I'm sorry for any confusion in my post, and thanks for any help in advance!
This might not work for all use cases, but you could just write it like this:
<div
cdkDropList
[cdkDropListData]="expanded.activities"
(cdkDropListDropped)="dropActivity($event)">
<div *ngFor="let activity of expanded.activities" cdkDrag>
<ng-container [ngTemplateOutlet]="orangeProgramActivity"></ng-container>
</div>
</div>
<ng-template #orangeProgramActivity>
<div>This is just a test</div>
</ng-template>
I was lucky that I only need to reorder elements inside an array in which they are and I don't need to transfer them into other containers. So for my "root" activities, I used your simple solution to wrap it all in a draggable element.
<div cdkDropList [cdkDropListData]="expanded.activities" (cdkDropListDropped)="dropActivity($event)">
<!-- For every object in array make a container with template and an icon that will serve as a handle -->
<div *ngFor="let activity of expanded.activities">
<div cdkDrag>
<div cdkDragHandle>
<!-- Handle icon from FontAwesome -->
<i class="fas fa-grip-vertical"></i>
</div>
<!-- Container with a template that we need -->
<ng-container [ngTemplateOutlet]="orangeProgramActivity" [ngTemplateOutletContext]="{act: activity}"></ng-container>
</div>
<!-- Recursive template for children -->
<ng-container [ngTemplateOutlet]="childActivitiesRecursion" [ngTemplateOutletContext]="{act: activity}"></ng-container>
</div>
And for my recursion, I created a droplist inside a template that serves only for reordering these children activities.
(I had problems with pasting template so the ng-template tag is not terminated at the end of the code)
<ng-template let-act="act" #childActivitiesRecursion>
<!-- Droplist inside a template for reordering child activites -->
<div cdkDropList [cdkDropListData]="act.childActivities" (cdkDropListDropped)="dropActivity($event)">
<!-- Iterates through all child activities -->
<div *ngFor="let childActivity of act.childActivities">
<div cdkDrag>
<div cdkDragHandle>
<i class="fas fa-grip-vertical"></i>
</div>
<!-- Different template for child activities -->
<ng-container [ngTemplateOutlet]="blueProgramActivity" [ngTemplateOutletContext]="{act: childActivity}"></ng-container>
</div>
<!-- Recursion for even deeper child activites -->
<ng-container [ngTemplateOutlet]="childActivitiesRecursion" [ngTemplateOutletContext]="{act: childActivity}"></ng-container>
</div>
</div>
I will accept Richard's answer as a solution because if you are able to solve it like this, it's definitely the most elegant solution you can do. But there would be still an issue if you needed to transfer items into different containers after using templates for recursion!

Using a conditional in HTML/Typescript?

In my project I'm using a mat-dialog to display a description of an object. The objects are generated through ngFor, like this:
<mat-card id="CARDBOX" *ngFor="let box of box">
<img class="logoy" src="{{box.image}}" height=35px>
{{box.button_name}}
<input type="image" id="info" title="Click for description" src="{{box.info}}" (click)="openDialog()" height=20px/>
</mat-card>
It's a basic card object that has an info icon that when clicked, opens the dialog, which looks like this:
<title mat-dialog-title></title>
<div mat-dialog-content *ngFor="let box of box">
{{box.description}}
</div>
<div mat-dialog-action>
<button mat-button (click)="onNoClick()">Close</button>
</div>
This works. However, it is displaying EVERY description in box, rather than just the corresponding one. I know this is because of the ngFor running through every item. Is there a way so that it will only display the one correct description, perhaps through use of some kind of conditional? I would ideally like to keep everything as abstracted as possible, I figured using some kind of conditional in the HTML would make the most sense but I'm not sure if that exists? Please let me know if you need more detail.
<div mat-dialog-content *ngFor="let box of box">
{{box.description}}
</div>
Your ngFor directive is looping through with an element whose name (and thus its reference if I'm not making a mistake here) is equal to its container.
Have you tried this?
<div mat-dialog-content *ngFor="let boxEl of box">
{{boxEl.description}}
</div>
Your code might not be able to differentiate a "box" (element) from a "box" iterable.

Angular: How can I remove wrapper (parent element) without removing the child?

I have seen this answer but it answers for jquery and not angular/typescript.
My question is similar to this question, but my question is seeking a solution for angular.
How can I remove wrapper (parent element) without removing the child in Angular using typescript or scss? (If it is possible by both kindly show both methods).
For example how to programatically manupulate the dom below from
<div class="wrapper">
<span>This is It!</span>
</div>
<div class="button">Remove wrapper</div>
to: (After clicking on the button I would like to have the dom iook like below)
<span>This is It!</span>
<div class="button">Remove wrapper</div>
EDIT:
Kindly also show how to set it so that it can add the wrapper div back when I click the button again. Basically toggling the wrapper div.
I think you can simulate using ng-template and two divs, some like
<div *ngIf="toogle" class="wrapper">
<ng-content *ngTemplateOutlet="template"></ng-content>
</div>
<ng-content *ngTemplateOutlet="!toogle?template:null"></ng-content>
<ng-template #template>
<hello name="{{ name }}"></hello>
</ng-template>
<button (click)="toogle=!toogle">toogle</button>
See a example in stackblitz

Searching DOM elements for ID value in angular

Is there a way to search DOM elements for a specific element ID in angular 4? I am trying to search for an ID of a div in my ngb-accordion inside the ng-template.
I am looking for that Id div id="3005"
<ng-template class="super" ngbPanelTitle>
<div class="row heads">
<div class="col-sm">
<div [class]="className" id="3005" #mydiv></div> <b>Just a description</b>
</div>
</div>
</ng-template>
you can access your div by using#ViewChild.
Please go through this.
You can find it using querySelector like so:
angular.element(document.querySelector('#3005'))[0]
happy coding :)

How to use 2 ui-grid with ng-hide and ng-grid?

I am new to angularjs and I want create one Page in which I want to add 2 ui-grid with one button called "other-grid" first time it must Load 1st grid and when we click again then 1st grid must replace with 2nd grid But some Problems are there I want to use ng-show and ng-hide it works for first grid but not loading 2nd grid Properly Why So and Any Solution Please..
You are probably better off using ng-if when dealing with ui-grid.
As for ng-show, it does weird things when rendering ui-grids (refer to: https://github.com/angular-ui/ui-grid/issues/4559)
Here is plunker of 2 grids with a toogle button:
http://plnkr.co/edit/IGF6X7SqtFdFSk8jiory?p=preview
<button ng-click="toggleuigrid()">toggle grid</button>
<div class="row">
<div class="span4" ng-if="show">
<div id="grid1" ui-grid="gridOptions1" class="grid" ></div>
</div>
<div class="span4" ng-if="!show">
<div id="grid2" ui-grid="gridOptions2" class="grid" ></div>
</div>
</div>
Make sure you give the 'other' hidden grid proper height and width so that the framework knows the grid dimensions
You can try using ng-if, ng-show, ng-hide and make the other grid visible when you click on the button - 'other grid'
Here is the official UI grid tutorial which does something similar to what you want