Variabilizing the # from <mat-menu> - json

I'm currently trying to generate a dynamic menu from a JSON blob in my typescript files, Angular project.
I'm using the / component from Angular Material.
My json menu has this structure :
{
id: 0,
titreTranslate: 'menu-accueil',
isDescriptionRequired: true,
routerLink: '/accueil',
icon: faHome,
isAllowed: true,
hasSubOptions: false,
trigger: 'accueil'
}
My code look something like this :
<mat-toolbar-row class="navigation-row">
<span *ngFor="let option of menuOptions">
<button mat-button
[matMenuTriggerFor]="admin"
routerLink="{{option.routerLink}}"
(keyup.enter)="router.navigate([option.routerLink], { queryParams: option.queryParams })"
[routerLinkActive]="['active-menu']"
[queryParams]="option.queryParams"
class="link bouton-menu-gauche flex-row"
*ngIf="option.isAllowed"
>
<fa-icon
*ngIf="option.icon"
class="primary-color"
[icon]="option.icon"></fa-icon>
{{ option.titreTranslate | translate }}
</button>
<mat-menu #{{option.trigger}}="matMenu">
<span *ngFor="let subOption of option.menuOptions">
<button mat-menu-item
*ngIf="option.menuOptions"
routerLink="{{subOption.routerLink}}"
(keyup.enter)="router.navigate([subOption.routerLink], { queryParams: subOption.queryParams })"
[routerLinkActive]="['active-menu']"
[queryParams]="subOption.queryParams"
class="link bouton-menu-gauche flex-row">
<fa-icon
*ngIf="subOption.icon"
class="primary-color"
[icon]="subOption.icon"></fa-icon>
{{ subOption.titreTranslate | translate }}
</button>
</span>
</mat-menu>
</span>
</mat-toolbar-row>
The line with " <mat-menu #{{option.trigger}}="matMenu"> " is what concerns me here ; I've tried many ways to variabilize this #, such as putting it directly in the Json menu or trying different syntax ; It always fail, and won't give me the code structure i want.
If i had to guess, i'd say the generated code should look like : <mat-menu #"accueil"="matMenu">, with "" that fail to compile ; but i don't get any compilation errors, so i'm lost here.
Does anybody had to work with that kind of structure before ?
(apologies for my english if it's bad, i'm french)

Check out this answer:
https://stackoverflow.com/a/44441164/2025458
It's easier to search for the answer if you call things by the name they are given in the documentation, the '#' is a template reference or template variable
And since it's inside a structural directive (*ngfor or any other directive strating with *)
It binds to a template created by the structural directive, so every loop of the ngfor generates its own nested template with its own instance of the variable, so you can just use one name

Related

Pass TemplateRef with data to 3rd party library

I'm using ng-zorro-antd library and I'm trying to pass a ng-template without duplicating HTML.
The component (in the url) expects TemplateRef as an Input property (nzDot).
This is my ng-template:
<ng-template #dotTemplate let-step>
<div [ngSwitch]="step.status">
<span *ngSwitchCase="'active'" nz-icon nzType="info-circle" nzTheme="outline"></span>
<span
*ngSwitchCase="'error'"
nz-icon
nzType="info-circle"
nzTheme="fill"
class="text-volcano-6"
></span>
<span
*ngSwitchCase="'onboarding'"
nz-icon
nzType="info-circle"
nzTheme="outline"
class="text-yellow-6"
></span>
<span
*ngSwitchCase="'syncing'"
nz-icon
nzType="sync"
nzTheme="outline"
class="text-geekblue-6"
></span>
</div>
</ng-template>
What I'm trying to do is basically send this step object (let-step) as variable to the outlet, but I don't see a way to do this, as this is controlled by the ngTemplateOutlet inside of the 3rd party component.
Is there any smart way to do this?
I see that I can make 4 templates and then bind to the property like this:
step.status === 'Active'
? dotActiveTemplate
: step.status === 'syncing'
? dotSyncingTemplate
: step.status === 'onboarding'
? dotOnboardingTemplate
: dotErrorTemplate
But this solution seems bad and I would appreciate help.
Thanks!

How to iterate over single character in a string

How can I iterate a string using the *ngFor?
I have a string with binary code (e.g. 0010) and dependendig on a single bit I have to show a different icon.
<div class="row" *ngFor="let item of subscribedCommandBus2Easy; let i = index">
<span class="numberCircleBus2Easy col-md-2">
{{item}}
</span>
<i *ngFor="let num of commandsDecimal">
<i ng-repeat="let el in num">
<span [ngClass]="el =='0' ? 'off-icon' : 'on-icon'">
//is this the way I access the single character?
</span>
</i>
</i>
</div>
I tried this code but it does not work.
commandsDecimal is my array of binary string. I want to loop commandsDecimal at index i (suppose the element is 1010) and if the character at position y is 0 I have to show an icon otherwise the other icon and so on...
Any suggestion?
The best way is to do a split on your string. With a custom pipe:
#Pipe({
name: 'split'
})
export class SplitPipe implements PipeTransform {
transform(value: any, args?: any): any {
return value.split('');
}
}
And then iterate over it. like that:
<div *ngFor="let item of myString">
<div *ngFor="let num of item | split item">
// access num
</div>
</div>
Example: https://stackblitz.com/edit/angular-8bkywr
In your component typescript
function getSplit(string) {
return string.split('').map(number)
}
In the template
*ngFor="let num of getSplit(commandsDecimal)"
You can do this without the need for any code in your component. Also ng-repeat is AngularJS syntax, not Angular 2+. In Angular 2+, ngFor is used to iterate in the HTML.
<ng-container *ngFor="let num of commandsDecimal">
<i *ngFor="let el of num.split('')" [ngClass]="el === '0' ? 'off-icon' : 'on-icon'"></i>
</ng-container>

Reference ngFor value in component

I have a nested ngFor statement. I need to retrieve the value of my first ngFor on button click.
I have tried the following:
use template reference variable
use attribute binding
use Input decorator
This is my code:
<mat-expansion-panel *ngFor="let item of Datasource;">
<mat-expansion-panel-header style="display:flex" class="mat-row">
{{item.Header}}
</mat-expansion-panel-header>
<mat-selection-list [(ngModel)]="selectedOptions">
<mat-list-option *ngFor="let line of item.match; let i= index;" [value]="line">
<div class="container-name">
<div class="col-6">{{i}} - {line.user.Name }} vs {{ line.user.Address }}</div>
</mat-list-option>
</mat-selection-list>
<div style="text-align:center; padding: 20px">
<button mat-raised-button color="primary" (click)="submit()" type="submit">Add</button>
</div>
</mat-expansion-panel>
Can this be achieved?
Well, you need to clone that object properties first. As that object is linked to the template, when you manipulate it, it is manipulated on template too. You can use var obj = Object.assign({}, actual obj) and then do the manipulation on obj instead of actual one. Then it will not get affected in template. Hope it helps.

Ngx-datatable wrongly in (component) HTML how do I sort table by column name with a dropdown box (Angular 7)

I am needing to have a dropdown box that sits above a Ngx-datatable and that dropdown box needs to sort the Ngx-datatable by the values in the dropdown box. I am still new to Angular, so as I discovered that it is wrong to have the ngx-datatable in the component.html. How might I bootleg the datatable so that I can sort the rows by the values in the dropdown box?
Thinking that the method in the component.ts was linked to the datatable I was going to call that method in the dropdown box to sort it. It is completely separate!
component.html
<ngx-datatable class="expandable"
[rows]="rows"
[columns]="columns"
[headerHeight]="40"
[rowHeight]="'auto'"
[columnMode]="'force'" [limit]="20" [footerHeight]="50">
<ngx-datatable-column name="Header">
<ng-template let-value="value" let-row="row" ngx-datatable-cell-template>
<span class="custom-cell"><a (click)="method(content, id, false)">{{value}}</a></span>
</ng-template>
</ngx-datatable-column>
My dropdown box
<div ngbDropdown class="filter-dropdown-wrapper">
<button class="btn btn-light filter-button" id="input1" ngbDropdownToggle>Select an option</button>
<div ngbDropdownMenu aria-labelledby="inputmethod">
<!--<select (change)="getColumnNames($event.target.value)">
<option *ngFor="let element of rows" value="{{element.header}}"></option>
</select>-->
component.ts
columns = [
{ prop: 'header', name: 'Header' },
{ prop: 'notrelavant', name: 'Not Relevant' }, ];
What I need is: On click of the header value in dropdown box sort the datatable.
I have came up with a solution for yours. It is actually not very difficult if we break down the steps:
1) On the ngbDropdown, we populate the menu items with the columns of the datatable. we attach a click event binding on each individual menu items, and we bind them to the sort() method, which takes in the column property (column.prop) as a parameter.
<div ngbDropdown class="d-inline-block">
<button class="btn btn-light filter-button" id="input1" ngbDropdownToggle>Select an option</button>
<div ngbDropdownMenu aria-labelledby="inputmethod">
<button ngbDropdownItem *ngFor="let column of columns" (click)="sort(column.prop)">
{{ column.prop }}
</button>
</div>
</div>
2) Now, on our component.ts, we define our sort() method. rows represent the data in the datatable. We use localeCompare
to sort it in alphanumerical order. We create a shallow copy of rows to explicitly trigger change detection within the datatable to update the order of the data.
sort(prop) {
this.rows.sort((a, b) => a[prop].localeCompare(b[prop], 'en', { numeric: true }));
this.rows = [...this.rows];
}
I have created a working demo over here. Hope it helps in my explanation!

Angular4+ show/hide child element in HTML template

I am trying to implement a simple "hover on a comment to show a reply button" implementation in my angular application. Is there a way to achieve this effect by using template reference variables only?
Something along the lines of...
<mat-list>
<mat-list-item *ngFor="let comment of comments" #SomeRefToThisParticularElement (mouseenter)="SomeRefToThisParticularElement=true" (mouseleave)="SomeRefToThisParticularElement=false">
<h4>{{comment.text}}</h4>
<p> 3 replies </p>
<mat-icon *ngIf="SomeRefToThisParticularElement==true">reply</mat-icon>
</mat-list-item>
</mat-list>
Obviously, the above doesn't work cause angular won't let you assign or set template variables the way I have shown in the snippet above. But I would like to explore options to achieve this at the html template level.
Is this a good approach to do this?
Edit
Click here for a summary of possible solutions.
try this, Hide and show based on the hover index of an array.
<mat-list>
<mat-list-item *ngFor="let comment of comments; let i = index" (mouseenter)="commentIndex= i" (mouseleave)="commentIndex = -1">
<h4 mat-line>{{comment.text}}</h4>
<p mat-line style="font-size:x-small; color:rgba(0, 0, 0, 0.54)"> 3 replies </p>
<mat-icon *ngIf="commentIndex == i">reply</mat-icon>
</mat-list-item>
</mat-list>
Use a dynamic object to do this, like comment.isVisible. Now the isVisible object is dynamically created and it will be modify the value while hover in/out.
Please try this way
<mat-list>
<mat-list-item *ngFor="let comment of comments" (mouseenter)="comment.isVisible = true" (mouseleave)="comment.isVisible= false" >
<h4 mat-line >{{comment.text}}</h4>
<p mat-line style="font-size:x-small; color:rgba(0, 0, 0, 0.54)"> 3 replies </p>
<mat-icon *ngIf="comment.isVisible">reply</mat-icon>
</mat-list-item>
</mat-list>
Simplest Solution (Credit: Rahul Sharma) - use index of the element and set that as the value of a dynamic variable that is used within an *ngIf statement for show/hide behavior of an element.
Next Best Solution (Credit: Ramesh Rajendran) - Add a dynamic property to the iterable object. This assumes that the iterable object is not immutable.
Thanks to the respective contributors. Hope this helps someone in the future.
you can use css:
mat-icon{
color: transparent;
}
mat-icon:hover, mat-icon:active{
color:black;
}