How to pass a string into a ng-template via HTML? - html

Lets say I have an ng-template like so:
<ng-template #templateRef>
<button
(click)="testClick()"
Click me
</button>
<a
I'm a link
</a>
</ng-template>
And then I want to use it in my html template like
<ng-container
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="asdf"
>
</ng-container>
Is there any way for me to pass one additional variable into the template so that depending on where I use the template in the html, I can pass a different string that I can use to set the value of a custom directive via property binding? IE I really want the end result to be
<button
(click)="testClick()"
[customLocation]="top"
Click me
</button>
<a
[customLocation]="top"
I'm a link
>
</a>
and
<button
(click)="testClick()"
[customLocation]="bottom"
Click me
</button>
<a
[customLocation]="bottom"
I'm a link
>
</a>
So when I use the template like
<ng-container
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="asdf"
>
</ng-container>
I just want to leave everything that's currently there as-is and pass one more variable into the container that will get put as the value for customLocation. Can this be done? Any help is appreciated

templates have a context. Here we create a template variable named location thanks to the syntax let-location. The variable will be binded to the $implicit property of the context.
<ng-template #templateRef let-location>
<button
[customLocation]="location"
</button>
</ng-template>
<ng-container
[ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{$implicit: 'top'}"
>
</ng-container>
Note that it's possible to have template variables by naming them. It's explained in https://angular.io/api/common/NgTemplateOutlet.

Related

Change the buttons on the angular material Nav-bar based on page

I have my navbar deigned using angular material in my app.component.html page.
It initially contains LOGIN button, but when user is logged in successfully, login button should be hidden
Current approach:
I am disabling the login button based on boolean attribute in localstorage, but in this case, login button is not disabled until i refresh my page.
My Navbar is created as shared component, So Is there a way i can add new button based on page and data is updated without refreshing the page?
app.component.html:
<app-navbar></app-navbar>
<router-outlet></router-outlet>
Navbar.component.html:
<mat-toolbar color="primary" class="mat-elevation-z">
<span><mat-icon>post_add</mat-icon> ED-Planner</span>
<div class="spacer"> </div>
<button mat-button [routerLink]="['/user']">Home</button>
<button mat-raised-button color="accent" [routerLink]="['/user/signup']">Signup</button>
</mat-toolbar>
Any help would be appreciated
Well, user data usually is stored in localstorage.
If you create a service for example, you can store it there too, and provide it (make sure its providedIn root, so there will be only 1 instance).
After you log in your user, you can store it's data in the service, or localstorage (please pay attention for security), you can do something like this:
<ng-container *ngIf=isLoggedIn; else loggedOut>
//Your content when the user is logged in), few else buttons for example profile or something
</ng-container>
<ng-template #loggedOut>
//your login section
</ng-template>

How do I nest multiple ng-if properly?

I am facing an issue with multiple nested ngIf applied on ng-template elements in Angular.js, and I can't seem to get the perfect answer. I know workarounds but they are not optimized.
This is the code that I am trying to get running:
<div class="container">
<ng-template *ngIf="booleanA;then caseA else caseB">
<ng-template #caseA>
<el>1</el>
<el>2</el>
</ng-template>
<ng-template #caseB>
<ng-template *ngIf="booleanB">
<el>3</el>
<el>4</el>
<el>5</el>
</ng-template>
</ng-template>
</ng-template>
</div>
And these are the two solutions I have found to my problem:
Placing the ngIf on every child element inside of the #caseB element:
<ng-template #caseB>
<el *ngIf="booleanB">3</el>
<el *ngIf="booleanB">4</el>
<el *ngIf="booleanB">5</el>
</ng-template>
Placing the surrounding class="container" element inside both #caseA and #caseB, and applying the second ngIf to it:
<ng-template *ngIf="booleanA;then caseA else caseB">
<ng-template #caseA>
<div class="container">
<el>1</el>
<el>2</el>
</div>
</ng-template>
<ng-template #caseB>
<div *ngIf="booleanB" class="container">
<el>3</el>
<el>4</el>
<el>5</el>
</div>
</ng-template>
</ng-template>
The issue with these solutions is in the optimization. The first one checks multiple times for the same value, and the second one uses the same HTML element twice.
Is there any way I could make the original design work?
EDIT: The two solutions wouldn't appear as blocks of code, therefore I styled them as inline code. If you know how to fix that you'd be very welcome.
EDIT 2: Bringing some clarification as to what I am looking for: The end goal is not for the code to work, I have already found workarounds that I could use if all else fails.
The end goal is to get this code working only with Angular's logical element <ng-template> and by following the original design; and without the help of additional native elements like div, which would alter the DOM.
Two changes you need to make
Using ng-container
Using div instead of nested ng-template
please see this stackblitz
<div class="container">
<ng-container *ngIf="booleanA; then caseA; else caseB">
</ng-container>
<ng-template #caseA>
<span>1</span>
<span>2</span>
</ng-template>
<ng-template #caseB>
<div *ngIf="booleanB">
<span>3</span>
<span>4</span>
<span>5</span>
</div>
</ng-template>
</div>
You can try to use ngSwitch:
https://angular.io/api/common/NgSwitch
example code from Angular:
<container-element [ngSwitch]="switch_expression">
<some-element *ngSwitchCase="match_expression_1">...</some-element>
<some-element *ngSwitchCase="match_expression_2">...</some-element>
<some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
<ng-container *ngSwitchCase="match_expression_3">
<!-- use a ng-container to group multiple root nodes -->
<inner-element></inner-element>
<inner-other-element></inner-other-element>
</ng-container>
<some-element *ngSwitchDefault>...</some-element>
</container-element>
I have found a way to keep the mindset of my original design without adding unnecessary new DOM elements, duplicating HTML code, or double-checking the same variable.
<div class="container">
<ng-template *ngIf="booleanA;then caseA else caseB">
<ng-template #caseA>
<el>1</el>
<el>2</el>
</ng-template>
<ng-template #caseB>
<ng-template *ngIf="booleanB;then caseC"></ng-template>
<ng-template #caseC>
<el>3</el>
<el>4</el>
<el>5</el>
</ng-template>
</ng-template>
</ng-template>
</div>
Thanks to everyone for giving me other paths to explore, those will serve me well.

How to show data from nested web service array (Angular 4)

I made a project to pull data from web service API.
But the web service has nested arrays that needs to be displayed too, how can i access the data from nested JSON arrays?, what is the right way to write inside the HTML to get the data from the web service.
By the way when i fetch the first objects it shows correctly, only in the nested objects.
This is the response screenshot from Postman
This is the API link
This is the link of the project on Stackblitz
Click on sign in without user or password, then any school, then divisions.
divisions component is the one i'm asking about.
If I remember correctly, you have the data available.
Just use the dots to reach the data you need: response.data[0].grade[0].classes[1].grade_id
In the template you can do this:
<div *ngFor="let division of divisionList">
<div *ngFor="let grade of division.grade">
<div *ngFor="let class of grade.classes">
<span>{{ class.grade_id }}</span>
</div>
</div>
</div>
In your component you can start with this:
<ng-container *ngIf="divisionList">
<button name="American Primary"
*ngFor="let division of divisionList"
class="choose-list arrow-onclick1" data-toggle="collapse" data-target="#list1">
{{division.name}}
<i class="fa fa-arrow-right pull-right arrow-down-onclick1"
aria-hidden="true" style="margin-top:12px"></i>
</button>
<a routerLink="/editaisdivision"
style="text-decoration: none;">
<span class="fa fa-pencil pen-pen-pen" aria-hidden="true"
style="margin-left: 10px; color: #171b5e;"></span>
</a>
</ng-container>
Note that you can't use two structural (with *) directives in one tag, hence the ng-container.
Use JSON.parse(JSON_STRING) and you will get a JavaScript object that represents the data in the JSON.

How to open/close a angular-material menu

I recently started using angular-material and am struggling/unsure about opening/closing a mat-menu... I see in the examples on the angular-material documentation site that they assign an id to the menu and then apply a directive to the button that is used to toggle the menu. e.g. [matMenuTriggerFor]="menu"
How can I go about writing a directive that does that? I'm not sure how to pass a reference to a specific menu to a directive that then calls the toggle() method on the DOM element with that id?
The following code produces the errors:
Can't bind to 'matMenuTriggerFor' since it isn't a known property of 'button'.
There is no directive with "exportAs" set to "matMenu".
My code:
<li>
<button mat-icon-button [matMenuTriggerFor]="stockSystemMenu">
<mat-icon class="sn-item">
<i class="material-icons">archive</i>
</mat-icon>
</button>
<span class="sn-item" (click)="toggleMenu(stockSystemMenu)">Stok System</span>
<mat-menu #stockSystemMenu="matMenu">
<button mat-menu-item>
<mat-icon>
<i class="material-icons">chevron_right</i>
</mat-icon>
<span>Service 1</span>
</button>
</mat-menu>
</li>
There is confusion because Material introduced a breaking change as I understand it.. See material 2 Changelog - Breaking Changes
Starting with Material 2.0.0-beta.12. Use mat instead of md-*.. Seems only some of the docs at material.angular.io are updated with mat. Specifically, if you click view source and see md, I believe they have yet to replace it with mat.
So either update to Material 2.0.0-beta.12 and use mat-*, or use md-*.
"Your code is correct, you don't need to write matMenuTriggerFor directive, it is part of the API, make sure you have imported the MatMenuModule, MatButtonModule, and MatIconModule into your app module." - from comments

Angular2 *ngIf does not work correctly

In my code im using *ngIf which should only show one of the two at any times. But the problem is that it only shows the second one , even though the second one should not be shown at some points. Im using a for loop to show all elements, but as you can see in the picture, only the second one is shown even though the second one its value is -1. Thank you for helping!
Code:
<ng-template let-internship="rowData" pTemplate="body" *ngIf="favorite?.FavoritesIds.indexOf(internship?.InternshipId) === -1;"><!--TODO BRIAN ngif-->
<a class="btn btn-default" [routerLink]="['/student/stageopdrachten', internship.InternshipId, false]"><!--Not shown TODO-->
<i class="glyphicon"></i>Meer
</a>
</ng-template>
<ng-template let-internship="rowData" pTemplate="body" *ngIf="favorite?.FavoritesIds.indexOf(internship?.InternshipId) !== -1;"><!--TODO BRIAN ngif-->
<a class="btn btn-default" [routerLink]="['/student/stageopdrachten', internship.InternshipId, true]"><!--always shown-->
<i class="glyphicon"></i>Meer{{favorite?.FavoritesIds.indexOf(internship?.InternshipId)}}
</a>
</ng-template>
Image:
Use div instead of ng-template. You cannot use the syntactic sugar * with ng-template.
Structural Directives
The asterisk is "syntactic sugar" for something a bit more
complicated. Internally, Angular desugars it in two stages. First, it
translates the *ngIf="..." into a template attribute, template="ngIf
...", like this.
<div template="ngIf hero">{{hero.name}}</div>
Then it translates the template attribute into a
element, wrapped around the host element, like this.
<ng-template [ngIf]="hero">
<div>{{hero.name}}</div>
</ng-template>
UPDATE
Like #Daniel Cooke said, I should have used a div because the ng templates dont work that well when next to eachother. Rather use divs and only one ng template did it. So the working code:
<ng-template let-internship="rowData" pTemplate="body" ><!--TODO BRIAN ngif -->
<div *ngIf="favorite?.FavoritesIds.indexOf(internship?.InternshipId) === -1">
<a class="btn btn-default" [routerLink]="['/student/stageopdrachten', internship.InternshipId, false]"><!--niet bestaat TODO-->
<i class="glyphicon"></i>Meer
</a>
</div>
<div *ngIf="favorite?.FavoritesIds.indexOf(internship?.InternshipId) != -1;">
<a class="btn btn-default" [routerLink]="['/student/stageopdrachten', internship.InternshipId, true]"><!--wel bestaat-->
<i class="glyphicon"></i>Meer{{favorite?.FavoritesIds.indexOf(internship?.InternshipId)}}
</a>
</div>
</ng-template>
Do a {{favorite?.FavoritesIds.indexOf(internship?.InternshipId) | json}} to see whats in the variable
My tip is to place the if statement in a separate function.
Its possible that === tests on string while its a number.