Ionic radio-group with Angular Reactive Forms - ionic-framework

I'm working on a project in Ionic and need to use reactive forms from Angular 5. I need to allow a user to select a language preference and I'm doing that with radio-group and ion-radio, but when I make changes to the form, I'm getting
ERROR Error: There is no FormControl instance attached to form control element with name: 'preferredLanguage'
Here is my template with the form
<ion-header>
<ion-navbar>
<ion-title>User Profile</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<form *ngIf="user" [formGroup]="profileForm" (ngSubmit)="saveUser()">
<ion-item>
<ion-label>Display Name</ion-label>
<ion-input type="text" formControlName="displayName"></ion-input>
</ion-item>
<ion-item>
<ion-label>First Name</ion-label>
<ion-input type="text" formControlName="firstName"></ion-input>
</ion-item>
<ion-item>
<ion-label>Last Name</ion-label>
<ion-input type="text" formControlName="lastName"></ion-input>
</ion-item>
<ion-item>
<ion-label>Email</ion-label>
<ion-input type="text" formControlName="email"></ion-input>
</ion-item>
<ion-item>
<ion-label>Street</ion-label>
<ion-input type="text" formControlName="street"></ion-input>
</ion-item>
<ion-item>
<ion-label>City</ion-label>
<ion-input type="text" formControlName="city"></ion-input>
</ion-item>
<ion-item>
<ion-label>State</ion-label>
<ion-input type="text" formControlName="state"></ion-input>
</ion-item>
<ion-item>
<ion-label>Zip Code</ion-label>
<ion-input type="text" formControlName="zip"></ion-input>
</ion-item>
<ion-list radio-group formControlName="preferredLanguage">
<ion-list-header>
Select Preferred Language
</ion-list-header>
<ion-item>
<ion-label>English</ion-label>
<ion-radio value="en-US"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Spanish</ion-label>
<ion-radio value="es-US"></ion-radio>
</ion-item>
</ion-list>
<button type="submit" ion-button>Submit Profile</button>
</form>
</ion-content>
Here is my method that initializes the form
initProfileForm() {
this.profileForm = this.formBuilder.group({
displayName: [this.user.profile.displayName, Validators.required],
firstName: [this.user.profile.firstName, Validators.required],
lastName: [this.user.profile.lastName, Validators.required],
email: [this.user.profile.email, Validators.required],
street: [this.user.profile.street, Validators.required],
city: [this.user.profile.city, Validators.required],
state: [this.user.profile.state, Validators.required],
zip: [this.user.profile.zip, Validators.required] ,
preferredLanguage: [this.user.profile.preferredLanguage,
Validators.required]
});
}
Has anyone else had this issue and how did you solve it?

I ended up coming up with a solution. This blog helped me come up with an answer: https://robferguson.org/blog/2017/11/19/ionic-3-and-forms/ Apparently, radio-group doesn't work with formControlName so instead I ended up using this:
<ion-list radio-group [formControl]="profileForm.controls['preferredLanguage']">
<ion-list-header>
Select Preferred Language
</ion-list-header>
<ion-item>
<ion-label>English</ion-label>
<ion-radio value="en-US"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Spanish</ion-label>
<ion-radio value="es-US"></ion-radio>
</ion-item>
</ion-list>
This fixed the issue so there is no longer an error.

Make some amendments in your HTML code:
<ion-item>
<ion-label>English</ion-label>
<ion-radio value="en-US" formControlName="preferredLanguage"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Spanish</ion-label>
<ion-radio value="es-US" formControlName="preferredLanguage"></ion-radio>
</ion-item>
So basically instead of using it on parent use it individually for each radio button. This worked for me.
Hope it helps!

get preferredLanguage() {
return this.profileForm.get('preferredLanguage').value;
}
set preferredLanguage(ev: CustomEvent) {
this.profileForm.get('preferredLanguage').patchValue(ev.detail.value);
}
<ion-radio-group [value]="preferredLanguage" (ionChange)="preferredLanguage = $event">
<ion-item>
<ion-label>English</ion-label>
<ion-radio value="en-US"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Spanish</ion-label>
<ion-radio value="es-US"></ion-radio>
</ion-item>
</ion-radio-group>

Related

Ionic Validation Array Form

I have the below form that is generate dynamically, I am trying to make a validation but is fail
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'model: sdaDAd'. Current value: 'model: '.
HTML
<div [formGroup]="someForm">
<ion-item-group *ngFor="let att of day; let idx = index">
<ion-item >
<ion-label color="primary">{{att.label}}{{idx+1}}. Day</ion-
label>
<ion-input formControlName="day" type="text" text-right
[(ngModel)]="day[idx].value"></ion-input>
</ion-item>
<ion-item >
<ion-label color="primary">{{att.label}}{{idx+1}}. Exc</ion-label>
<ion-input type="text" formControlName="exc" text-right
[(ngModel)]="exc[idx].value"></ion-input>
</ion-item>
<ion-item >
<ion-label color="primary">{{att.label}}{{idx+1}}.
Hint/Repeats</ion-label>
<ion-input type="text" formControlName="hint" text-right
[(ngModel)]="hint[idx].value"></ion-input>
</ion-item>
<span color=danger float-right ion-button icon-left clear
*ngIf="exc.length > 0"
(click)="removeInputField(idx)"><ion-icon name="close"></ion-icon>
Remove
</span>
</ion-item-group>
<ion-card *ngIf="data_exc">
<ion-item *ngFor="let att of exc; let i=index">
<div class="card-title">Exc: {{att.value}}</div>
<div class="card-title">Hint/Repeats: {{hint[i].value}}</div>
<div class="card-title">Day: {{day[i].value}}</div>
</ion-item>
<div class="card-title">Notes: {{workoutData.notes}}</div>
</ion-card>
<button ion-button (click)="Add()">+Add</button>
<button ion-button (click)="goTo()" >Preview</button> <br>
<ion-toolbar>
<ion-item>
<ion-textarea placeholder="Tap here to enter a new note"
[(ngModel)]="workoutData.notes" formControlName="notes"
autocomplete="on" autocorrect="on"></ion-textarea>
</ion-item>
</ion-toolbar>
<button [disabled]="!someForm.valid" ion-button full
(click)="presentConfirmCustomWorkout()" type="submit" >Submit</button>
</div>
Validation to check if the form is empty so I will not get empty values in array
this.someForm = formbuilder.group({
'day': [this.day.value, Validators.compose([Validators.required])],
'exc': [this.exc.value, Validators.compose([Validators.required])],
'hint': [this.hint.value, Validators.compose([Validators.required])],
'notes': ['', Validators.compose([Validators.required])],
});

Ionic 3: How to fix ion-list-header at the top and scroll only list-item, not ion-content?

I have an ion-list on page, and I want to scroll only list-item, not the whole ion-content.
Here is my code:
<ion-content scroll="false" no-padding>
<ion-list>
<ion-list-header>Select your Status</ion-list-header>
<ion-scroll style="width:100%; height:100vh" scrollY="true">
<ion-item>
<h2>Available</h2>
</ion-item>
<ion-item>
<h2>Busy</h2>
</ion-item>
<ion-item>
<h2>At school</h2>
</ion-item>
<ion-item>
<h2>At the movies</h2>
</ion-item>
<ion-item>
<h2>At work</h2>
</ion-item>
<ion-item>
<h2>Battery about to die</h2>
</ion-item>
<ion-item>
<h2>Can't talk, text only</h2>
</ion-item>
<ion-item>
<h2>In a meeting</h2>
</ion-item>
<ion-item>
<h2>At the gym</h2>
</ion-item>
<ion-item>
<h2>Sleeping</h2>
</ion-item>
<ion-item>
<h2>Urgent calls only</h2>
</ion-item>
</ion-scroll>
</ion-list>
</ion-content>
I want to fix list-header "Select your status" and scroll only other list-items, but it hides list-header even when I have set scroll="false" to ion-content.
Please help.
Thanks in advance.
You can do as such:
html
<ion-header>
<ion-list-header>Select your Status</ion-list-header>
</ion-header>
<ion-content scroll="false" no-padding>
<ion-list>
<ion-scroll style="width:100%; height:100vh" scrollY="true">
<ion-item>
<h2>Available</h2>
</ion-item>
...
</ion-scroll>
</ion-list>
</ion-content>
Position your ion-list-header inside of ion-header to make it sticky
Result:
Add "sticky" attribute in ion-list-header tag.
Applicable in ionic 2, ionic 3, ionic 4

keyboard hide the ion-input field iOS Ionic 3

When I click on the last ion-input, ion-input display below the keyboard this is my HTML field
<ion-list>
<ion-label> Conveyance
</ion-label>
<ion-item>
<ion-input type="number" [(ngModel)]="modelfirst.conveyance"></ion-input>
</ion-item>
<ion-label> HRA
</ion-label>
<ion-item>
<ion-input type="number" [(ngModel)]="modelfirst.hra"></ion-input>
</ion-item>
<ion-label> Interest Paid on Home Loan
</ion-label>
<ion-item>
<ion-input type="number" [(ngModel)]="modelfirst.home_loan"></ion-input>
</ion-item>
</ion-list>
Everything working on an Android device but not working in iOS if any kind of help let me know.

Proper way to handle fields with same names in Forms

I met strange bug in my application. I template driven form with two addresses two fill:
<ion-list>
<ion-list-header color="secondary">From
<button ion-button icon-only item-right clear small (click)="usePosition($event)">
<ion-icon name="locate"></ion-icon>
</button>
<button ion-button icon-only item-right clear small (click)="searchAddress(true,$event)">
<ion-icon name="search"></ion-icon>
</button>
<button ion-button icon-only item-right clear small (click)="useHome(true,$event)">
<ion-icon name="home"></ion-icon>
</button>
</ion-list-header>
<div>
<ion-item>
<ion-label floating>Street Address*</ion-label>
<ion-input type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="createRequest.legs[0].addressFrom.Street"
required></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Floor/Apartment</ion-label>
<ion-input type="text"
name="Extention"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="createRequest.legs[0].addressFrom.Extention"></ion-input>
</ion-item>
<ion-item padding>
<ion-label floating>City or Borough*</ion-label>
<ion-input type="text" required name="City"
pattern="[a-zA-Z ]*"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="createRequest.legs[0].addressFrom.City">
</ion-input>
</ion-item>
<ion-item padding-bottom>
<ion-label floating>
Zip Code*(5 digits)
</ion-label>
<ion-input type="tel" name="Zip" #ZipF="ngModel"
pattern="\d{5}"
[textMask]="{mask:masks.zip}"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="createRequest.legs[0].addressFrom.Zip"
></ion-input>
</ion-item>
</div>
</ion-list>
<ion-list padding-bottom padding-top>
<ion-list-header>To
<button ion-button icon-only item-right clear small (click)="searchAddress(false,$event)">
<ion-icon name="search"></ion-icon>
</button>
<button ion-button icon-only item-right clear small (click)="useHome(false,$event)">
<ion-icon name="home"></ion-icon>
</button>
</ion-list-header>
<ion-item>
<ion-label floating>Street Address*</ion-label>
<ion-input type="text"
[ngModelOptions]="{standalone: true}"
#Street="ngModel"
[(ngModel)]="createRequest.legs[0].addressTo.Street"
required></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Floor/Apartment</ion-label>
<ion-input type="text"
[ngModelOptions]="{standalone: true}"
#Extention="ngModel"
[(ngModel)]="createRequest.legs[0].addressTo.Extention"
></ion-input>
</ion-item>
<ion-item padding>
<ion-label floating>City or Borough*</ion-label>
<ion-input type="text"
pattern="[a-zA-Z ]*"
#City="ngModel"
[ngModelOptions]="{standalone: true}"
[(ngModel)]="createRequest.legs[0].addressTo.City">
</ion-input>
</ion-item>
<ion-item>
<ion-label floating>Zip Code(5 digits)</ion-label>
<ion-input type="tel" #Zip="ngModel"
pattern="\d{5}"
[ngModelOptions]="{standalone: true}"
[textMask]="{mask:masks.zip}"
[(ngModel)]="createRequest.legs[0].addressTo.Zip"
></ion-input>
</ion-item>
</ion-list>
I tried to use [ngModelOptions]="{standalone: true}" but without any result.
At some moment for unkonown reason two addresses start duplicate each other and even stranger thing in this case that using predefined data(like in useHome() method) didn't give effect. I know that answer is near, so will appreciate any help in advance.
Use unique name attributes for your form fields, this way each form field will be evaluated as separate one. I see some inconsistency in the use of the name attribute, all should have a name attribute, as well as #someName="ngModel" if you want to use validation. Loose the ngModelOptions altogether. I would separate these and do for example From... and To... for the name attribute:
For example the two fields for Street:
<ion-input name="FromStreet" #FromStreet="ngModel"
[(ngModel)]="createRequest.legs[0].addressFrom.Street" required>
</ion-input>
and
<ion-input type="text" name="ToStreet" #ToStreet="ngModel"
[(ngModel)]="createRequest.legs[0].addressTo.Street" required>
</ion-input>
This way all fields are unique.
This line of code:
[ngModelOptions]="{standalone: true}"
Is telling the form that your input element is NOT included as part of the form's data. You only want to do that with controls that you don't want to track on submittal. For example, say you have a checkbox that simply opens or closes a part of the user interface. You don't want that part of the submitted data, so you would use the standalone option so it "stands alone" from the form and its data.

ionic2: How to do calculations in form?

I have a form with product rate and quantity inputs. I need to calculate the value (rate * quantity) and assign the value to amount input field.
Please find the code snippet below:
<form [formGroup]="additemForm" (ngSubmit)="submit(additemForm.value)" >
<ion-row>
<ion-col>
<ion-list inset>
<ion-item>
<ion-label>Product :</ion-label>
<ion-select formControlName="product">
<ion-option *ngFor="let product of productArray" value="{{product.code}}" selected="false">{{product.name}}</ion-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label>QUANTITY :</ion-label>
<ion-input type="number" formControlName="qty"></ion-input>
</ion-item>
<ion-item>
<ion-label>RATE:</ion-label>
<ion-input type="number" formControlName="rate"></ion-input>
</ion-item>
<ion-item>
<ion-label>Value :</ion-label>
<ion-input type="number" formControlName="value"></ion-input>
</ion-item>
</ion-list>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="signup-col">
<button ion-button class="submit-btn" type="submit" [disabled]="!additemForm.valid">Submit</button>
<button ion-button type="button" (click)='cancel()' >Cancel</button>
</ion-col>
</ion-row>
</form>
You need to use [(ngModel)] for this.
<ion-item>
<ion-label>QUANTITY :</ion-label>
<ion-input type="number" formControlName="qty" [(ngModel)]="quantity"></ion-input>
</ion-item>
<ion-item>
<ion-label>RATE:</ion-label>
<ion-input type="number" formControlName="rate" [(ngModel)]="rateValue"></ion-input>
</ion-item>
<ion-item>
<ion-label>Value :</ion-label>
<ion-input type="number" formControlName="value" [(ngModel)]="rateValue * quantity"></ion-input>
</ion-item>
Now, still amount input field will be edible after this. And changing this value will affect the other 2 input values also. You can disable the input, so it will appear as a read-only input. This worked in my code.