how to create custom tree model WITHOUT mat-tree in angular? - html

I have a tree structure like below :
every item can have unlimited nested children. I created it like below but it only shows the parent and their children. It doesn't show the children of their children(only shows 2 level). This is the html code:
<div *ngFor="let parent of phonebookTreeModelArray; let i = index">
<div
class="tree-parent"
(mouseenter)="showButtons(i)"
(mouseleave)="hideButtons(i)"
>
<p class="phonebook-title" (click)="displayChilds(i)">
{{ parent.name }}
</p>
</div>
<div
class="tree-children"
*ngIf="parent.children.length > 0 && currentDisplayChildIndex === i"
>
<div class="tree-child" *ngFor="let child of parent.children">
<p class="phonebook-title">
{{ child.name }}
</p>
</div>
</div>
</div>
I don't know how to implement it when the nested tree is infinite.

Related

Property 'x' does not exist on type 'y' in Angular ngIf

I am creating an Angular page to display the results of football matches and would like a warning message to be displayed when there are no matches. I used an ngIf to check if the array containing all matches is different from 0 but specifying in the else the ng-template to refer to gives me an error "Property 'noMatch' does not exist on type 'MainPageComponent'".
This is the piece of html code in which the matches are displayed, and if there are none, I would like the message contained in the ng-template to be displayed.
<div *ngIf="live.length > 0">
<h1 style="color: red;">Live ScoreBoard</h1>
<div *ngFor="let data of live">
<div class="title-box">
<p id="elapsed">{{ data.fixture.status.elapsed }}'</p>
</div>
<div class="title-box">
<div class="team">
<img id="homeLogo" src="{{ data.teams.home.logo }}">
<p id="homeName">{{ data.teams.home.name }}</p>
</div>
<p id="goals">{{ data.goals.home }} - {{data.goals.away}}</p>
<div class="team">
<img id="awayLogo" src="{{ data.teams.away.logo }}">
<p id="homeName">{{ data.teams.away.name }}</p>
</div>
</div>
<hr>
</div>
<ng-template #noPartite>
<p>No matches are scheduled today, however you can check the results of past matches <a routerLink="/matchday">here</a>and the standings here.</p>
</ng-template>
</div>
This is what the terminal returns
Error: src/app/Components/main-page/main-page.component.html:3:43 - error TS2339: Property 'noPartite' does not exist on type 'MainPageComponent'.
3 <div *ngIf="live.length > 0; else noPartite">
~~~~~~~~~
src/app/Components/main-page/main-page.component.ts:7:16
7 templateUrl: './main-page.component.html',
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error occurs in the template of component MainPageComponent.
The position of noPartite template is important. You have to move out from <div> element with *ngIf as below:
<div *ngIf="live.length > 0; else noPartite">
...
</div>
<ng-template #noPartite>
...
</ng-template>
Demo # StackBlitz

Restructure the display of instantiated elements in *ngFor

I am currently just adding randomized numbers to an array and displaying them to the user.
<div *ngFor="let temp of randomIntArray; let i = index">
<div *ngIf="i == randomIntArray.length - 1">
This is the real random number {{ temp }}
<div>
<button (click)="addRandomValueIntoRandomIntArray()">
add random number
</button>
</div>
</div>
<div *ngIf="i != randomIntArray.length - 1">
{{ temp }}
</div>
</div>
I think I understand what's happening here as I am creating a new element on the DOM each time the user clicks : addRandomValueIntoRandomIntArray() as its increasing the length of the randomIntArray.
Due to the: *ngIf="i == randomIntArray.length - 1 this will always be the last element and will be always be displayed at the bottom. Is there any feasible way for me to to swap them around and have all the new elements created at the bottom going downwards instead? Below is an image of how it currently looks.
You can reverse the order of ngFor items using randomIntArray.slice().reverse()
and you need to change ngIf condition to i==0.
app.component.html
<div *ngFor="let temp of randomIntArray.slice().reverse(); let i = index">
<div *ngIf="i ==0">
This is the real random number {{ temp }}
<div>
<button (click)="addRandomValueIntoRandomIntArray()">
add random number
</button>
</div>
</div>
<div *ngIf="i != randomIntArray.length - 1">
{{ temp }}
</div>
</div>

highlight word by start and end matches using angular 7

I have a collection of array which is having datas like
[0]: username {kavya} secret {password}
[1]: lorem ipsem text data value
[2]: lorem {{lrm}} datas {{pp}}
I am using foreach to show this data in frontend with
<div *ngFor="let data of output;let i=index">
<div *ngIf="data.includes('{') || data.includes('{{');else isNotEdited;" >
<div class="variable-textarea" contenteditable="false" >
<span>
{{data | slice:' ' }}
</span>
</div>
</div>
<ng-template #isNotEdited>
<ngx-md>{{data}}</ngx-md>
</ng-template>
</div>
Here I achieved like 0,2 row of div will be editable and in case of 1st array is non-editable.
But I want to do like specific matches which word starts with { or {{ and that particular word needs to be highlight and editable.
Is there any option to do in this way
Thanks in advance.
You could split the data into words:
<div *ngFor="let data of arr">
<span *ngFor="let word of data.split(' ')">
<span *ngIf="word.indexOf('{') > -1;else isNotEdited;">
<span class="variable-textarea-2" contenteditable="true">
{{word | slice:' ' }}
</span>
</span>
<ng-template #isNotEdited>
<span class="variable-text-2" contenteditable="false">
{{word}}
</span>
</ng-template>
</span>
</div>
Check this Stackblitz example I made based on your code: https://stackblitz.com/edit/angular-pkg6i9
this is a performance nightmare, you don't want to be running this many functions in template, and your format isn't helping you. map your data ahead of time into a friendlier view model:
this.mappedOutput = this.output.map(data => {
const editable = data.includes('{'); // checking for doubles is redundant
return {
editable,
data: !editable
? data
: data.split(' ')
.map(word => ({
word,
editable: word.trim().startsWith('{') && word.trim().endsWith('}')
}))
};
})
run this whenever your output changes, then use it in template:
<div *ngFor="let data of mappedOutput;let i=index">
<div *ngIf="data.editable;else isNotEdited;" >
<div class="variable-text">
<ng-container *ngFor="let word of data.data">
<div *ngIf="word.editable; else wordTmp" class="variable-textarea inline" contenteditable="true" >
<span>{{word.word}}</span>
</div>
<ng-template #wordTmp>
{{word.word}}
</ng-template>
</ng-container>
</div>
</div>
<ng-template #isNotEdited>
<ngx-md>{{data.data}}</ngx-md>
</ng-template>
</div>
and adjust the styles by adding this to your css:
.variable-textarea.inline {
display: inline-block;
width: fit-content;
margin: 0;
}
here's an edited blitz: https://stackblitz.com/edit/angular-arayve?file=src/app/app.component.html

display multiple nested data with angular6

I'm receiving JSON data from an API which has some child objects as well. The API has a menu level and down the menu, it's having meals. What I want to do is to display meals relating to each menu under the menu
JSON from API
[{"id":6,"name":"Menu 1","serveDate":"2019-05-10","meals":[{"id":13,"name":"Rice with Stew","description":"rice","image":"","mealType":"BREAKFAST","unitPrice":5,"status":"ENABLED"}]},{"id":5,"name":"Menu 2","serveDate":"2019-06-10","meals":[{"id":13,"name":"Corn Flakes,"description":"Flakes","image":"","mealType":"BREAKFAST","unitPrice":5,"status":"ENABLED"}]},{"id":4,"name":"Menu 3","serveDate":"2019-07-10","meals":[]}]
HTML
<div *ngFor="let item of menuList">
<h2>Menu</h2>
{{item.name}} - {{item.servate}}
<h2 *ngFor="let item of menuList.meals">Meals</h2>
{{item.name}} - {{item.mealType}}
</div>
JS
getMenus() {
this.menuServices.menuList(this.pagedData)
.subscribe(
response => {
if (response && response.code === HttpStatus.OK) {
this.menuList = response.data;
}
},
);
}
Any help on how to make this work correctly the way it should work?
<div *ngFor="let menu of menuList">
<h2>Menu</h2>
{{menu.name}} - {{menu.servate}}
<h2>Meals</h2>
<ng-container *ngFor="let meal of menu.meals">
{{meal.name}} - {{meal.mealType}}
</ng-container>
</div>
Using this way you don't have to add unnecessary divs or any other html tag for looping in angular.
this is the perfect way to do nested loops without changing your html
No need to access the main list as you have your meals array in the item object.
Change HTML Code to:
<div *ngFor="let item of menuList">
<h2>Menu</h2>
{{item.name}} - {{item.servate}}
<h2>Meals</h2>
<div *ngFor="let item of item.meals">
{{item.name}} - {{item.mealType}}
</div>
</div>
When you're doing something like let item of menuList that means the item variable should be used to refer to an individual item within your loop. To avoid confusion, I'd also recommend naming these item vars for nested loops differently.
Another important thing to keep in mind that all the markup that you want to be output for each array item should be wrapped with an element with *ngFor. It's not the case with your <h2> tag being printed for each meal, but not the meal description.
Edit the template as follows:
<div *ngFor="let menuItem of menuList">
<h1>Menu</h1>
<h2>{{menuItem.name}} - {{menuItem.serveDate}}</h2>
<p>maybe description here</p>
<h3>Meals</h2>
<p *ngFor="let mealItem of menuItem.meals">{{mealItem.name}} - {{mealItem.mealType}}</p>
</div>

Can you use a ngFor with a modulus operator in Angular 2

I'm writing out images to the web page. Every three images I would like to start a new row. Does angular 2 support this?
You can achieve it by doing following:
<div *ngFor="let t of temp(math.ceil(arr.length/3)).fill(); let i = index" class="row">
<div *ngFor="let item of arr.slice(3*i,3*i + 3);" class="item">
{{item}}
</div>
</div>
And in your component:
export class App {
temp = Array;
math = Math;
arr= [1,2,3,4,5,6,7,8,9,10,11];
}
Here's working Plunker
You can access the index of the iteration from the *ngFor like so:
*ngFor="let x of container; let i = index;"
you can then reference that index in an *ngIf inside of the *ngFor to display your new row:
<div *ngIf="i%3 === 0">
Borrowing from both answers, but leaning more on Alex's, here's how I accomplished it in ng2 (v2.0.1).
<template ... is deprecated. Where you see <template ... you should use <ng-template ... after v5.
<template ngFor let-peer [ngForOf]="peers" let-p="index" let-last="last">
<div class="row" *ngIf="p % 2 === 0">
<div class="col-xs-8 col-sm-6">
<label><input type="checkbox" ... {{peers[p].name}}</label>
</div>
<div class="col-xs-8 col-sm-6" *ngIf="!last">
<label><input type="checkbox" ... {{peers[p+1].name}}</label>
</div>
</div>
</template>
Note that while I get each item from the collection, let-peer [ngForOf]="peers", I don't specifically use it. Instead I use the collection and the index, let-p="index", adding to the index as needed, e.g., peers[p] and peers[p+n].
Adjust your modulus as needed. The last row should check to be sure that the first column to the last column is not the last item in the iterable.