How to dynamically highlight text in ngFor - javascript

I have a series of rows inside a ngFor.
<div *ngFor="let block of data;">
<div class="class-row">
<div class="left">A Label:</div>
<div class="right">{{block.key1}}</div>
</div>
<div class="class-row">
<div class="left">Another Label:</div>
<div class="right">{{block.key2}}</div>
</div>
</div>
It turns out that for some rows I would like to highlight {{block.key1}} or {{block.key2}} based on some logic.
Is there a way of doing this in angular 4+?
I thought of calling a component function and passing it the {{block.key1}} or {{block.key2}}, something like
<div *ngFor="let block of data;">
<div class="class-row">
<div class="left">A Label:</div>
<div [innerHTML]=highlight({{block.key1}}) class="right"></div>
</div>
<div class="class-row">
<div class="left">Another Label:</div>
<div [innerHTML]=highlight({{block.key2}}" class="right"></div>
</div>
</div>
but this is probably not possible.
Is there some way of changing the property of the text in the div dynamically?

Here is how you can do it. I assume when you talk about hightlight you mean CSS. If so you better use ngClass. Here is an example of how you can do it.
<div *ngFor="let block of data;">
<div class="class-row">
<div class="left">A Label:</div>
<div [ngClass]="[shouldHighlight(block.key2) ? 'highlight' : '']" class="right">{{block.key1}}</div>
</div>
<div class="class-row">
<div class="left">Another Label:</div>
<div [ngClass]="[shouldHighlight(block.key2) ? 'highlight' : '']" class="right">{{block.key2}}</div>
</div>
</div>
<!--You can also do it by this way -->
<div *ngFor="let block of data;">
<div class="class-row">
<div class="left">A Label:</div>
<div [class.highlight]="shouldHighlight(block.key1)" class="right">{{block.key1}}</div>
</div>
<div class="class-row">
<div class="left">Another Label:</div>
<div [class.highlight]="shouldHighlight(block.key2)" class="right">{{block.key2}}</div>
</div>
</div>
<style>
.highlight{
/* Your CSS Here*/
}
</style>

The answer by #Ulrich is correct, but I would like to expand on an alternative, that is easily missed.
That is: you are assuming you must call a function inside the ngFor. Well, this is true if you choose not to preprocess the data.
What I would try to do is preprocess data when loading it:
this.data.forEach(block => {
block.highlightKey1 = this.shouldHighlight(block.key1);
block.highlightKey2 = this.shouldHighlight(block.key2);
}
If you can extend the model to include also this extra fields, everything is smoother:
<div *ngFor="let block of data;">
<div class="class-row">
<div class="left">A Label:</div>
<div [ngClass]="[block.highlightKey1 ? 'highlight' : '']" class="right">{{block.key1}}</div>
</div>
<div class="class-row">
<div class="left">Another Label:</div>
<div [ngClass]="[block.highlightKey2 ? 'highlight' : '']" class="right">{{block.key2}}</div>
</div>
</div>
At runtime this solution is going to perform slightly better, because no function call is going to be made during change detection, but only at load.
I find it cleaner too, because no "complex logic" is going to be invoked directly by the template.

Related

Best way to loop the angular flex layout with two rows?

I would like to know how to do the ngFor with two rows in angular flex layout.
<div *ngFor="let item of items">
<div class="container" fxLayout fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap.xs="0">
<div class="item item-1" fxFlex="50%">
<div fxLayoutAlign="center" class="item-label">
{{item.name}}
</div>
<div fxLayoutAlign="center" class="item-value">{{item.value}}</div>
</div>
<div class="item item-2" fxFlex="50%">
<div fxLayoutAlign="center" class="item-label">
{{item.name}}
</div>
<div fxLayoutAlign="center" class="item-value">{{item.value}}</div>
</div>
</div>
<div class="container" fxLayout fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap.xs="0">
<div class="item item-3" fxFlex="100%">
<div fxLayoutAlign="center" class="item-label">
{{item.name}}
</div>
<div fxLayoutAlign="center" class="item-value">{{item.value}}</div>
</div>
</div>
</div>
Here its looping but each time its printing 3 values for every index. I would like to print index 0 value of first column, index 1 on second column and index 2 value on second row.
I've created for you an example on stackblitz, based on your requirements.
The correct way would be :
<div class="container" fxLayout="row wrap" fxLayoutAlign="center" fxLayoutGap.xs="0">
<!-- you can use ngClass to manage 'item-1' or 'item-2' style -->
<div class="item" fxFlex="50%" *ngFor="let item of items">
<div fxLayoutAlign="center" class="item-label">
{{item.name}}
</div>
<div fxLayoutAlign="center" class="item-value">{{item.value}}</div>
</div>
</div>
The wrap/nowrap property on the fxLayout directive does everything, because childs are set with fxFlex="50%".
I put out fxLayout.xs to render it correctly on stackblitz.
Here is #angular/flex-layout documentation
Hope it helps ;-)
The "wrap" parameter in the "fxLayout" is used to wrap the child content with the help of "fxFlex"

javascript remove parent div where particular class appears in hierarchy below

First time question on this site. Sorry if I have failed the formatting test.
I am almost completely ignorant about javascript but I have been told I need it to solve this problem. I have a page where there are multiple divs with the same class. Each has a multi-level hierarchy beneath it. I want to stop the parent displaying if any of its children contain a div of a particular class. e.g. In the following code I want to stop all divs with class of "classa" displaying if one of their direct or indirect children contains class of "classb draft". So here, none of divb would display.
<div id="diva" class="classa">
<div id="divaa">
</div>
<div id="divab">
<div id="divaba">
<div id="divabaa"
<div id="divabaaa" class="classb">
</div>
</div>
</div>
</div>
</div>
<div id="divb" class="classa">
<div id="divba">
</div>
<div id="divbb">
<div id="divbba">
<div id="divbbaa"
<div id="divbbaaa" class="classb draft">
</div>
</div>
</div>
</div>
</div>
You are not closing div in
<div id="divabaa"
You can use querySelectorAll() to select all the children with that class (draft). Then use forEach() to loop through all the matching elements to find the closest() div with .classa to set display property to none.
var elements = document.querySelectorAll('.classa .draft');
elements.forEach(function(el){
el.closest('.classa').style.display = 'none';
});
<div id="diva" class="classa">
<div id="divaa">
</div>
<div id="divab">
<div id="divaba">
<div id="divabaa">
<div id="divabaaa" class="classb">
Without Draft
</div>
</div>
</div>
</div>
</div>
<div id="divs" class="classa">
<div id="divba">
</div>
<div id="divbb">
<div id="divbba">
<div id="divbbaa">
<div id="divbbaaa" class="classb draft">
Draft
</div>
</div>
</div>
</div>
</div>

Cannot set .color property in JS

<button id="change_button" class="btn btn-primary" onclick="ColorMe()">CLICK ME</button>
<div class="container">
<div class="row">
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
</div>
Clicking a button is supposed to color all the elements of class "grid_element" into red but in never happens.
function ColorMe() {
document.getElementsByClassName("grid_element").style.color = ("red");
}
The problem is said to be Cannot set property 'color' of undefined
at ColorMe (js.js:2) but I know it worked in the same way many times before.
The problem is that you are attempting to use the .style property on the collection of elements found by .getElementsByClassName() instead of on each of the elements within the collection.
Also (FYI), .getElementsByClassName() returns a "live" node list, which causes the entire DOM to be re-scanned every time you access the node list variable and that can impact performance quite a bit. There are limited use cases for that, so you probably want a "static" node list more often than not. For that, use .querySelectorAll().
function ColorMe() {
// Get all the matching elements into a JavaScript Array
var elements = Array.prototype.slice.call(document.querySelectorAll(".grid_element"));
// Loop over each element....
elements.forEach(function(el){
el.style.color = "red"; // Adjust the element's style
});
}
<button id="change_button" class="btn btn-primary" onclick="ColorMe()">CLICK ME</button>
<div class="container">
<div class="row">
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
<div class="col-md-4 col-sm-4">
<div class="grid_element">
<div class="title">
COLOR IS:
</div>
</div>
</div>
</div>

Use Kendo UI Flip Effects / Combined Effects for multiple items on the same page

I need to use kendo ui to display between 6-60 items. Each using the flip effect here http://demos.telerik.com/kendo-ui/fx/combined
The products will be loaded from the database with the unique id like this:
<div class="row">
<div class="col-md-4 product-container">
<div id="productID1" class="productID">
<div class="product">
<div id="product-back1" class="product-desc">
<p>BACK</p>
</div>
<div id="product-front1" class="product-image">
<p>FRONT</p>
</div>
</div>
</div>
</div>
<div class="col-md-4 product-container">
<div id="productID2" class="productID">
<div class="product">
<div id="product-back2" class="product-desc">
<p>BACK</p>
</div>
<div id="product-front2" class="product-image">
<p>FRONT</p>
</div>
</div>
</div>
</div>
<div class="col-md-4 product-container">
<div id="productID3" class="productID">
<div class="product">
<div id="product-back3" class="product-desc">
<p>BACK</p>
</div>
<div id="product-front3" class="product-image">
<p>FRONT</p>
</div>
</div>
</div>
</div>
The problem is I need multiple panels on the page how can I make each "front" and "back" click unique.
var el = kendo.fx($('div[id^=productID]')),
flip = el.flip("horizontal", $('div[id^=product-front]'), $('div[id^=product-back]')),
zoom = el.zoomIn().startValue(1).endValue(1);
flip.add(zoom).duration(200);
$('div[id^=product-front]').click(function () {
flip.stop().play();
});
$('div[id^=product-back]').click(function () {
flip.stop().reverse();
});
I've tried loading each item into an array but have not found a good way to assure the correct item will be flipped.
Since every div[id^=product-front] is a child of div[id^=productID], you can find the children of that and use it.
replace flip.stop().play(); with
kendo.fx($(this)).flip("horizontal", $(this).children()[0], $(this).children()[1]).stop().play();

jQuery count number of divs with a particular class inside a div with a particular class

How can I count the number of div's with class item that are inside the div with class outer-1? The result here should be 7.
I tried this and several other failed variations of it.
alert( $(".outer-1").$(".item").length );
This gives me 12 which is all the divs on the page.
alert( $(".item").length );
How can i specify only the outer-1 div?
Divs
<div class="outer-1">
<div class="item">a</div>
<div class="item">b</div>
<div class="item">c</div>
<div class="item">d</div>
<div class="item">e</div>
<div class="item">f</div>
<div class="item">g</div>
</div>
<div class="outer-2">
<div class="item">a</div>
<div class="item">b</div>
<div class="item">c</div>
<div class="item">d</div>
<div class="item">e</div>
</div>
Use find() selector:
$(".outer-1").find(".item").length ;
or
$(".outer-1 .item").length
For your html, $(".outer-1 .item").length would be sufficient, but if div.items are nested, like this
<div class="outer-1">
<div class="item">a</div>
<div class="item">b</div>
<div class="item">c</div>
<div class="item">d</div>
<div class="outer-x">
<div class="item">h</div>
<div class="item">i</div>
</div>
<div class="item">e</div>
<div class="item">f</div>
<div class="item">g</div>
</div>
You may want to use $(".outer-1 > .item").length (all div.item that are child of div.outer-1).
See jsFiddle
if you have a bunch of outers with the same name, you may need to loop:
$('.outer-1').each(function(){
console.log( $('.item',this).length );
});

Categories