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

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.

Related

How to pass a string into a ng-template via 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.

Angular Accordion accessibility issues

have an issue with the expansion of the accordion containing a list of items using a keyboard.
There is a complication in that all the accordion's data is being populated from an API, so a lot of the solutions that I had seen are hardcoded. For example, using the information found in the article below didn't work as the unique values are hard coded:
https://www.hassellinclusion.com/blog/accessible-accordion-pattern/
I've added in a lot of the appropriate ARIA labels and navigating via tab around it works well as does the screen reader, but I can't get the accordion to expand.
I tried to talking it from another angle by getting the enter button to be interpreted as a click on the element to expand the accordion but got completely lost trying to do such a thing in Angular as inserting Vanilla JS is not as straight forward as it would seem.
Here is the code, it's spread over three components so I've compiled into one and removed some styling classes for legibility:
<div tabindex="0" (keydown.enter)="myFunction()" role="button">
<h2 tabindex="0">
<img>
<i tabindex="0"></i>{{ organiser.name }}
</h2>
//this component displays the selected item. the accessibility on this works fine
<app-selected-area role="region">
</app-selected-area>
</div>
<div class="content">
//this component displays the items that can be selected
<app-skill-item class="item">
<div tabindex="0" role="button">
<h3>{{ skill.name }}</h3>
<button *ngIf="updateable && isSelected()" (click)="select()">
Remove
<span class="screen-reader-only">
{{ skill.name }}
</span>
skill
</button>
<button *ngIf="updateable && !isSelected()" (click)="select()" tabindex="0">
Add
<span class="screen-reader-only">
{{ skill.name }}
</span>
skill
</button>
</app-skill-item>
</div>
Any help or hints would be much appreciated!
Ended up finding the answer for this in the follow question/thread:
Trigger click in Typescript - Property 'click' does not exist on type 'Element'
let element: HTMLElement = document.getElementsByClassName('btn')[0] as HTMLElement;
element.click();
this was the following code that let me manipulate the accordion via the enter key as a click

How to render the materail element added using innerHTML in typescript?

Adding material design elements using innerHTML attribute renders a result. But if we develop a html page with the same material design content without using innerHTML attribute produces different result.
here is the example
html file material design content directly inserted:
<div id="requestForm">
<button mat-raised-button color="primary" id="proceed_btn" style="margin-right: 15px;" (click)="reset()" *ngIf="request_form_selectedIndex > 2">Submit<i class="material-icons right material_icons_btn">send</i>
</button>
</div>
in the browser's dom the above page rendered like this.
expected result
<div id="requestForm">
<button _ngcontent-c3="" color="primary" id="proceed_btn" mat-raised-button="" style="margin-right: 15px;" class="mat-raised-button mat-primary ng-star-inserted" ng-reflect-color="primary">
<span class="mat-button-wrapper">Submit
<i _ngcontent-c3="" class="material-icons right material_icons_btn">send</i>
</span>
<div class="mat-button-ripple mat-ripple" matripple="" ng-reflect-centered="false" ng-reflect-disabled="false" ng-reflect-trigger="[object HTMLButtonElement]"></div>
<div class="mat-button-focus-overlay"></div>
</button>
But when I try to insert the material element using innerHTML attribute, it does not render the same result.
html file:
<div id="requestForm">
</div>
ts file:
htmlElement1 = "<button mat-raised-button color=\"primary\" id=\"proceed_btn\" style=\"margin: 95px 15px;\" (click) =\"reset()\" *ngIf=\"request_form_selectedIndex > 2\">Submit <i class=\"material-icons right material_icons_btn\">send</i> </button>"
document.getElementById("requestForm").innerHTML = htmlElement1;
in the browser's dom the above page renders the following result.
but I want the above mentioned expecpted result while adding material
element through the innerHTML attribute.
my result
<div _ngcontent-c3="" id="requestForm">
<button mat-raised-button="" color="primary" id="proceed_btn" style="margin: 95px 15px;" (click)="reset()" *ngif="request_form_selectedIndex > 2">Submit
<i class="material-icons right material_icons_btn">send</i>
</button>
</div>
what shoud I do to get my expected result?
Angular sanitizes DOM manipulation so that unsafe (potentially hackable) things won't work. The tag is obvious, but also won't work as well as others. You haven't said what you are trying to achieve, so it's not possible to make a good recommendation other than the obvious - insert DOM from the DOM (like your first example). If you need conditional insertion, use ngIf or ngSwitch.

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.