I have the following code, which I stole from here:
https://github.com/angular/material2/blob/master/src/demo-app/expansion/expansion-demo.html
<mat-expansion-panel class="mat-expansion-demo-width" #myPanel>
<mat-expansion-panel-header [expandedHeight]="expandedHeight" [collapsedHeight]="collapsedHeight">
<mat-panel-description>Click here to change view format.</mat-panel-description>
<mat-panel-title>View Controls</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
This is the content text that makes sense here.
<mat-checkbox>Trigger a ripple</mat-checkbox>
</ng-template>
foo bar baz
<mat-action-row>
<button mat-button (click)="myPanel.expanded = false">CANCEL</button>
</mat-action-row>
</mat-expansion-panel>
One question - I am confused, because the content inside the <ng-template> tag does not display, however "foo bar baz" does display. So what is the purpose of the content inside <ng-template> and why is it not displaying?
<ng-template> doesn't render until you call it. Try this:
<mat-expansion-panel class="mat-expansion-demo-width" #myPanel>
<mat-expansion-panel-header [expandedHeight]="expandedHeight" [collapsedHeight]="collapsedHeight">
<mat-panel-description>Click here to change view format.</mat-panel-description>
<mat-panel-title>View Controls</mat-panel-title>
</mat-expansion-panel-header>
<ng-container *ngTemplateOutlet="matExpansionPanelContent"></ng-container>
foo bar baz
<mat-action-row>
<button mat-button (click)="myPanel.expanded = false">CANCEL</button>
</mat-action-row>
</mat-expansion-panel>
<ng-template #matExpansionPanelContent> <-- Note the #hashtag
This is the content text that makes sense here.
<mat-checkbox>Trigger a ripple</mat-checkbox>
</ng-template>
This way you can build the <ng-template> once, and re-use it all of the place.
Related
I am new to angular and ngx-datatable and before asking this questions on SO. I've already gone through the answers with same problem which I'm facing related to adding custom buttons in each row in ngx-datatable of angular.
My HTML template looks like this:
<ngx-datatable [rows]="rows" [loadingIndicator]="loadingIndicator" class="bootstrap"
[selected]="selected" (activate)="onActivate($event, NewEventContent)">
<ngx-datatable-column *ngFor="let column of columns; let i = index;"
name="{{column.name}}" prop="{{column.prop}}">
<ngx-datatable-column name="Actions" prop="skuCode"></ngx-datatable-column>
<ng-template let-value="value" let-row="row" let-rowIndex="rowIndex"
*ngIf="column.name==='Actions'" ngx-datatable-cell-template>
<button type="button" class="btn btn-outline-success">Success {{rowIndex}}</button>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
My .ts file look like this
columns = [{name: 'Actions', prop: 'Id' }...];
I have no idea what I'm doing wrong it this and I've seen a similar type of approach in other answers of similar type of question but none of them worked for me.
Kindly help.
I've found an alternative way to solve this problem and successfully implemented custom button in each row. So I thought to answer the question so that it could be helpful for anyone.
After the change, my HTML template look like this.
<ngx-datatable [rows]="rows" class="material" [loadingIndicator]="loadingIndicator"
[columnMode]="'force'"
[selected]="selected" (activate)="onActivate($event, NewEventContent)"
[headerHeight]="50" [footerHeight]="50" [rowHeight]="'auto'" [columns]="columns">
<ngx-datatable-column *ngFor="let column of columns;
let i = index;" name="{{column.name}}"
prop="{{column.prop}}">
</ngx-datatable-column>
<ngx-datatable-column name="Actions" sortable="false" prop="Id">
<ng-template let-row="row" let-value="value" let-rowIndex="rowIndex"
ngx-datatable-cell-template>
<button class="btn btn-dark" (click)="onSelect(row)">
Edit{{rowIndex + 1}}
</button>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
kindly pay attention to the ng-template part and onSelect(row) function. The above solution works very well in my case.
Original answer
https://github.com/swimlane/ngx-datatable/issues/489#issuecomment-356765048
I am using Angular 8 with Angular Materials to build a multi-level menu. I can get the menu to work by using recursion for each level. I recursively call the same directive that displays each level of the menu.
This all works, and the menu is built as expected. However, the menu does not behave as expected. Examples I have seen, when you hover over an item, the nest item is opened, and if you move off an item, its child is closed.
For example, this is a simple version I made:
https://stackblitz.com/edit/dynamic-sidenav-multi-level-menu-tvim5b?file=app/app.component.html
Problem
My issue is when I build my menu, if I click on an item, the child opens. However, I can never get the child to close unless I click off the menu altogether. It is not behaving like the above example.
Question
How can I get my example to be have like the above, and close menu items (children) when the items loses focus?
Info
I have not put my specific example in StackBlitz because I don't own the code, and it needs backend services to support the implementation.
I think my issues are because I am building the menu items recursively , and the [matMenuTriggerFor] is referencing the menu in the next recursion.
Code
sidenav-list.component.html
<mat-nav-list>
<!-- Add the Home item -->
<a mat-list-item routerLink="/home" (click)="onSidenavClose()"><mat-icon>home</mat-icon><span class="nav-caption">Home</span></a>
<!-- Recurse over the app-sidenav-item -->
<app-sidenav-item *ngFor="let item of navItems" [item]="item" [depth]="depth+1" [sidenavClose]="sidenavClose"></app-sidenav-item>
</mat-nav-list>
sidenav-item.component.html <app-sidenav-item>
<div>
<button mat-button *ngIf="depth === 1" [matMenuTriggerFor]="menu"><mat-icon>play_arrow</mat-icon>{{item.name | titlecase}}</button>
<button mat-menu-item *ngIf="depth > 1" [matMenuTriggerFor]="menu">{{item.name}}</button>
<mat-menu #menu="matMenu">
<button *ngIf="item.actions.getItems" mat-menu-item (click)="onItemSelected(item, 0)"><mat-icon>list</mat-icon>Get Items</button>
<button *ngIf="item.actions.updateItem" mat-menu-item (click)="onItemSelected(item, 1)"><mat-icon>edit</mat-icon>Update Items</button>
<button *ngIf="item.actions.addItem" mat-menu-item (click)="onItemSelected(item, 2)"><mat-icon>add</mat-icon>Add Item</button>
<app-sidenav-item *ngFor="let child of item.children" [item]="child" [depth]="depth+1" [sidenavClose]="sidenavClose"></app-sidenav-item>
</mat-menu>
</div>
Screen Print
As you can see, I am able to open more than one item on separate nodes. I cannot get the previous one to close. Also, it only responds to clicks and not mouse hover.
Ok here is the thing, You have to preprocess some data 2 way, that means in your object you have to know if it has children to enable more hierarchy level and you need to know which parent it came from to filter it to build this
and your html should look like this. Since you know you can go 3 - 4 levels you generate template for those levels and play with data when it is there.
There is also another #input for MatMenu called matMenuTriggerData with which the parent will trigger data to child.
<button mat-button [matMenuTriggerFor]="level1" [matMenuTriggerData]="getData(null, 1)">Animal index</button>
<mat-menu #level1="matMenu">
<ng-template matMenuContent let-data="data">
<ng-template ngFor let-item [ngForOf]="data">
<button mat-menu-item *ngIf="item.children" [matMenuTriggerFor]="level2"
[matMenuTriggerData]="getData(item, 2)">{{item.label}}</button>
<button mat-menu-item *ngIf="!item.children">{{item.label}}</button>
</ng-template>
</ng-template>
</mat-menu>
<mat-menu #level2="matMenu">
<ng-template matMenuContent let-data="data">
<ng-template ngFor let-item [ngForOf]="data">
<button mat-menu-item *ngIf="item.children" [matMenuTriggerFor]="level3"
[matMenuTriggerData]="getData(item, 3)">{{item.label}}</button>
<button mat-menu-item *ngIf="!item.children">{{item.label}}</button>
</ng-template>
</ng-template>
</mat-menu>
<mat-menu #level3="matMenu">
<ng-template matMenuContent let-data="data">
<button mat-menu-item *ngFor="let item of data">{{item.label}}</button>
</ng-template>
</mat-menu>
Note the last level has no more triggers.
you can write a function for your filtered data
getData(selected, requested) {
return selected ? {
data:
this['level' + requested].filter(item => item.parent === selected.value)
} : { data: this.level1 };
}
Each item will contain value, label, parent and hasChildren in different levels, you can directly hook up with api make sure the object is passed has a data attribute , see functon getData
You can checkout this solution at https://stackblitz.com/edit/angular-yfslub
Hope you can modify to your needs.
I have this html code
<empty-list [hidden]="!emptylist" text="There is currently No User"</empty-list>
<div *ngFor="let userObser of userObservables">
<ion-item #emptylist *ngIf="userObser | async as user">
<h2>{{user.displayName}}</h2>
<h3>{{user.email}}</h3>
</ion-item>
</div>
I want to show the empty list if there is no user & hide it if there is at least one.
I know I can do it using a subscribe method,but I want to use async pipe also I need to unsubscribe each time I use a subscribe which is really not efficient.
My question is there a way I could create a local variable inside the ion-item the ntest test if it exists outside & therefore use it in the hidden input ? It's just a suggestion I can't really seem to make it work.
You can use a simple else inside the ngIf to display another template if the condition doesn't match:
<ng-container *ngIf="userObservables.length > 0; else emptyList">
<ng-container *ngFor="let user of userObservables">
<ion-item *ngIf="user | async as user">…</ion-item>
</ng-container>
</ng-container>
<ng-template #emptyList>
<empty-list>…</empty-list>
</ng-template>
Note that I also replaced your seemingly unnecessary div with a ng-container to avoid unnecessary DOM nodes.
As a side note to your own suggestion: template references cannot be accessed from outside the template boundary. Structural directives like *ngIf and *ngFor create their own template, so this becomes a boundary. That's why
<h2>{{ ref.innerText }}</h2>
<ng-container>
<span #ref>Hello, World</span>
</ng-container>
will work, but
<h2>{{ ref.innerText }}</h2>
<ng-container *ngIf="true">
<span #ref>Hello, World</span>
</ng-container>
won't work.
I am using swimlane/ngx-datatable component and I developed a wrapper for ngx-datatable.
Wrapper:
<div class="wrapper">
<ngx-datatable [rows]="rows">
<ng-content></ng-content>
</ngx-datatable>
</div>
Use:
<app-wrapper>
<ngx-datatable-column name="Name">
<ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value" let-row="row">
<span>{{ value }}</span>
</ng-template>
</ngx-datatable-column>
</app-wrapper>
This does not work, custom html that I wrote inside app-wrapper is not passed to the component. How would someone go through this if the component already uses ng-template?
This is not directly related to ngx-datatable. Angular doesn't currently support content projection with ng-content more that 2 levels of components deep. No, using ng-content inside ngx-datatable inside app-wrapper will not work.
I thought this was pretty easy, but i don't have a clue of how to make it work, my last try is on the style attribute in the button tag, i've searched the web for like and hour and nothing works.
<ion-header>
<ion-navbar color="danger">
<ion-title>Cadenas</ion-title>
</ion-navbar>
</ion-header>
<ion-content container class="card-cadenasmercados">
<button id="sucursal"
*ngFor="let cadena of cadenasCollection"
[navPush]="sucursalesPage"
[navParams]="cadena"
style:background='{{cadena.img}}'>
{{cadena.nombre}}</button>
</ion-content>
You can use ngStyle: https://angular.io/api/common/NgStyle
<button id="sucursal"
*ngFor="let cadena of cadenasCollection"
[navPush]="sucursalesPage"
[navParams]="cadena"
[ngStyle]="{'background': cadena.img}">
{{cadena.nombre}}</button>
this assumes cadena.img is something like "url(someimage.png)"