Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular2: Cannot read property 'nativeElement' of undefined #411

Closed
Splaktar opened this issue Jul 16, 2016 · 10 comments
Closed

Angular2: Cannot read property 'nativeElement' of undefined #411

Splaktar opened this issue Jul 16, 2016 · 10 comments

Comments

@Splaktar
Copy link

Splaktar commented Jul 16, 2016

This was working fine with 1.1.0-beta4 using (grid-ready)="onGridReady($event)" event. Now that I've moved to 1.1.0 final, I can't seem to get the grid in a good way. It seems like the grid-ready event was removed?

So I've been trying to use the @ViewChild('grid') approach with ngAfterViewInit(), but that isn't working. I've tried ngAfterContentInit() as well w/ the same result.

Using:
"vaadin-grid": "1.1.0",
"@vaadin/angular2-polymer": "1.0.0-beta2",
"@angular/common": "2.0.0-rc.4",
"@angular/compiler": "2.0.0-rc.4",
"@angular/core": "2.0.0-rc.4",
"@angular/forms": "0.2.0",
"@angular/http": "2.0.0-rc.4",
"@angular/platform-browser": "2.0.0-rc.4",
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
"@angular/platform-server": "2.0.0-rc.4",
"@angular/router": "3.0.0-beta.2",

Template:

    <vaadin-grid #grid *ngIf="propertyCount > 0" visible-rows="15" class="flex-8"
                 (selected-items-changed)="onPropertySelected($event)">

Component:

...
export class AreasComponent implements OnInit, AfterViewInit {
  @ViewChild('grid') grid: ElementRef;
  vaadinGrid: any;
...
  ngAfterViewInit() {
    this.grid.nativeElement.then(grid => {
      this.vaadinGrid = grid;
    });
  }

Always results in:

EXCEPTION: Error in http://localhost:4200/app/+areas/areas.component.js class AreasComponent_Host - inline template:0:0
browser_adapter.ts:82
EXCEPTION: Error in http://localhost:4200/app/+areas/areas.component.js class AreasComponent_Host - inline template:0:0BrowserDomAdapter.logError @ browser_adapter.ts:82BrowserDomAdapter.logGroup @ browser_adapter.ts:93ExceptionHandler.call @ exception_handler.ts:58(anonymous function) @ application_ref.ts:374schedulerFn @ async.ts:148SafeSubscriber.__tryOrUnsub @ Subscriber.ts:240SafeSubscriber.next @ Subscriber.ts:192Subscriber._next @ Subscriber.ts:133Subscriber.next @ Subscriber.ts:93Subject._finalNext @ Subject.ts:154Subject._next @ Subject.ts:144Subject.next @ Subject.ts:90EventEmitter.emit @ async.ts:133onHandleError @ ng_zone_impl.ts:95ZoneDelegate.handleError @ zone.js:327Zone.runTask @ zone.js:259drainMicroTaskQueue @ zone.js:474ZoneTask.invoke @ zone.js:426
browser_adapter.ts:82
ORIGINAL EXCEPTION: TypeError: Cannot read property 'nativeElement' of undefinedBrowserDomAdapter.logError @ browser_adapter.ts:82ExceptionHandler.call @ exception_handler.ts:70(anonymous function) @ application_ref.ts:374schedulerFn @ async.ts:148SafeSubscriber.__tryOrUnsub @ Subscriber.ts:240SafeSubscriber.next @ Subscriber.ts:192Subscriber._next @ Subscriber.ts:133Subscriber.next @ Subscriber.ts:93Subject._finalNext @ Subject.ts:154Subject._next @ Subject.ts:144Subject.next @ Subject.ts:90EventEmitter.emit @ async.ts:133onHandleError @ ng_zone_impl.ts:95ZoneDelegate.handleError @ zone.js:327Zone.runTask @ zone.js:259drainMicroTaskQueue @ zone.js:474ZoneTask.invoke @ zone.js:426
browser_adapter.ts:82ORIGINAL STACKTRACE:BrowserDomAdapter.logError @ browser_adapter.ts:82ExceptionHandler.call @ exception_handler.ts:74(anonymous function) @ application_ref.ts:374schedulerFn @ async.ts:148SafeSubscriber.__tryOrUnsub @ Subscriber.ts:240SafeSubscriber.next @ Subscriber.ts:192Subscriber._next @ Subscriber.ts:133Subscriber.next @ Subscriber.ts:93Subject._finalNext @ Subject.ts:154Subject._next @ Subject.ts:144Subject.next @ Subject.ts:90EventEmitter.emit @ async.ts:133onHandleError @ ng_zone_impl.ts:95ZoneDelegate.handleError @ zone.js:327Zone.runTask @ zone.js:259drainMicroTaskQueue @ zone.js:474ZoneTask.invoke @ zone.js:426
browser_adapter.ts:82
TypeError: Cannot read property 'nativeElement' of undefined
    at AreasComponent.ngAfterViewInit (http://localhost:4200/app/+areas/areas.component.js:43:18)
    at _View_AreasComponent_Host0.detectChangesInternal (AreasComponent.template.js:40:65)
    at _View_AreasComponent_Host0.AppView.detectChanges (http://localhost:4200/vendor/@angular/core/src/linker/view.js:243:14)
    at _View_AreasComponent_Host0.DebugAppView.detectChanges (http://localhost:4200/vendor/@angular/core/src/linker/view.js:348:44)
    at _View_AppComponent4.AppView.detectContentChildrenChanges (http://localhost:4200/vendor/@angular/core/src/linker/view.js:261:19)
    at _View_AppComponent4.AppView.detectChangesInternal (http://localhost:4200/vendor/@angular/core/src/linker/view.js:253:14)
    at _View_AppComponent4.AppView.detectChanges (http://localhost:4200/vendor/@angular/core/src/linker/view.js:243:14)
    at _View_AppComponent4.DebugAppView.detectChanges (http://localhost:4200/vendor/@angular/core/src/linker/view.js:348:44)
    at _View_AppComponent0.AppView.detectContentChildrenChanges (http://localhost:4200/vendor/@angular/core/src/linker/view.js:261:19)
    at _View_AppComponent0.detectChangesInternal (AppComponent.template.js:207:8)
@Splaktar
Copy link
Author

Splaktar commented Jul 16, 2016

I've traced the problem down to this one line:

<vaadin-grid #grid *ngIf="propertyCount > 0"

Using #grid and *ngIf in the same element does not work in Angular 2-rc.4. Removing the *ngIf results in a valid element every time, where having it there always returns undefined.

Moving the *ngIf up to a parent div also breaks things. I guess that I'll have to resort to display: none via classes (normally something that seems to be avoided in Angular 2).

This worked fine when using the grid-ready events :( But it would be nice to use 'the Angular way here', unfortunately that way is either bugged or just doesn't allow an obvious use case.

@Splaktar
Copy link
Author

@Splaktar
Copy link
Author

Splaktar commented Jul 18, 2016

Investigated this some more via input from the Angular 2 team on angular/angular#6179 and using this Plunkr. It seems to work fine with Angular 2 components, but not with angular2-polymer or vaadin-grid elements.

Perhaps angular2-polymer is not handling resolution of the components in the view before ngAfterViewInit()?

@Splaktar Splaktar reopened this Jul 18, 2016
@Saulis
Copy link
Contributor

Saulis commented Jul 29, 2016

angular2-polymer doesn't affect the resolution of view childs in any way – if childs are defined as a nested childs of a ngIf element, ngAfterViewInit() is run regardless if those childs are rendered in the DOM or not.

This issue affects all elements, as you can see with this snippet:

<input type="checkbox" [checked]="visible" (change)="visible=$event.target.checked">

<div *ngIf="visible">
  <label #hiddenLabel>Visible!</label>
</div>
export class AppComponent {
  @ViewChild('hiddenLabel') label: ElementRef;

  private visible: Boolean;

  constructor() {
    this.visible = false;
  }

  ngAfterViewInit() {
    console.log(this.label.nativeElement); // throws an error
  }
}

Your Plunkr example works in this case because things.length > 0 is true when ngAfterViewInit() is run.

Indeed grid-ready was handy in this case, since it could be used to detect when the element is actually rendered, but unfortunately it's gone now.

I'd suggest either using hidden instead of *ngIf or using such a trigger for *ngIf visibility that you can set the vaadinGrid variable simultaneously in that trigger to make sure #grid exists in the DOM.

@jouni jouni added the 1.x label Mar 23, 2017
@jouni
Copy link
Member

jouni commented Mar 23, 2017

As we are no longer actively supporting the use of our elements with Angular, I’m closing this issue. Please open a new issue in the angular-polymer project if you are still affected by this issue.

@jouni jouni closed this as completed Mar 23, 2017
@DuaneQ
Copy link

DuaneQ commented Apr 20, 2018

I'm getting "Cannot read property 'nativeElement' of undefined" but only after I save to a firebase backend.

<p> <input [(ngModel)]="location" placeholder="Enter city" #search> </p>

`export class HomePage implements OnInit{
loggedInUser: any = null;

@ViewChild('search') public searchElement: ElementRef;
@ViewChild('searchPlaces') public searchPlacesElement: ElementRef;
@ViewChild('myInput') public myInput: ElementRef;`

` ngOnInit(){
this.mapsApiLoader.load().then(
() => {
let autocomplete = new google.maps.places.Autocomplete(this.searchElement.nativeElement, {types:['(cities)']});

      autocomplete.addListener("place_changed", () => {
        this.ngZone.run(() => {
            let place: google.maps.places.PlaceResult = autocomplete.getPlace();

            if(place.geometry === undefined || place.geometry === null){
              return;
          }
            this.location = place.formatted_address;
          });
        });
      }
    )`

` savePersonalInfo() {
this.firebaseSvcProvider.addSettings(this.location,
this.intPlaces,
this.bio,
this.connectionNotifications,
this.messageNotifications,
this.showTrips).then(() => {
this.navCtrl.setRoot(PopularPage)});

        console.log(this.searchElement.nativeElement + ' save');
}`

After I save the data to firebase, when I navigate back to the page with the autofill I get the above mentioned error. I have to close the app then re-open in order for it to resume working.

@senturkhasan
Copy link

#1294

@toughthinktank
Copy link

toughthinktank commented Aug 23, 2018

Using openModal type of method where modal has *ngIf condition will set the native element to undefined as the native element is only created when the condition mentioned with *ngIf becomes true. But there is a way around this if you absolutely have to use *ngIf by using Javascript's event loop manipulation using setTimeout or Promises or any sort of callbacks. Here's an example

HTML Code

<button (click)="openModal()">Open Modal</button>

<div class="modal fade" #sampleModal *ngIf="showModal">
       <!--Modal View goes here-->
</div>

TS Code

//Inside export component class
showModal:boolean = false;

openModal = function () {
        this.showModal = true;
        var that = this;
        setTimeout(() => {that.showHideModal('sampleModal', 'show')  }, 0)
}

showHideModal(id, showOrHide) {
        $(this[id].nativeElement).modal(showOrHide);
}

Now you won't get the 'nativeElement undefined error' even with *ngIf. Why does this work? It is because of the angular/JS event loop. Basically *ngIf ensures that the modal with the id as 'sampleModal' only comes into the DOM as a native element when the boolean 'showModal' becomes true. Therefore, the command for actually showing the Modal which is the 'showHideModal' function in the given code must be a part of the next loop. While the openModal function is running, the setTimeout will set its calling function at the end of the loop, i.e. after the current function call stack is over. Therefore, we also need to pass in the context of the showHideModal function for it to run in the given scope. So, we use a variable 'that' and set it to the execution context 'this' of the class in which it is defined and pass that.showHideModal in the setTimeout. So, by the time setTimeout executes its callback function, the *ngIf condition has become true and the modal is present in the DOM to be shown or hidden by the 'showHideModal' function.
Hope it helped! :)

@doivosevic
Copy link

Seeing as this is the first google result for this generic issue I would like to point out that the fix for me was to change target back to es5 from es6 related to angular/components#13695

@therence11
Copy link

TypeError: Cannot read property 'nativeElement' of undefined
at MaptransportPage.ngOnInit (maptransport.page.ts:43)

C'est l'erreur que je rencontre dans l’exécution de mon code

import { Component, OnInit, ViewChild } from '@angular/core';
import { ActionSheetController, Platform, AlertController } from '@ionic/angular';
import {
GoogleMaps,
GoogleMap,
GoogleMapsEvent,
GoogleMapOptions,
CameraPosition,
MarkerOptions,
Marker,
Environment,
MyLocation,
GoogleMapsAnimation
} from '@ionic-native/google-maps';

import { LoadingController } from '@ionic/angular';

@component({
selector: 'app-maptransport',
templateUrl: './maptransport.page.html',
styleUrls: ['./maptransport.page.scss'],
})
export class MaptransportPage implements OnInit {
@ViewChild('map') mapElement: any;
map: GoogleMap;
loading: any;

constructor(
public alertController: AlertController,
public actionCtrl: ActionSheetController,
private platform: Platform,
private loadingCtrl: LoadingController
) {
if (this.platform.is('cordova')) {
this.loadMap();
}

}

ngOnInit() {
this.mapElement = this.mapElement.nativeElement;

this.mapElement.style.width = this.platform.width() + 'px';
this.mapElement.style.height = this.platform.height() + 'px';

this.loadMap();

}

async loadMap() {
this.loading = await this.loadingCtrl.create({message: 'Patientez Svp...' });
await this.loading.present();

Environment.setEnv({
  API_KEY_FOR_BROWSER_RELEASE: 'AIzaSyA6ohTVr2poCuSWxZznl_GTz25eYiL2wD8',
  API_KEY_FOR_BROWSER_DEBUG: 'AIzaSyA6ohTVr2poCuSWxZznl_GTz25eYiL2wD8'
});

const mapOptions: GoogleMapOptions = {
  controls: {
    zoom: false
  }
};

this.map = GoogleMaps.create(this.mapElement, mapOptions);

try {
  await this.map.one(GoogleMapsEvent.MAP_READY);

  this.addOriginMarker();
}catch(error) {
  console.log(error);
}

}

//Script pour afficher la position de l'utilisateur à l'aide d'un marker sur la carte;
async addOriginMarker() {
try {
const myLocation: MyLocation = await this.map.getMyLocation();

  await this.map.moveCamera({
    target: myLocation.latLng,
    zoom: 18
  });

  this.map.addMarkerSync({
    title: 'Origem',
    icon: '#000',
    animation: GoogleMapsAnimation.DROP,
    position: myLocation.latLng
  });

  
}catch (error) {
  console.error(error);
}finally {
  this.loading.dismiss();
}  

}

async transportOptions() {
const actionSheet = await this.actionCtrl.create({
buttons: [{
text: 'Choix de Transport',
icon: 'share',
handler: () => {
console.log('routerLink="/choixtransport" ');
}
}, {
text: 'Offres de Transport',
icon: 'reader',
handler: () => {
console.log('Play clicked');
}
}, {
text: 'Favorite',
icon: 'heart',
handler: () => {
console.log('Favorite clicked');
}
}, {
text: 'Annuler',
icon: 'close',
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
}
}]
});
await actionSheet.present();
}

}

HJK181 pushed a commit to CommerceExperts/vaadin-grid that referenced this issue Jul 27, 2020
…rride

Prevent overriding text when clear icon is shown
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants