Wanted to Update Title and Image in Angular after component load - javascript

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

Related

Angular 2+ : Get Reference of appcomponent div in another components

I have components called app.component which is the main component in the angular project.
Navigation to customer component is done by routing.
And
Folder structer
src\app
- app.component.html
- app.component.ts
and
src\app\components\customer
- customer.component.html
- customer.component.ts
In my app.component.html
<div class="top-container" #topContainerRef>
<router-outlet></router-outlet>
</div>
In my customer.component.ts
I want to get reference of the top most container div which is contained in app.components
I want to replace
document.getElementsByClassName('top-container')[0].scrollTop = some values
with something similar to
#ViewChild('topContainerRef', { read: ElementRef, static: false }) topContainerRef: ElementRef;
this.topContainerRef.nativeElement.scrollTop= "some value" //here the topContainerRef is undefined
Is there any way i can use elementRef instead of classname or Id's.
You cannot use ViewChild for the #topContainerRef to get a reference of this element, because it is not rendered by your CustomerComponent.
You either need to get the reference of this element inside the app component itself and find a way to pass it to all the other children that might need it (not recommended).
Or you can just build a service and use that to "request" the scrollTop change by whichever component has access to this element (in your case the app component).
I would do it something like this:
export class AppContainerService {
private scrollTopSource = new ReplaySubject<number>(1);
public scrollTop$ = this.scrollTopSource.asObservable();
public updateScrollTop(value: number) {
this.scrollTopSource.next(value);
}
}
Inside your CustomerComponent:
public class CustomerComponent implements OnInit {
// ...
constructor(private containerService: AppContainerService) {
}
ngOnInit() {
this.containerService.updateScrollTop(/* whatever value you need */);
}
// ...
}
And finally, the AppComponent that will react to the scrollTop changes:
export class AppComponent implements AfterViewInit {
#ViewChild('topContainerRef', { read: ElementRef, static: false }) topContainerRef: ElementRef;
private subscriptions = new Subscription();
constructor(private containerService: AppContainerService) {
}
ngAfterViewInit() {
this.subscriptions.add(this.containerService.scrollTop$.subscribe((value: number) => {
this.topContainerRef.nativeElement.scrollTop = value;
}));
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
Don't forget about unsubscribing inside ngOnDestroy. This is important so that you don't have memory leaks

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

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

Angular 2 Input ID not getting bound to element

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, ...);
}
}

TypeScript | JavaScript | Angular 2: Dynamically Set #HostListener Argument

Dynamically Setting the #HostListener's Arguments
I have a directive which needs to listen for any event provided declaratively by the engineer. Here's an example:
import { Directive, Input, ElementRef, HostListener, OnInit } from '#angular/core';
//--
import { Sandbox } from '../../../sandbox';
#Directive({ selector: '[addItem]' })
class AddNewItemDirective implements OnInit {
#Input('addItem') data;
#Input() on: string = 'click';
constructor(private $: Sandbox, private element: ElementRef) { }
ngOnInit() { console.log('#INIT', this); }
#HostListener('click', ['$event']) handleEvent(e) {
console.log('add-item', e);
}
}
export { AddNewItemDirective };
Here's its usage:
<button class="btn btn-primary" [addItem]="{ name: 'Jeffrey' }" on="focus">Add New Item</button>
This works fine. However, my intuition told me I should be able to dynamically set the HostListener's arguments at render time based upon an input parameter:
#Directive({ selector: '[addItem]' })
class AddNewItemDirective implements OnInit {
#Input('addItem') data;
#Input() on: string = 'click';
constructor(private $: Sandbox, private element: ElementRef) { }
ngOnInit() { console.log('#INIT', this); }
#HostListener(this.on, ['$event']) handleEvent(e) {
console.log('add-item', e);
}
}
Of course, this.on would not be overwritten with 'focus' until the time ngOnInit is invoked. To my surprise, this.on throws an error because undefined has no property 'on'. So when my directive class is instantiated, for whatever reason, this === undefined.
I found one similar question here, though, its looking to dynamically modify HostListener at runtime while I just need it modified at compile/render/instantiation time.
Can someone please shed light on how I can accomplish this?
Thx
HostListener is not dynamic, it can not be changed at runtime. You should use Renderer class, which provides listen method:
#Input()
public on:string;
private dispose:Function;
constructor(private renderer:Renderer, private elementRef:ElementRef){}
ngOnInit(){
this.dispose = this.renderer.listen(this.elementRef.nativeElement, this.on, e => console.log(e));
}
ngOnDestroy(){
this.dispose();
}

Categories

Resources