Closed Splaktar closed 7 years ago
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.
Investigated this some more via input from the Angular 2 team on https://github.com/angular/angular/issues/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()
?
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.
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.
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.
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! :)
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 https://github.com/angular/material2/issues/13695
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(); }
}
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 thegrid-ready
event was removed?So I've been trying to use the
@ViewChild('grid')
approach withngAfterViewInit()
, but that isn't working. I've triedngAfterContentInit()
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:
Component:
Always results in: