I am trying to access the childView instance but it keeps saying the the childView is undefined.
Here is my code for childViews:
#ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;
#ViewChild(ProjectNavbarComponent,{ read: true, static: false }) private childProjectNavBar:ProjectNavbarComponent;
#ViewChild(QuestionnaireNodeComponent,{ read: true, static: false }) private childQuestionnaireNode:QuestionnaireNodeComponent;
....
onCreateTerminal() {
this.childQuestionnaireDialog.generateQuestionnaireDropDownList();
this.childQuestionnaireDialog.resetFields();
this._hideQuestionnaireDialog = false;
this._modalTitle = 'New Terminal';
this._placeHolderText = 'Terminal Questionnaire Title';
this._terminal = true;
}
...
It says :this.childQuestionnaireDialog is undefined".
It was working with Angular 7.
As per my new knowledge, the #viewChild takes a flag called static. If we put the flag to true, the parent component tries to get a reference to the childView during its own creation. In other words, we could have an instance of the childView in the onInit() method of the parent Component.Basically a one time access because we won't be able to access in any other methods.
The flag set to false, is basically the new way in ivy renderer.
The problem in my case, neither options are working.
I had a similar problem where ViewChild component is undefined in onInit() method.
This fixed the issue:
// Ensure Change Detection runs before accessing the instance
#ContentChild('foo', { static: false }) foo!: ElementRef;
// If you need to access it in ngOnInit hook
#ViewChild(TemplateRef, { static: true }) foo!: TemplateRef;
You must be trying to access the results of a ViewChild query before the view has completed initializing.
So, you can either mark the query static:
#ViewChild('test', {static: true}) test: ElementRef;
... or move the logic to ngAfterViewInit (preferred).
Refer https://angular.io/guide/static-query-migration
In my case i had rendered my child on *ngIf condition. Hence ViewChild was unable to initialize the required element. Have updated the logic to render the component outside *ngIf, there by ViewChild was able to successfully initialize the element.
Hence if possible, when ViewChild is required, alter the logic to render the component outside *ngIf and show appropriate messages. Or Hide the visibility of the elements using CSS, instead of *ngIf.
I spent an hour not finding it on SO, so it may also help someone else:
My mistake was that I used the wrong selector in the angular html.
The component I wanted to reference looks like this:
#Component({
selector: 'app-universal-modal',
templateUrl: './universal-modal.component.html',
styleUrls: ['./universal-modal.component.scss']
})
export class UniversalModalComponent implements OnInit {
Now I used viewchild in its parent:
#ViewChild('universalModalComponent', {static: true}) universalModalComponent: UniversalModalComponent;
And the html selector should be app-universal-modal:
<app-universal-modal #universalModalComponent ></app-universal-modal>
But instead I used new-task selector.
Strange thing there was no compile time error, but a runtime one...
If you are using #viewchildTemplateRef element inside *ngIf condition in .HTML file then #ViewChild selector/reference variable in .ts file will be undefined. So, use [ngClass] instead.
Example: Use [ngClass]={'hide': data.length = 0} instead of *ngIf=data.length>0
According to the docs, the metadata property read is:
`read - read a different token from the queried elements.`
In other words, it's used if you want to read in ViewContainerRef or the Component name instead of the normal ElementRef (which is the default if you leave read out). So putting true as the value is saying to return type true from the element, which as far as I know is impossible.
A much better explanation is here, but the short answer to your problem is take out the read property or to specify ElementRef or the specific type you want.
Slightly different issue on my side, so adding for posterity.
I was initially implementing a #ViewChild and within the scope of the component everything was alright as normal.
However I was implementing a event subscription to triger a method on the child using this.action()withing the event, which lost its reference via the event. (Bad eventing on my side)
Strange one for me, but one solution (not the best fix but before the event manager refactor) was to pass through the reference to this at subscription creation time.
var self = this; // var because we want it to stick around.
subscription.event.subscribe(()=>{
self.callOnThisRef();
})
I'm sure this is by design as it highlighted the problems with my event management. But it was strange to debug.
For Angular 7 we won't get the {static: boolean} parameter instead we are getting {read: any} in my case I had a *ngif directive so the reference of the element was not getting by Viewchild(), what I did is I just checked the element is undefined or not in Component.ts
If you need to access it in ngOnInit hook
Here is an example for removing class by Hot listening
#ViewChild('elref', ) elref: ElementRef;
import { Component, ElementRef, , Renderer2, ViewChild } from'#angular/core';
export class Component implements OnInit {
#ViewChild('elref', ) elref: ElementRef;
constructor(private renderer: Renderer2) {}
ngOnInit() { this.hotlistening(); }
hotlistening() {
this.renderer.listen('window', 'click', (e: Event) => {
if (this.elref !== undefined) { //Here i checked for undefined
this.elref.nativeElement.classList.remove('open');
});
}
}
#ViewChild('test', {static: false}) test: ElementRef;
(I know this question is a bit old. but for those who the guys still find the hint like me)
I had faced this problem also and I found that
{static: true} option might not work properly always when your element is in the parent that has *ngIf.
The workaround to me is that making that element to sub-component
and using it on its own ngOnInit or ngAfterViewInit.
In this way, I don't have to check whether it really exists, subscribe changes, or something complicated extra work.
maybe you could consider it too.
I think this is the simplest solution if it's possible.
I had the error, but what was causing was more hidden. Simply My View Child component had an error on template, where a variable was undefined. I only perceived it because I revised the HTML. It was hard to notice the error, once no error message was triggered.
for child to be available in ngOnInit()
#ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;
for child to be available in ngAfterViewInit()
#ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;
but please make sure your selector of child component <app-questionnaire> </app-questionnaire> in html of parent should not embedded in any condition otherwise it will not follow the above pattern, it will required the condition to be true first then.
I am setting up a new project with latest Angular.I am using Angular Material for this.I am using BreakpointObserver from #angular/cdk/layout.
I am able to add that succesfully to one of my component.But I want to add it globally to my project so that all the components/modules can use web/tablet/mobile breakpoints for different DOM manipulation.
I am able to add that to the app.component.ts , but I am expecting to write a directive or something.Not service because BreakpointObserver is already a service.
What would be the best approach to add BreakPointObserver observables globally in the project.Do not want to add isHandset$ observables everytime in each component's ts file
I think your idea of using some kind of custom directive would be a good solution. For example, in the case you want to add/remove components of the DOM, you could define only one structural directive that handle adding/removing its host, depending on the viewport dimension using the BreakpointObserver service.
To accomplish that you could define something like this:
#Directive({
selector: '[contentForSmallScreen]'
})
export class ContentForSmallScreenDirective implements OnDestroy {
constructor(
private templateReference: TemplateRef<any>,
private viewContainerRef: ViewContainerRef,
private breakpointObserver: BreakpointObserver
) {
this.breakpointObserver
.observe([tablet,mobile])
.pipe(pluck('matches'), distinctUntilChanged())
.subscribe(this.showHideHost);
}
private showHideHost = (matches: boolean) => {
matches
? this.viewContainerRef.createEmbeddedView(this.templateReference)
: this.viewContainerRef.clear();
}
ngOnDestroy(): void {
this.breakpointObserver.ngOnDestroy();
}
}
And then after declare it at your NgModule, it can be used with any component/html element you wanna add/remove from the DOM depending on if the viewport's dimension meet the tablet and mobile viewport specs.
<app-any-component *contentForSmallScreen></app-any-component>
👨💻 StackBlitz example
Looking at a rough implementation of ngIf :
#Directive({ selector: '[myNgIf]'})
export class MyNgIfDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
#Input() set myNgIf(condition: boolean) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
Getter and setter of input are set initially and raise the ngOnchanges event. This is the first event even before ngInit.
But In that setter we are actually using the viewContainer (which is type of <ViewContainerRef>) - but how come it has access to it at the stage ?
Question
I was expecting to see this line :
this.viewContainer.createEmbeddedView(this.templateRef) ,
— at a later-Already-available-view-stage such as ngAfterViewInit - so how come it still works and have access to view container?
I already know about * which desugar it to a template syntax but still I don't understand how view-container is already available at that stage.
You probably know that every element can be used as a view container. So a directive applied to any element can inject the view container associated with the element it's applied on into constructor:
#Directive({
selector: '[adir]'
})
export class ADirective {
constructor(private vc: ViewContainerRef) {
console.log(this.vc.element.nativeElement.nodeName); // `DIV`
}
And I can apply the directive to any element:
<div adir></div>
And this is essentially how ngIf gets access to the view container.
What's even more interesting is that Angular processes #ViewChild queries before the digest starts and so the static #ViewChild elements are available in ngOnInit. It means that you can do like this:
#ViewChild('vc', {read: ViewContainerRef}) vc;
ngOnInit() {
this.vc; // already available
}
I've been trying to access my custom directive I applied on an element by using #ViewChild(CustomDirective) or #ContentChild(CustomDirective), respectively using the set variable for the first time in my ngAfterViewInit or ngAfterContentInit function.
However, neither of them worked.
At first, I thought that it was due to me loading some content from a webserver, but even after having set static variables, it doesn't work.
Here's what I have:
#Directive({
selector: '[det-insert]',
})
export class DetailedDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
And
... (importing + component stuff)
export class DetailedView implements AfterViewInit {
#ViewChild(DetailedDirective) detView : DetailedDirective;
constructor(...)
ngAfterViewInit(){
alert(detView);
}
}
And the template:
<ng-template det-insert></ng-template>
However, the alert returns undefined.
And I have no clue as to why. Any ideas? I already looked through stackoverflow, but neither is my template obstructed by *ngIf, nor do I start using my queried directive before the proper "AfterXInit" function. I already tried switching ViewChild and AfterViewInit for ViewContent and AfterContentInit respectively, to no avail.
I ran into a similar issue myself. I created a separate module for various alert directives/components. My alert directive was defined and declared in that alert module. I was trying to use the directive in my app module, and in order to get this same example to work, you need to specify that the alert module exports: [AppLevelAlertDirective].
You can still use <ng-template yourDirective></ng-template>, despite it not showing up in the DOM later. This is captured well before the DOM is rendered. Please see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html#!#loading-components
Add DetailedDirective to declarations: [] of your module
<ng-template> isn't added to the DOM, and therefore DetailedDirective can't be found. If you use <div det-insert> it will work
I know it should be easy but angular 2.0 has no many examples yet..
In one of my components in some case I need to add class on my body tag. But my application is bootstrapped deeper than body, so I need something like
angular.element('body').addClass('fixed');
but in Angular 2.0..
BTW, I know I can somehow bootstrap my application to include body tag, but I think in some other cases I would need to select some elements anyway, so I need a solution how to do this simple task - "select element and add class to it"
Update
I'm not sure if DOM is actually still supported in RC. The related statements aren't very clear. Something like
DOM is only for internal use. Either access the DOM directly or use a custom renderer.
I haven't see how a custom renderer might be implemented or how to provide an implementation depending on the current platform (webworker, server, DOM thread).
Update
This seems to be the Angular2 way
import { DOM } from 'angular2/src/platform/dom/dom_adapter';
DOM.addClass(DOM.query("body"), 'fixed');
Import from .../src/... at your own risk. .../src/... is considered private implementation and you can't expect any guarantees that the API won't change without notice.
I tried it in Dart and it works fine (not sure if the TS import above is correct though). In Dart DOM is exported by package:angular2/angular2.dart
Original
If you want to access a DOM element that's outside of your Angular application root, just use document.querySelector(), no need to involve Angular.
As of Angular2 Version 2.0.0-beta.17.
Attribute Directives in Angular2 will do this for you nicely.
Please see this plunk written in TypeScript. This does what you want nicely.
The directive file my-highlight.ts has the line:
this.renderer.setElementClass(this.element, "red", true);
This sets the CSS class on the element.
While template.html has the actual element which is decorated with the directive [myHighlight]:
<p [myHighlight]>Mouseover to highlight me!</p>
This, to me, provides the least hack-ish answer to the question without any dependency on jqLite.
As of angular 2.4 you should inject the DOCUMENT and don't interact with any adapter:
import { Component, Inject } from '#angular/core';
import { DOCUMENT } from '#angular/platform-browser';
#Component({})
export class MyClass {
constructor (#Inject(DOCUMENT) private document) { }
doSomething() {
this.document.someMethodOfDocument();
}
}
Further reading: https://github.com/angular/angular/issues/8509
DOM Manipulation in Angular 2 / 4 app
To manipulate the DOM in Angular 2/4 apps, we need to implement the method ngAfterViewInit() of AfterViewInit. The method ngAfterViewInit() is called when the bindings of the children directives have been checked for the first time. In other words, when the view is initially rendered.
The #ViewChild provides access to nativeElement. It is recommended to not access nativeElement inside the ngAfterViewInit() because it is not browser safe. Also, it's not supported by web workers. Web workers will never know when the DOM updates.
The right way is to use renderer. The renderer needs to be injected to the component constructor. We need to provide an id reference to the HTML element on the view something like this:
<p #p1></p>
It shall be accessed by the corresponding coponent .ts file, something like this:
export class SampleComponent implements AfterViewInit {
#ViewChild("p1") p1;
constructor(private renderer: Renderer2) //Renderer set to be depreciated soon
{ }
ngAfterViewInit() {
//recommended DOM manipulation approach
this.renderer.setStyle(this.p1.nativeElement, //setElementStyle for soon to be depreciate Renderer
'color',
'red');
//not recommended DOM manipulation approach
//this.p1.nativeElement.style = "color:blue;";
}
}
I don't recommend direct DOM access from Angular, but you have a DOM hook via the ElementRef of your component. Once you have access to it you can use it directly or via jquery or any other DOM manipulation technique. I have included an example of how to use jquery to run general queries. If you are always going for the body tag you don't really need ElementRef, but it's useful for something that is relative to the root of the component.
import {Component, ElementRef, OnInit} from '#angular/core';
declare var jQuery:any;
#Component({
selector: 'jquery-integration',
templateUrl: './components/jquery-integration/jquery-integration.html'
})
export class JqueryIntegration implements OnInit {
elementRef: ElementRef;
constructor(private elementRef: ElementRef) {
}
ngOnInit() {
jQuery(this.elementRef.nativeElement).find('.moving-box').draggable({containment:'#draggable-parent'});
}
}
More info here: http://www.syntaxsuccess.com/viewarticle/using-jquery-with-angular-2.0
Demo: http://www.syntaxsuccess.com/angular-2-samples/#/demo/jquery