Angular 2 Input ID not getting bound to element - javascript

I have an Angular 2 component that gets elementId as an Input then sets that as an id attribute on a div. For some reason the div's id not getting set.
#Component({
selector: 'my-chart',
template: `
<div>
Hello {{elementId}}
<div [id]="elementId"></div>
</div>
`
})
export class MyChartComponent implements OnInit, OnChanges {
#Input() elementId: string;
ngOnInit() {
this.createChart();
}
createChart() {
console.log("ID: ", this.elementId);
...
}
}
This is what it looks like in the parent component:
<div *ngFor="let chart of charts">
<my-chart [elementId]="chart.id"></my-chart>
</div>
--
When I inspect element on the div, it shows that the id attribute was not set on the HTML element. Also the Hello {{elementId}} only shows the "Hello ". There is no elementId filled in. See photo below.
But the console.log statement correctly prints out the id, indicating that the input binding is correct.
Image: Inspect Element shows id is missing

there is a equivalent for document.getElementById(); in angular and you can use that.
you can use element reference (ElementRef) in angular and querySelector the way you used in jQuery.
constructor(private elementRef: ElementRef) {
}
// this is inside any of the method
// this is to select multiple
this.elementRef.nativeElement.querySelectorAll('.mandate');
// this is to select single
this.elementRef.nativeElement.querySelector('.mandate')
So rather using document, you should use ElementRef

In angular lifecycle AfterViewInit is where the place all the html and js binding happened.
You should write external dom monuplation in your AfterViewInit method.
export class MyChartComponent implements OnInit, OnChanges, AfterViewInit {
#Input() elementId: string;
ngOnInit() {
}
ngAfterViewInit(): void {
console.log("ID: ", this.elementId);
Plotly.newPlot(this.elementId, ...);
}
}

Related

Wanted to Update Title and Image in Angular after component load

Wanted to update Dom elements like Image and Text after loading component in Angular. Note: Both of them does not have ID but have Class Name.
I used following code:
#ViewChild('.appnametitle') el: ElementRef;
constructor(
private renderer: Renderer2,
#Inject(DOCUMENT) private document,
#Inject(ONEPLACE_JS_URI) private oneplaceJsUri,
public cookieConfig: CookieConfig
) { }
ngAfterViewInit(): void {
this.renderer.setAttribute(this.el.nativeElement, 'innerHTML', 'Test Title Change');
}
but it shows following error:
Cannot read properties of undefined (reading 'nativeElement')
Could you help me where I am doing it wrong?
ViewChild has not a classname as selector (a directive yes). So you can create a directive with the selector of the class and use ViewChild asking about the directive
#Directive({
selector: '.appnametitle'
})
export class SelectDirective {
constructor(public el:ElementRef) { }
}
And in Component:
#ViewChild(SelectDirective) selectDirective:SelectDirective
ngAfterViewInit(): void {
this.renderer.setAttribute(this.selectDirective.el.nativeElement,
'innerHTML', 'Test Title Change');
}
But this is used if you has not access to the component (because was on third persons, e.g.), else you should rethinking the problem in a more angular way

Angular: ngOnInit hook does not work in dynamically created component

I'm having the following directive that adds dynamic component to ng-container
#Directive({
selector: '[appAddingDirective]'
})
export class AddingDirective {
constructor(protected vc: ViewContainerRef) { }
public addComponent(factory: ComponentFactory<any>, inputs: any): void {
this.vc.clear();
const ref: ComponentRef<any> = this.vc.createComponent(factory);
Object.assign(ref.instance, inputs); // can't find more elegant way to assign inputs((
ref.instance.ngOnInit(); // IMPORTANT: if I remove this call ngOnInit will not be called
}
}
The directive is used in an obvious way.
#Component({
selector: 'app-wrapper',
template: `<ng-container appAddingDirective></ng-container>`
})
export class WrapperComponent implements AfterViewInit{
#ViewChild(DynamicItemDirective)
private dynamicItem: DynamicItemDirective;
constructor() { }
ngAfterViewInit(): void {
// hope it doesn't matter how we get componentFactory
this.dynamicItem.addComponent(componentFactory, {a: '123'});
}
}
Finally in a component that is loaded dynamically I have
#Component({
selector: 'app-dynamic',
template: '<p>Dynamic load works {{ a }}</p>'
})
export class DynamicComponent implements OnInit {
#Input() a: string;
constructor() {}
ngOnInit(): void {
console.log(this.a);
debugger;
}
}
Here are my questions.
If I remove ref.instance.ngOnInit() call in AddingDirective, I do not get in ngOnInit of DynamicComponent (debugger and console.log do not fire up). Do component lifecycle hooks work in a component that is created and attached dynamically? What is the best way to make these hooks work?
I don't see rendered string Dynamic load works 123 still if I remove {{ a }} in template (template: '<p>Dynamic load works</p>'), Dynamic load works is rendered as it should. What is the reason and how can I fix that?
Is there a better way to assing inputs than doing Object.assign(ref.instance, inputs) as above?
PS. I'm using Angular 11

How to operate multiple doms at the same time with angular directive?

I solved this problem, just < p [appHighlight]="markArray" #mark>111< /p >, and set '#Input('appHighlight') mark: Array' in highlight.directive.ts.
refer to: https://stackblitz.com/edit/angular-7ewavt
Thanks for all answer, and welcome other solutions.
Question Desc:
This is HTML:
<p appHighlight>111</p>
<p appHighlight>222</p>
<p appHighlight>333</p>
This is directive.ts:
#Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) {
}
#HostListener('mouseenter') onMouseEnter() {
console.log(this.el);
// only output current mouse-hover DOM ElementRef
// but I want to get all DOM's ElementRef whose bound appHighlight
// in highlight.directive.ts, NOT xxx.component.ts
// in pursuit of better decouple and reuse.
}
}
I want :
When the mouse is hover one of the DOM elements, all DOMs bound with the appHighlight instruction are triggered.
The question is how to get the DOM ElementRef of all bound elements in directive.ts, NOT xxx.component.ts ? (because in pursuit of better decouple and reuse.)
Thanks.
the ViewChildren decorator gives you exactly that:
export class AppComponent implements AfterViewInit {
#ViewChildren(HighlightDirective, {read: ElementRef) children: QueryList<ElementRef>;
// ...
ngAfterViewInit() {
this.children.forEach(child => console.log(child.nativeElement));
}
}
If you are looking for all the elements inside the directive then it is not the right place to look into. If you need all the elements which are bound to appHighlight directive then you should look that in the parent component.
export class AppComponent {
#HostListener('mouseenter') onMouseEnter() {
this.highlights.forEach(highlight => console.log(highlight));
}
#ViewChildren(HighlightDirective, { read: ElementRef }) highlights: QueryList<ElementRef>
name = 'Angular';
}
Here we are now listening to mouseenter in AppComponent rather than inside
HighlightDirective .
Working Stackblitz

#Input and #Output are always undefined in Angular-Cli

Whatever values are inside the individuals are printed without issues but whatever is obtained using #Input or #Output is not displayed.
child.component.ts
#Component({
selector: 'app-form-input',
templateUrl: './form-input.component.html',
styleUrls: ['./form-input.component.scss']
})
export class FormInputComponent implements OnInit {
#Input() fieldType: string;
//with another Input and 1 Output DOM
constructor(
) {
}
ngOnInit() {
console.log(this.fieldType);
}
}
parent.component.html
<app-form-input (value)="action($event)"
[fieldType]="date"
[maxAllowItem]="1">
</app-form-input>
Is there anything goes wrong in syntax?
The Log always show 'undefined' in all cases.
Thanks
I think this is trying to pull in a variable defined within your component.
Try the following syntax, wrap the string again, this should ensure you are passing a string and not a variable from the component, the input will then know to expect a string.
[fieldType]="'date'"
This is wrapping the string in " and '.
You may need to initialize the initial values of your #Input and #Output variables inside your component because #Input properties will be undefined unless they are provided from outside and #Output properties need to be initialized with EventEmitter
Also you need to check the values inside ngOnChanges which will be executed during Change Detection
Your code will be like this:
#Component({
selector: 'app-form-input',
templateUrl: './form-input.component.html',
styleUrls: ['./form-input.component.scss']
})
export class FormInputComponent implements OnInit {
#Input() fieldType: string;
#Output() event: EventEmitter<any>
//with another Input and 1 Output DOM
constructor() {
this.fieldType = ''
this.event = new EventEmitter()
}
ngOnInit() {
}
ngOnChanges() { // <- it will run every time and give you the latest value of fieldType
console.log(this.fieldType);
}
}

Angular 4 #HostBinding('attr.id') does not work (undefined)

I have difficulties binding to host element 'id' attribute in my select-picker directive. I am using #HostBinding('attr.id'), but it returns undefined. I have checked the actual DOC and it looks like this is the way this simple task should be done.
Here is my component:
import {Component, OnInit, Input, Output, EventEmitter, AfterViewInit, HostBinding} from '#angular/core';
declare const $;
#Component({
selector: '[select-picker]',
templateUrl: 'select-picker.component.html'
})
export class SelectPickerComponent implements OnInit, AfterViewInit {
#Input() options: Array<Object>;
#Input() #HostBinding('class.cancelable') cancelable: boolean;
#Input() #HostBinding('class.expand-up') expandUp: boolean;
#Input() #HostBinding('style.width') elemWidth: string;
#HostBinding('attr.id') id: string;
#Output() value: EventEmitter<boolean> = new EventEmitter<boolean>();
select: any;
constructor() {
}
ngOnInit() {
console.log(this.id) // <-- this logs 'undefined';
}
ngAfterViewInit() {
const self = this;
this.select = $(`#${this.id} select`).selectize({ // this init works, but with `id="undefined"`
readOnly: true,
onChange: function (val) {
self.value.emit(val);
},
dropdownDirection: 'up'
});
}
discardValue() {
this.select[0].selectize.setValue(0);
}
}
And this is the view (from parent component where directive is used):
<div select-picker id="page-options" [options]="pageOptions" [elemWidth]="'200px'" (value)="setItemsPerPage($event)"></div>
Attribute binding for static values can also be done with simple:
#Input() id: string;
Both versions - <div id="some-static-id" ...> and <div [id]="someDynamicId" ...> - will set the value on your component when using #Input().
EDIT: However, it is strongly discouraged to use jQuery and lookup by IDs in Angular. I would question if your approach is the best option to accomplish what you want. You should probably create a separate question where you explain what you try to accomplish and what is the best way to do this with Angular.
If you want to grab a Id value just use #ViewChild not #HostBinding
<div #myDiv></div>
#ViewChild('myDiv') myDiv: ElementRef;
console.log(this.myDiv.nativeElement.id)
and then use methods of ElementRef do get id attribute
You could directly retrieve attribute value of element using getAttribute method on directive DOM. This will only work when you have static element id. If you want to pass id dynamically, use Input bindings.
constructor(private elementRef: ElementRef){}
ngOnInit() {
this.elementRef.nativeElement.getAttribute('id')
}
Plunker Demo

Categories

Resources