Cannot read property 'first_name' of undefined - javascript

I have this issue
Cannot read property 'first_name' of undefined
This is my HTML File checkout.html
<ion-content>
<ion-item>
<ion-label>First Name</ion-label>
<ion-input type="text" [(ngModel)]="newOrder.billing.first_name"></ion-input>
</ion-item>
....
</ion-content>
and this is my ts file checkout.ts
this.storage.get("userLoginInfo").then((userLoginInfo) => {
this.userInfo = userLoginInfo.user;
let email = userLoginInfo.user.email;
let id = userLoginInfo.user.id;
this.WooCommerce.getAsync("customers/email/"+email).then((data) => {
this.newOrder = JSON.parse(data.body).customer;
})
})
and this is an image of the error

It seems that you are loading the newOrder asynchronous.
As a result your page get rendered, but the async-task is not finished yet.
You wrote that you declared this.newOrder.billing = {} in the constructor.
Because of that, he now tries to read newOrder.billing.first_name in your html.
newOrder.billing is an empty object, so there is no first_name there.
Only when the async-task has finished, this data will be existent. But currently Angular throws an error before that could happen.
There are two very common ways to handle a situation like that.
You could tell the template that it should NOT render the part with the missing data
<ion-item *ngIf="newOrder.billing">
<ion-label>First Name</ion-label>
<ion-input type="text" [(ngModel)]="newOrder.billing.first_name"></ion-input>
</ion-item>
Then the *ngIf will see that the variable is undefined and that part of the template won´t be shown in the DOM => no error :-)
As soon as the async-task finishes, the variable is not undefined anymore and the template-part will be shown.
Another way (more common when you just want to show data) is using
{{ newOrder.billing.first_name | async }}
The async-pipe will also make sure that Angular could handle that one.
In combination with [ngModel] i think the async-pipe will not work. But it may be worth a try.
warm regards

Just add a quotemark, i think it will help.
<ion-input type="text" [(ngModel)]="newOrder?.billing.first_name"></ion-input>

You can use simply a quote mark with the object in your interpolation as said by
#Kraken.
But two solution provided by the #JanRecker:
if you use async pipe in your interpolation it won't work, because this works with Observables rxjs.
So, adding a quote mark really a helpful option.

Related

Angular Reactive Forms, is it a bad practice to use "myForm.get('myFormControl'')" in the template?

I know that using function calls in Angular templates is a bad practice. In short it is because Change Detection will make the function run many times, which will lead to bad performance. (This article goes more into depth on the topic)
I have been following this rule closely except from one exception, and that is when I need the value from my Reactive Form in my template. Usually I use it like this:
My way of doing it
Template: <div *ngIf="myFormGroup.get('name').invalid">Filling out name is required</div>
Here I am not following the rule because I am calling the function myFormGroup.get('name') inside my template. To solve this I decided to go to the Angular documentation to see how they do this, and I found a similar example here: https://angular.io/guide/form-validation#built-in-validator-functions.
What the Angular documentation does is that they put the form control in a getter, and then they use the getter in the template, like this:
Angular documentation way of doing it
Component class: get name() { return this.myFormGroup.get('name'); }
Template: <div *ngIf="name.invalid">Filling out name is required</div>
My questions are
Is it ok to use "myFormGroup.get('name')" in a template, even though it breaks the rule of not having function calls in the template?
Is the Angular Documentation way of doing this any different from my way of doing it when it comes to performance? (My understanding is that using a getter this way does noe solve the Change Detection Performance issue)
You can do like this:
In Template:
<input type="text" id="name" formControlName="name" />
<div *ngIf="isControlInvalid('name')">
Something
</div>
In Component:
isControlInvalid(controlName: string): boolean {
const control = this.myForm.controls[controlName];
const result = control.invalid && control.touched;
return result;
}

How to use reactive forms inside ng-template

I have just started with Angular 4 and I need to develop a CRUD grid, where the user can add, edit or delete rows.
During my research I found this article where it shows how to create the grid and also the actions: Angular 4 Grid with CRUD operations.
Looking at his code, what called my attention was the way he is using the ng-template to toggle between edit/view mode.
<tr *ngFor="let emp of EMPLOYEES;let i=idx">
<ng-template [ngTemplateOutlet]="loadTemplate(emp)" [ngOutletContext]="{ $implicit: emp, idx: i }"></ng-template>
</tr>
On the article he uses template driven forms to edit the row. However, I was trying to change to reactive forms.
In my attempt to do that, I tried to replace the [(ngModel)] to formControlName and I got some errors. My first attempt I tried to add the [formGroup] at the beginning of the template html inside form element. But when I tried to run and edit the row, I got the following error:
Error: formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class).
When I tried to move the [formGroup] inside the ng-template it works, however I was not able to bind the value to the fields and I had to set the values in the loadTemplate function:
loadTemplate(emp: Employee) {
if (this.selemp && this.selemp.id === emp.id) {
this.rForm.setValue({
id: emp.id,
name: emp.name
});
return this.editTemplate;
} else {
return this.readOnlyTemplate;
}
}
This works and show the values inside the fields in a read only mode :(
Here is the Plunker of what I have got so far.
How can I make a reactive form work with ng-template and how to set values to edit the entries?
Any help is appreciated! Thanks
Actually your form is not readonly, you are just constantly overwriting the input you are entering. Since you are having a method call in template (which is usually not a good idea), loadTemplate gets called whenever changes happen, which in it's turn means that
this.rForm.setValue({
id: emp.id,
name: emp.name
});
gets called over and over whenever you try and type anything. We can overcome this with instead setting the form values when you click to edit. Here we also store the index so that we can use it to set the modified values in the correct place in array, utilizing the index could perhaps be done in a smarter way, but this is a quick solution to achieve what we want.
editEmployee(emp: Employee) {
this.index = this.EMPLOYEES.indexOf(emp)
this.selemp = emp;
this.rForm.setValue({
id: emp.id,
name: emp.name
});
}
so when we click save, we use that index...
saveEmp(formValues) {
this.EMPLOYEES[this.index] = formValues;
this.selemp = null;
this.rForm.setValue({
id: '',
name: ''
});
}
Your plunker: https://plnkr.co/edit/6QyPmqsbUd6gzi2RhgPp?p=preview
BUT notice...
I would suggest you perhaps rethink this idea, having the method loadTemplate in template, will cause this method to fire way too much. You can see in the plunker, where we console log fired! whenever it is fired, so it is a lot! Depending on the case, this can cause serious performance issues, so keep that in mind :)
PS. Made some other changes to code for adding a new employee to work properly (not relevant to question)

Ionic 3 ion-checkbox checked attribute not queryable in Firebase subscribe function

I've spent a while trying to track down a solution to this on Stack Overflow without any luck.
I'm using the Ionic 3 checked attribute in my html page as part of a list of contacts, as such..
<ion-list>
<ion-item *ngFor="let friend of friends_profile_data | async ;">
<ion-label>
<h2>{{friend.name}}</h2>
</ion-label>
<ion-checkbox [checked]="friend.checked" (click)="toggleItem(friend)" ></ion-checkbox>
</ion-item>
</ion-list>
..and have the toggleItem function in the associated .ts file:
toggleItem(friend): void {
friend.checked = !friend.checked;
}
In my test run with a dummy database I was able to successfully query this state of each 'friend' by using the following in the same .ts file (there was no | async in the ion-content line for the local dummy DB)..
this.friends_profile_data.forEach((friend) => {
var i =0;
var total = friend.month.length;
for(;i < total; i++ ) {
if(friend.checked) {
console.log(friend.name);
}
}
});
This runs through all the friends in my contact list, find the ones that I've selected, and gives me their names, not the ones that weren't selected. All good.
With that working fine, I've moved to hooking everything up to a Firebase database. Everything has been going well, except that when I tried to do a similar query as above within a Firebase subscribe function, it's not working. Here's what the code for that looks like..
this.friends_profile_data.subscribe(friends => {
friends.forEach((friend) => {
if (friend.checked) {
console.log(friend.name);
}
})
});
Where friends_profile_data in this case is returning a similar set of data as found in the dummy local database, but from my Firebase DB.
Instead of only returning the friends that I've selected in the list, it's logging every friends name.. as though it's not registering the friend.name in the IF statement.
I've tried using alternatives, in case any of them worked, such as:
friend.checked = true
friend.checked == true
friend.checked === true
..but nothing works.
Should this be working? Is there some issue I'm not aware of that's introduced by doing the query on the checked attribute within a Firebase subscribe function?
Alternatively, have I just been lucky in it working with the local dummy DB, and there's actually a more robust way of approaching this within the Firebase subscribe function? The use of the checked attribute approach is from a tutorial I'd been through a while back in early Ionic 2 days.
Thanks for any insight anyone can offer. As this is my first post, I've tried to be as informative as possible, but please let me know if there's anything else I can add that would help in trouble shooting the issue.
Before calling subscribe on the Observable, since this Observable contains an array of friends, you need to filter these friends using your rule first. You can use map function to transform the 'friends_profile_data'. It creates a new observable with the filtered content.
this.friends_profile_data
.map(array0 => {
return array0.filter(friend => friend.checked === true)
})
.subscribe(
...
);

Receiving error using ngFor and ngIf in Angulsrjs2 and Ionic 2

JoinedArray is an array with some values.
I have to check if the event.listDetails.accessId is present there in the array or not . If accessId is not there in the array then the event-card should not print
<ion-list *ngSwitchCase="'joined'">
<event-card *ngIf="event.listDetails.accessId in joinedArray " ></event-card>
</ion-list>
error is:
EXCEPTION: Error: Uncaught (in promise): Template parse errors:
Property binding ngIfIn not used by any directive on an embedded
template. Make sure that the property name is spelled correctly and
all directives are listed in the "directives" section. ("e keys and i
have to check if it is present in the array or not ..please
suggest-->-->
[ERROR ->]
"): EventListComponent#31:12
Instead of including that logic in your view, you could just create a property in the component like this:
public showCard: bool;
// In the constructor or somewhere else
//...
this.showCard = joinedArray.indexOf(event.listDetails.accessId) > -1;
// ...
And then in the view:
<ion-list *ngSwitchCase="'joined'">
<event-card *ngIf="showCard"></event-card>
</ion-list>
That way we keep things simple, the view just takes care of showing or hiding things, and the component code decides whether if that should be shown or not.
Please also notice that I've used the indexOf() (Reference) method to see if the array contains that value.

AngularJS OneTime Binding with ng-repeat doesn't let me refresh scope

I have a list that I am using ng-repeat and one-time binding because there is no need for me to update once I have the data.
I was hoping that I could use one-time binding and force a digest, but I get the common error that a $digest is already being processed or whatever it is.
My code looks like the following
$scope.$on(IonicEvents.beforeEnter, function() {
Locations.getAllLocations()
.then(function(allLocations) {
$scope.model.locations = allLocations;
//I have tried $scope.$digest()..
//also $scope.$apply()...
}, function(error) {
$log.error('Error retrieving locations');
})
});
My ng-repeat in my template looks like the following:
<ion-item class="item" ng-repeat="location in ::model.locations">
<div class="row">
<div class="col">
<p>{{::location.name}}</p>
</div>
</div>
</ion-item>
Any ideas, or am I completely off-track?
I think First you may check if(!$scope.$$phase) then apply your proccess
$scope.$on(IonicEvents.beforeEnter, function() {
if(!$scope.$$phase) {
Locations.getAllLocations()
.then(function(allLocations) {
$scope.model.locations = allLocations;
//I have tried $scope.$digest()..
//also $scope.$apply()...
}, function(error) {
$log.error('Error retrieving locations');
})
}
});
Yes I think you are off-track.
My idea for your particular situation would be using a clone object (with no reference to the main object) once they are retrieved from Locations.getAllLocations() using
$scope.clonedLocationObj = angular.copy($scope.model.locations);
and render this object to your view so that it won't effect your main object.
As far as one way binding is concerned, Angular doesn't let you change the one time binded model again because when we declare a value such as {{ ::foo }} inside the DOM, once this foo value becomes defined, Angular will render it, unbind it from the watchers and thus reduce the volume of bindings inside the $digest loop. Simple!
angular one time binding syntax article
Hope it helps.

Categories

Resources