How can I select dynamic elements rendered by *ngIf - javascript

As the code provided bellow. I tried to select a dynamic element generated by ngIf but failed.
I used two ways in total.
ElementRef and querySelector
component template:
`<div class="test" *ngIf="expr">
<a id="button">Value 1</a>
</div>
<div class="test" *ngIf="!expr">
<a id="button">Value 2</a>
</div>`
component class:
expr: boolean;
constructor(
private elementRef: ElementRef,
) {
}
ngOnInit(): void{
//Call Ajax and set the value of this.expr based on the callback;
//if expr == true, then show text Value 1;
//if expr == false, then show text Value 2;
}
ngAfterViewInit(): void{
console.log(this.elementRef.nativeElement.querySelector('#button'));
}
The output result is null.
#ViewChild
component template:
`<div class="test" *ngIf="expr">
<a #button>Value 1</a>
</div>
<div class="test" *ngIf="!expr">
<a #button>Value 2</a>
</div>`
component class:
#ViewChild('button') button: elementRef;
expr: boolean;
ngOnInit(): void{
//Call Ajax and set the value of this.expr based on the callback;
//if expr == true, then show text Value 1;
//if expr == false, then show text Value 2;
}
ngAfterViewInit(): void{
console.log(this.button);
}
The out put result is undefined;
Is there a way to get dynamic dom generated by *ngIf?
Finally the problem has been solved through #ViewChildren.
And to log the updated result, it is necessary to use a separate function.
For example:
Wrong Code:
#ViewChildren('button') buttons: ElementRef;
function(): void{
this.expr = true; // Change expression then the DOM will be changed;
console.log(this.buttons.toArray()); // This is wrong because you will still get the old result;
}
Right Code:
#ViewChildren('button') buttons: ElementRef;
function(): void{
this.expr = true; // Change expression then the DOM will be changed;
}
ngAfterViewInit(): void{
this.buttons.changes.subscribe( e => console.log(this.buttons.toArray()) ); // This is right and you will get the latest result;
}

You can't get the element when the *ngIf="expr" expression is false because then the element doesn't exist.
The value is not yet set in ngOnInit(), only when ngAfterViewInit() is called.
Plunker example
#Component({
selector: 'my-app',
template: `
<div class="test" *ngIf="prop">
<a #button id="button1">button1</a>
</div>
<div class="test" *ngIf="!boolean">
<a id="button2">button2</a>
</div>`
,
})
export class App {
#ViewChild('button') button: ElementRef;
prop:boolean = true;
ngAfterViewInit() {
console.log(this.button);
}
}
Plunker example with ViewChildren
#Component({
selector: 'my-app',
template: `
<button (click)="prop = !prop">toggle</button>
<div class="test" *ngIf="prop">
<a #button id="button1">button1</a>
</div>
<div class="test" *ngIf="!boolean">
<a #button id="button2">button2</a>
</div>`
,
})
export class App {
#ViewChildren('button') button: QueryList<ElementRef>;
prop:boolean = true;
ngAfterViewInit() {
console.log(this.button.toArray());
this.button.changes.subscribe(val => {
console.log(this.button.toArray());
});
}
}

Is not exactly the best but I deal with a similar situation using [ngClass] instead *ngIf.
Just create a class with "display: none" and hide the elements when needed. The elements selected with #ViewChild can be accessed without problem.
For now you can use this little "hack" while search for a better solution or more elegant one.
ex.
.hide-element{
display: none;
}
<div class="test" [ngClass]="expr === 'yourTest' ? 'hide-element' : null">
<a #b id="button1" (click)="onButton1(b)">button1</a>
</div>
if you are handling some async return you can just use async pipe
<div class="test" [ngClass]="(expr | async) === 'yourTest' ? 'hide-element' : null">
<a #b id="button1" (click)="onButton1(b)">button1</a>
</div>
Hope it helps.

If you need the element only in the moment that someone interacts with it (e.g. clicks on it) or with a sibling or child element of it you can pass it's reference with the event.
Template:
<div class="test" *ngIf="expr">
<a #b id="button1" (click)="onButton1(b)">button1</a>
</div>
Code:
onButton1(button: HTMLButtonElement) {
}
If you need the element without interaction with it you might also look at the ViewChildren query instead of ViewChild.
You can set it up with
#ViewChildren('button') buttons: QueryList<ElementRef>;
You can then subscribe to changes of elements that match a selector through this.buttons.changes.subscribe(...). If the element get's created or deleted you will get notified through the subscription. However my first way involves far less boilerplate code.
Alternativly you can also only access the QueryList synchronously in moments where you are sure that the element exists (through some preconditions). In your example you should be able to retrieve the button with
let button: ElementRef = this.buttons.first;
in these cases.

<div class="test" *ngIf="boolean">
<a #button id="button1">button1</a>
</div>
#ViewChild('button') button: elementRef;
console.log(this.button);
the ID = 'button1', not 'button'?
var target = document.getElementById('Button1');

Related

how to set dynamic attribute in angular 7

In Html:
<input class="form-control"[(ngModel)]="value" #muInput/>
<button (click)="onSetAttribute()"> set</button>`
In this:
`#ViewChild('muInput') muInput: ElementRef; `
public seperator : string onSetAttribute() { if(this.seperator ) {
this.muInput.nativeElement.setAttribute('mask' , this.seperator); this.muInput.nativeElement.setAttribute('thousandSeparator' , ',');`
}}
I want to click on the button set a mask attribute to input this my code but is not working
You can do something like this :
<input class="form-control"[(ngModel)]="value" #muInput [attr.mask]="mask" />
<button (click)="changeMask()"> set</button>
In your component ts file, you should declare a class attribute mask, give it default value of w/e you want then:
changeMask() { this.mask = 'separator.2' // w/e value }
same thing for thousandSeparator attribute.
import { Renderer2 } from '#angular/core';
Use Renderer2 setattribute function
constructor ( private _renderer: Renderer2) { }
onSetAttribute() {
this._renderer.setAttribute(this.muInput.nativeElement,'mask' , 'separator.2');
this._renderer.setAttribute(this.muInput.nativeElement,'thousandSeparator' , ',');
}

accessing *ngFor last in its component in angular 6

In angular 6 I want to access *ngFor last value as I want to operation if last value is set
eg
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let last=last;">
<span [last]="last"></span>
<img src="{{list.profile_img}}" alt="" />
<div *ngIf="list.sender_type==0">
<p>{{list.message}}{{last}}</p>
</div>
<div *ngIf="list.sender_type==1">
<p style="background-color: burlywood;">{{list.message}}</p>
</div>
</li>
I want to do is [(myvar)]=last in place of let last=last
I want to bind the last variable so, I can access it is set or not in its component.
you can create a custom directive:
import { Directive, Output, EventEmitter, Input } from '#angular/core';
#Directive({
selector: '[onCreate]'
})
export class OnCreate {
#Output() onCreate: EventEmitter<any> = new EventEmitter<any>();
constructor() {}
ngOnInit() {
this.onCreate.emit('dummy');
}
}
and then you can use it in your *ngFor to call the method in your component:
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let last=last;">
<span (onCreate)="onCreate(last)"></span>
<img src="{{list.profile_img}}" alt="" />
<div *ngIf="list.sender_type==0">
<p>{{list.message}}{{last}}</p>
</div>
<div *ngIf="list.sender_type==1">
<p style="background-color: burlywood;">{{list.message}}</p>
</div>
</li>
then in your component:
myvar: boolean = false;
onCreate(last) {
this.myvar = last;
}
checkout this DEMO.
Angular provides certain local variables when using *ngFor, one is for example last, which will (not as you expect currently) be a boolean value, being true if it is the last item. This is meant for adding specific stylings for the example to the last element of a list.
If you want that boolean you already use it correctly, but obviously the element using it should be a component. So instead of
<span [last]="last"></span>
it should be something like
<my-component [last]="last"></my-component>
where in my-component you define
#Input last: boolean;
and thus have access to it.
for example
<li [ngClass]="list.mydata==1?'replies a':'sent a'" *ngFor="let list of chatlist; let index=i;">
</li>
now you excess last elemnt like
<anyTag *ngIf="i === chatlist.length-1"></anyTag>

Can't change the colour of my text with subscription

I am wanting to change the colour of my text with a service and subscription, however I cannot seem to get it to work properly?
I am using [ngClass] to dynamically set characters that are true for 'isActive', but I don't know why I cannot get this to work?
At present, only the first letter is being changed.
Also... I am hoping to change the colour of the highlighted letters changed by the [ngClass].
Stackblitz
html
<div class="container">
<div class="keys" *ngFor="let item of dataKeys">
<div #text class="chars" *ngFor="let char of data[item]" [ngClass]="{'active': char.isActive}">
{{char.name}}
</div>
</div>
</div>
component
#ViewChild('text') private text: ElementRef;
constructor(private service: SettingsService) {
}
ngOnInit() {
this.subscribeToColour();
}
subscribeToColour() {
this.service.getColour.subscribe(res => {
if (this.text) {
this.text.nativeElement.style.color = res;
}
});
}
get dataKeys() {
return Object.keys(this.data);
}
service
export class SettingsService {
default = 'green'
colour = new BehaviorSubject<any>(this.default);
setColour(colour) {
this.colour.next(colour);
}
get getColour(): Observable<any> {
return this.colour;
}
}
As Peter said the view child reference you captured to "text" is only a reference to the first of the divs that you repeated.
As an alternative, you could consider adding the color to the data object:
Eg:
{
"name": "a",
"isActive": true,
"color": "red",
}
And binding to this in your html file - you shouldn't really be manipulating the nativeElement property yourself
You would then update the property in your data object in your service subscriber as appropriate
The #text is only pointing at the div for a. Therefore, this.text.nativeElement.style.color = res; only updates the color for a.
I modified your code a little bit:
my-comp.component.html
<div class="container">
<div class="keys" *ngFor="let item of dataKeys">
<div class="chars" *ngFor="let char of data[item]" [style.color]="char.isActive ? color : 'white'">
{{char.name}}
</div>
</div>
</div>
my-comp.component.ts
color: string = 'white';
subscribeToColour() {
this.service.getColour.subscribe(res => {
this.color = res;
});
}
Note that I got rid of #text in your html file as well as the conditional block in your ts file.
You don't seem to be setting the .isActive variable of any character in your code. That #text is not working how you want it to.
I think you may be overcomplicating this. You can simply change colors by setting them in your service without subscribing to a value.
Try something like:
<div class="container">
<div class="keys" *ngFor="let item of dataKeys">
<div class="chars" *ngFor="let char of data[item]" [ngClass]="{'active': getColour(char.isActive)}">
{{char.name}}
</div>
</div>
</div>
export class SettingsService {
getColour(boolean): string {
if (boolean == true) {
return 'green'; // whatever colour you want.
} else {
return 'red' // whatever you want
}
}

Not able to use *ngIf in pop up

I'm making a pop up in angular with primeng. The content of the pop up depends on two flags controlled by radio buttons. If one is true, a particular HTML has to be rendered and if other is true, other part has to be rendered. But if one is true, other part in not getting cleared form the pop up. My code is something like this:
In .ts file:
part1: boolean = false;
part2: boolean = false;
makeP1True() {
this.part1=true;
this.part2=false;
}
makeP2True() {
this.part2=true;
this.part1=false;
}
in HTML file:
<p-radioButton name="groupname" value="Part1" (onClick)="makeP1True()"></p-radioButton>
<p-radioButton name="groupname" value="Part2" (onClick)="makeP2True()"></p-radioButton>
<div *ngIf="part1">
Show Part 1
</div>
<div *ngIf="part2">
Show Part 2
</div>
You don't have part2 property as in you example
part1: boolean = false;
part1: boolean = false; // just name it to part2
template
<p-radioButton name="groupname" [value]="Part1" label="Part1" (onClick)="makeP1True()"></p-radioButton>
<p-radioButton name="groupname" [value]="Part2" label="Part2" (onClick)="makeP2True()"></p-radioButton>
<div *ngIf="part1">
Show Part 1
</div>
<div *ngIf="part2">
Show Part 2
</div>
component
export class AppComponent {
part1: boolean = false;
part2: boolean = false;
makeP1True() {
this.part1 = true;
this.part2 = false;
}
makeP2True() {
this.part2 = true;
this.part1 = false;
}
}
stackblitz example
As a general note you can solve it with single propert and relay on
p-radioButton to toggle the value
component
export class AppComponent {
part1: boolean ;
}
template
<p-radioButton name="groupname" label="part1" [value]="true" [(ngModel)]="part1"></p-radioButton>
<p-radioButton name="groupname" label="part2" [value]="false" [(ngModel)]="part1"></p-radioButton>
<div *ngIf="part1 === true">
Show Part 1
</div>
<div *ngIf="part1 === false">
Show Part 2
</div>
stackblitz example with single variable
By doing *ngIf="part1" you are actually just checking whether 'part1' is truthy, which means whether it exists or not. However, to see which item is selected, you would need to set a state variable such as selected.
I would suggest you simplify this by using a single function and ngSwitch. Here is a tutorial: https://www.tektutorialshub.com/angular-2-ngswitch-directive/
With the [ngSwitch] directive you can specify an expression to be evaluated. With *ngSwitchCase you can specify the matcher. The syntax would be the closest to your code.
It would look like this:
HTML
<p-radioButton name="groupname"
value="Part1"
(onClick)="setPart('part1')">
</p-radioButton>
<p-radioButton name="groupname"
value="Part2"
(onClick)="setPart('part2')">
</p-radioButton>
<ng-container [ngSwitch]="selected">
<div *ngSwitchCase="'part1'">
Show Part 1
</div>
<div *ngSwitchCase="'part2'">
Show Part 2
</div>
</ng-container>
TS
public selected: string;
public setPart(id: string): void {
this.selected = id;
}
In this case both divs do something like this: *ngIf = "selected === 'partX'.
BTW: <ng-container> lets you use directives such as *ngIf,*ngSwitch and so on without creating a new DOM elements. Great for building conditionals.
You can read up on all of this in the official docs

what is the equevalant for getelementbyid in angular 2 [duplicate]

I have a code:
document.getElementById('loginInput').value = '123';
But while compiling the code I receive following error:
Property value does not exist on type HTMLElement.
I have declared a var: value: string;.
How can I avoid this error?
Thank you.
if you want to set value than you can do the same in some function on click or on some event fire.
also you can get value using ViewChild using local variable like this
<input type='text' id='loginInput' #abc/>
and get value like this
this.abc.nativeElement.value
here is working example
Update
okay got it , you have to use ngAfterViewInit method of angualr2 for the same like this
ngAfterViewInit(){
document.getElementById('loginInput').value = '123344565';
}
ngAfterViewInit will not throw any error because it will render after template loading
(<HTMLInputElement>document.getElementById('loginInput')).value = '123';
Angular cannot take HTML elements directly thereby you need to specify the element type by binding the above generic to it.
UPDATE::
This can also be done using ViewChild with #localvariable as shown here, as mentioned in here
<textarea #someVar id="tasknote"
name="tasknote"
[(ngModel)]="taskNote"
placeholder="{{ notePlaceholder }}"
style="background-color: pink"
(blur)="updateNote() ; noteEditMode = false " (click)="noteEditMode = false"> {{ todo.note }}
</textarea>
import {ElementRef,Renderer2} from '#angular/core';
#ViewChild('someVar') el:ElementRef;
constructor(private rd: Renderer2) {}
ngAfterViewInit() {
console.log(this.rd);
this.el.nativeElement.focus(); //<<<=====same as oldest way
}
A different approach, i.e: You could just do it 'the Angular way' and use ngModel and skip document.getElementById('loginInput').value = '123'; altogether. Instead:
<input type="text" [(ngModel)]="username"/>
<input type="text" [(ngModel)]="password"/>
and in your component you give these values:
username: 'whatever'
password: 'whatever'
this will preset the username and password upon navigating to page.
Complate Angular Way ( Set/Get value by Id ):
// In Html tag
<button (click) ="setValue()">Set Value</button>
<input type="text" #userNameId />
// In component .ts File
export class testUserClass {
#ViewChild('userNameId') userNameId: ElementRef;
ngAfterViewInit(){
console.log(this.userNameId.nativeElement.value );
}
setValue(){
this.userNameId.nativeElement.value = "Sample user Name";
}
}

Categories

Resources