Angular 4+. Assign value to variable - javascript

Im trying to assign a variable in my html in angular 4+. Is this possible?
What im trying to achieve is to assign a comparison to a variable, so i do not have to make always the same in all the cases i have.
Here is an example i want to achieve:
<mat-list-item role="list" *ngFor="let o of examles;" role="listitem">
<span *ngIf="o.type == 'EXAMPLE_TYPE'"> some text</span>
// here more divs
<span *ngIf="o.type == 'EXAMPLE_TYPE'"> other text</span>
// more divs...
<span *ngIf="o.type == 'EXAMPLE_TYPE'"> last text</span>
</mat-list-item>
So, my question is, is there any way to declare something like?
<div #isExampleType="o.type == 'EXAMPLE_TYPE'" >
And then use it in the *ngIf="isExampleType"...

This particular case is conveniently solved with component method:
<span *ngIf="isExampleType(o)"> some text</span>
Or a pipe:
<span *ngIf="o | exampleType"> some text</span>
Both will have nearly zero performance impact
There is no good built-in way to assign a variable like that. #isExampleType is template variable and doesn't serve this purpose.
The closest thing is let in structural directives, like ngIf:
<mat-list-item role="list" *ngFor="let o of examles;" role="listitem">
<ng-container *ngIf="o.type == 'EXAMPLE_TYPE'; let isExampleType">
<span *ngIf="isExampleType"> some text</span>
...
</ng-container>
</mat-list-item>
However, the side effect is that it provides cloaking behaviour. Since isExampleType is expected to be truthy, o.type == 'EXAMPLE_TYPE' || ' '; let isExampleType trick won't work.
The dirty workaround is to use ngFor instead. It will work as expected but provide unreasonable performance overhead:
<mat-list-item role="list" *ngFor="let o of examles;" role="listitem">
<ng-container *ngFor="let isExampleType of [o.type == 'EXAMPLE_TYPE']">
<span *ngIf="isExampleType"> some text</span>
...
</ng-container>
</mat-list-item>
A good alternative is custom ngVar structural directive, like explained here.

You can try something like:
<input #myHiddenValue type="hidden" value="ASDFG">
<div *ngIf="myHiddenValue.value==='ASDFG'">{{ myHiddenValue.value }}</div>

Related

How can I show comments of clicked feed only

I'm stuck in a situation, in a nested loop. The top loop iterating over the Feeds array and every feed in Feeds have comments array. This comments is used for inner loop.
My problem is, I have Comment button and on click of that button, I want to show the list of comments only for that feed post. But instead of showing comments on current feed post, it is showing comments for all other feeds also, that's because of use of only one Boolean variable showComments created inside .ts file.
So how can make that work for only the given context Feed post?
HTML
<ng-container *ngFor="let feed of feeds">
<div class="ed-card ed-card-feed">
<div class="ed-card-feed--footer">
<div class="card-feed-values">
<a class="mr-4"><span>{{feed.likes_count}}</span> Likes</a>
<a><span>{{feed.comments_count}}</span> Comments</a>
</div>
<div class="card-feed-actions">
<a class="mr-5" (click)="likePost(feed.id)"><span></span> Like</a>
<span></span> Comment button
</div>
</div>
// Below div needs to be open for that particular feed only
<div *ngIf="showComments" class="ed-card-feed--comments">
<div class="feed-comments-list">
<ng-container *ngFor="let comment of feed.comments?.edges">
<div class="comment-list-item">
<p class="comment-box mt-1">
{{comment.node?.body}}
</p>
</div>
</ng-container>
</div>
</div>
</div>
</ng-container>
P.S. - Please don't confuse with feed.comments?.edges that's just because of GraphQL. It is just an array.
Thanx
You can maintain an array of showComments[] equal to size of feeds with initially set to false. showComments[false,false...]
<ng-container *ngFor="let feed of feeds; let myIndex = index"">
//code here
<span></span> Comment button
//check if true
<div *ngIf="showComments[myindex] === true" class="ed-card-feed--comments">
</ng-container>
Instead of a boolean, you can define a selectedFeeds array:
selectedFeeds = new Array<Feed>();
In the template, you add the feed that has been clicked to selectedFeeds:
<a href="javascript:void(0)" class="mr-5" (click)="selectedFeeds.push(feed)>Comment button</a>
and you filter the comments according to the selected feeds:
<div *ngIf="selectedFeeds.indexOf(feed) >= 0" class="ed-card-feed--comments">
The best way to achieve this is to separate the code to different components. And since each of the component will have it's own instance of comments - you won't have a problem creating a local variable and showing/displaying the comments in there.
I've created an example of how it can be implemented: stackblitz
Another approach
<ng-container *ngFor="let feed of feeds">
Comment button
<div *ngIf="feed.showComments" class="ed-card-feed--comments">
</ng-container>
Js file
toggleComments(feed) {
feed.showComments = true // (feed.showComments = !feed.showComments => to show or hide (toggle))
}

Display placeholder if array is empty

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.

How to set scope variable inside ng-repeat?

I want to determine whether any ng-repeated element is rendered.
I write this code
<div ng-show="anyRendered">Any has been rendered</div>
<div ng-show="anyFilterActive">ANY FILTER IS ACTIVE</div>
<div class="selected-filter-value" ng-if="filter.name && filterCtrl.isActiveFilter(filter)" data-ng-repeat="(name, filter) in filterOptions">
<span ng-init="anyFilterActive = true;">{{filter.name}}:</span>
</div>
But this code doesn't work. Also I try to write $parent.anyRendered inside ng-repeat.
Try with ng-if
<div ng-show="filterOptions.length > 0">Any has been rendered</div>
<div ng-if="filterOptions.length > 0" ng-repeat="(name, filter) in filterOptions">
<span></span>
</div>
just use $index
<div ng-repeat="(name, filter) in filterOptions">
<span ng-show="$index > 0"></span>
</div>
ngRepeat Docs $index
In the example in the ng-repeat docs u can see the usage of $index
You must not write code logic in your templates: all the code must be in your controllers, filters, directives and services. The ng-init directive is only here so that you can create aliases to make your templates simpler.
Filters and directives contain "presentation code" and controllers and services contain "business related code".
You could create a filter to know if an object is empty or not (filter code from another question), and use it with ng-if, ng-show...
Resulting template:
<div ng-if="filterOptions|empty">
Nothing to show!
</div>
<div ng-repeat="(k, v) in filterOptions">
{{k}}, {{v}}
</div>

using ngIf with ngRepeat

Hi i am trying to use two types of ng-repeat statements based on the codition using ng-if. Somehow it is not working.
Code:
<div ng-if="orderby === '0'">
<div ng-repeat="user in users | filter:search"></div>
</div>
<div ng-if="orderby === '1'">
<div ng-repeat="user in users | filter:search| orderBy:'-timestamp'"></div>
</div>
I have two controller, they are using the same template. In one template i set the value of $scope.orderby = '0' . In this template i want to use ng-repeat without orderBy and vice versa.
This isn't working out. Is there other way of doing this type of functionality?
Create object : someObjectName.orderby instead of primitive variable orderby.
And your binding will be:
<div ng-if="someObjectName.orderby === '0'">
<div ng-repeat="user in users | filter:search"></div>
</div>
<div ng-if="someObjectName.orderby === '1'">
<div ng-repeat="user in users | filter:search| orderBy:'-timestamp'"></div>
</div>
ng-repeat have another scope and your orderby variable out of ng-repeat is not same in orderby inside ng-repeat scope.
Read this article for more information about scopes: https://github.com/angular/angular.js/wiki/Understanding-Scopes

nested/dynamic handlebars in angular

Maybe I'm thinking about this the wrong way but let me explain:
I have 16 products and multiple orders. I know that these will be the only 16 products I ever have. (the following code doesn't work)
<div ng-repeat="order in orders">
<span ng-repeat="product in products">{{order.{{product}}}}</span>
</div>
my orders are structured like this:
{"date":"5834755","product1":564,"product2":456,etc...
I could write it like this:
<div ng-repeat="order in orders">
<span>{{order.product1}}</span>
<span>{{order.product2}}</span>
etc...
</div>
I am probably overthinking and trying to over simplify my template... What do you think?
I have used a similar notation to print the data of an object and I recommend you do the following:
<div ng-repeat="order in orders">
<span ng-repeat="product in products">{{order[product]}}</span>
</div>
This should work.

Categories

Resources