Ionic2 / Angular2 Forms With FormGroup For Nested Components - ionic2

In Ionic2 / Angular2, I'm struggling to get FormGroup and FormGroupName working with nested Components, keep getting errors like.
Cannot find control with path: 'location -> latitude'
The Form page renders a dynamic list of fields.
<form [formGroup]="formGroup" (ngSubmit)="postForm()">
<ion-list>
<ion-item *ngFor="let field of fields">
<field-location [field]="field" [formGroup]="formGroup" *ngIf="field.input == 'location'"></field-location>
<!-- OTHER FIELD TYPES HERE -->
</ion-item>
</ion-list>
</form>
The Form class creates the FormGroup and FormControl items in ionViewDidLoad.
this.formGroup = new FormGroup({});
for (let index in this.fields) {
let field = this.fields[index];
if (field.input == 'location') {
this.formGroup.addControl(field.key, new FormGroup({
latitude: new FormControl(''),
longitude: new FormControl('')}));
}
else {
this.formGroup.addControl(attribute.key, new FormControl(''));
}
}
The Location class defines field and formGroup as inputs.
#Component({
selector: 'field-location',
templateUrl: 'location.html',
inputs: ['field', 'formGroup']
})
export class LocationComponent {
field: any = {};
formGroup: FormGroup;
constructor() {
}
}
The Location view takes the FormGroup as an Input and uses the field Key as the formGroupName.
<ion-item [formGroup]="formGroup" *ngIf="formGroup">
<ion-label>{{field.label}}</ion-label>
<div [formGroupName]="field.key">
<ion-input type="text" hidden [formControlName]="latitude"></ion-input>
<ion-input type="text" hidden [formControlName]="longitude"></ion-input>
</div>
</ion-item>
How do you get formGroupName to work in nested Components? To you need to also pass the nested FormGroup in from the parent?

Ok, I found the problem, so sharing the solution in case it helps others.
Looks like the issue was related to binding on [formControlName]="latitude".
<ion-item [formGroup]="formGroup" *ngIf="formGroup">
<ion-label>{{field.label}}</ion-label>
<div [formGroupName]="field.key">
<ion-input type="text" hidden [formControlName]="latitude"></ion-input>
<ion-input type="text" hidden [formControlName]="longitude"></ion-input>
</div>
</ion-item>
But it should just have been formControlName="latitude".
<ion-item [formGroup]="formGroup" *ngIf="formGroup">
<ion-label>{{field.label}}</ion-label>
<div [formGroupName]="field.key">
<ion-input type="text" hidden formControlName="latitude"></ion-input>
<ion-input type="text" hidden formControlName="longitude"></ion-input>
</div>
</ion-item>
After making this change, the nested Component works as expected. Hope this helps someone else!

Related

Angular 4 give error as "No value accessor for form control with unspecified name attribute"

I am developing mobile application in ionic 2 with angular 4. I have implemented login page with reactive form previously it is working correctly but while implementing feature as after logout control will redirect to login page. So, I have created sharedModule which I have imported in my app.module.ts file. After this I am getting an below error:
No value accessor for form control with unspecified name attribute
Login.ts
constructor(public navCtrl: NavController, public navParams: NavParams,
private auth: AuthServiceProvider, private alertCtrl: AlertController,
private loadingCtrl: LoadingController, public storage: Storage,
private fb: FormBuilder) {
this.loginForm = this.fb.group({
'dashboardUsername': [null, Validators.required],
'dashboardPassword': [null, Validators.required]
})
}
Login.html
<form [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)">
<ion-row class="htm-container">
<ion-col>
<ion-list inset>
<ion-item class="inputRounded" [class.error]="!loginForm.controls['dashboardUsername'].valid && loginForm.controls['dashboardUsername'].touched">
<ion-input type="text" placeholder="Dashboard username" [formControl]="loginForm.controls['dashboardUsername']" name="dashboardUsername"></ion-input>
</ion-item>
<div class="htm-error" *ngIf="loginForm.controls['dashboardUsername'].hasError('required') && loginForm.controls['dashboardUsername'].touched">
<p>Username is required!</p>
</div>
<ion-item class="inputRounded" [class.error]="!loginForm.controls['dashboardPassword'].valid && loginForm.controls['dashboardPassword'].touched">
<ion-input type="password" placeholder="Dashboard password"
[formControl]="loginForm.controls['dashboardPassword']" name="dashboardPassword"></ion-input>
</ion-item>
<div class="htm-error" *ngIf="loginForm.controls['dashboardPassword'].hasError('required') &&
loginForm.controls['dashboardPassword'].touched">
<p>Password is required!</p>
</div>
</ion-list>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="signup-col">
<button ion-button color="light" class="submit-btn btn-bottom-margin" full type="submit"
[disabled]="!loginForm.valid">Sign In
</button>
</ion-col>
</ion-row>
</form>
Third party controls such as yours require ControlValueAccessor to function with angular forms otherwise they behave like native elements and can use DefaultValueAccessor. in such cases you should use ngDefaultControl attribute.
In your case
<ion-input type="text" placeholder="Dashboard username"
ngDefaultControl
[formControl]="loginForm.controls['dashboardUsername']" name="dashboardUsername">
Should fix that for you.
You cannot use formControl and name at same time.
If you want a name attribute than you may write as:
<ion-input type="password" placeholder="Dashboard password"
[formControl]="loginForm.controls['dashboardPassword']" [attr.name]="dashboardPassword"></ion-input>

Angular2,Ionic2: How to do 2way binding for radio button in nested *ngfor using ngModel?

I've my API in JSON in the following format.
I want to display this in the form of question & choices format. I'm trying to display the choices using radio buttons. But my ngModel is not working. I want to access the whole object of the selected choice using ngModel in my .ts file.
Here is my ts code
export class Quiz implements OnInit {
testcontents: Array<any>=[];
choiceObj:any;
constructor(public navCtrl: NavController, public navParams: NavParams,public http:Http) {}
ngOnInit()
{
this.launchfunc();
}
launchfunc()
{
this.http.post('launchtest/getTestContent',this.launchtestobj).subscribe(resp=>{
this.testcontents = resp.data.questions;
});
}
selectChoice($event)
{
console.log("12312341234",event);
console.log("obj",this.choiceObj);
}
Here is my html code
<ion-content padding>
<div *ngFor="let qContent of testcontents; let i = index">
{{i+1}} {{qContent.questionText}}
<div *ngFor="let choiceObj of qContent.choice">
<input name='options' type='radio' [value]='choiceObj' [(ngModel)]='choiceObj.choice' (ngModelChange)="selectChoice(choiceObj)" />
<label [for]='choiceObj'>{{choiceObj.choiceText}}</label>
</div>
</div>
This is the output for the above code.
I don't understand why is obj undefined.
I've a problem with the ngModel. I'm not understanding what should I put my ngModel as and how should I access the "choiceObj" in typescript file.Can someone please help me.
the model is choiceObj.choice which is not exist in object of choice.
you need to replace it with 'choiceObj.choiceId'
<ion-content padding>
<div *ngFor="let qContent of testcontents; let i = index">
{{i+1}} {{qContent.questionText}}
<div *ngFor="let choiceObj of qContent.choice">
<input name='options' type='radio' [value]='choiceObj' [(ngModel)]='choiceObj.choiceId' (ngModelChange)="selectChoice(choiceObj)" />
<label [for]='choiceObj'>{{choiceObj.choiceText}}</label>
</div>
</div>

Angular2 how to pass an array to component that is part of a reactive form

I am working with Angular2 reactive form (which is a resume form), there are a few fields on the form and then a component qualifications that i need to pass an array to (so that using loop i can display the qualifications of users).
This is how i am passing the data to the component qualifications
<ion-list-header> Qualification & Education </ion-list-header>
<div formArrayName="qualifications">
<div *ngFor="let qualification of profileForm.controls.qualifications.controls; let i=index"
class="panel panel-default">
<qualifications [group]="profileForm.controls.qualifications.controls[i]"></qualifications>
</div>
</div>
This is the html of a qualifications component
<div [formGroup]="qualificationForm">
<ion-item padding>
<ion-label stacked>Qualification</ion-label>
<ion-input type="text" formControlName="position"></ion-input>
<small [hidden]="qualificationForm.controls.position.valid" class="text-danger">
Qualification is required
</small>
</ion-item>
<ion-item padding>
<ion-label stacked>Institution</ion-label>
<ion-input type="text" formControlName="location"></ion-input>
</ion-item>
<ion-item padding>
<ion-label stacked>Date</ion-label>
<ion-input type="date" formControlName="at_date"></ion-input>
</ion-item>
</div>
This is how the qualification array looks like
0:{id: 1744687, location: "Singapore", position: "B Tech", at_date: "09/2014"}
1:{id: 1762435, location: "Singapore", position: "B Tech 2", at_date: "11/2014"}
2:{id: 1802837, location: "Singapore", position: "B Tech 3", at_date: "12/2016"}
I am not able to understand how to pass array of objects to this component so that it can display the qualifications in loop
In ngOnInit i am calling the addQualification function which is as following
addQualification() {
const control = <FormArray>this.profileForm.controls['qualifications'];
const addrCtrl = this.initAddress();
control.push(addrCtrl);
}
initAddress() {
return this.formBuilder.group({
position: ['', Validators.required],
location: [''],
at_date: ['']
});
}
The problem I am having is the form only display the first object of the array and is not looping through

ionic 2 <ion-select> wrapped in a <button> design

I want the <ion-select> of a ionic form to look like a <button>.
Here is my html template:
<form (ngSubmit)="doSubmit()" [formGroup] = "myForm" >
<ion-item>
<!-- <button ion-button block> -->
<ion-select type="text" formControlName="choose" placeholder = "choose">
<ion-option value=1>option 1</ion-option>
<ion-option value=2>option 2</ion-option>
</ion-select>
<!-- </button> -->
</ion-item>
// ... other input fields + doSubmit() function ...
</form>
As it is, it works and I can manage the selection. But I want it to look like a button. So I added some tag <button ion-button block></button> around the <select> tags. If the <button> tags are uncommented, it does not work: It does not show as a button and the select functionnaly is dead. Also I naively expected it would execute as I had wished it, but it doesn't.
Has anyone an idea?

data-value attribute in emberjs

I want to add data-value attribute in input element:
{{input type="text" data-value=ec.ec_id value=ec.ecl_subject placeholder=ec.ecl_en_subject name="ecl_subject" class="form-control" }}
But its not visible in browser:
<input id="ember874" class="ember-view ember-text-field form-control" placeholder="Doctor4US: Appointment" type="text" name="ecl_subject">
ADDING DATA ATTRIBUTES
By default, view helpers do not accept data attributes
i.e.
{{input type="text" data-value=ec.ec_id value=ec.ecl_subject name="ecl_subject" }}
Render as
<input id="ember257" class="ember-view ember-text-field" type="text" value="12">
There are two ways to enable support for data attributes. One way
would be to add an attribute binding on the view, e.g. Ember.TextField for the specific attribute:
Ember.TextField.reopen({
attributeBindings: ['data-value']
});
Now the same handlebars code above renders the following HTML:
<input id="ember259" class="ember-view ember-text-field"
type="text" data-value="110" value="12">
You can also automatically bind data attributes on the base view with the following:
Ember.View.reopen({
init: function() {
this._super();
var self = this;
// bind attributes beginning with 'data-'
Em.keys(this).forEach(function(key) {
if (key.substr(0, 5) === 'data-') {
self.get('attributeBindings').pushObject(key);
}
});
}
});
For more detail reffer:https://guides.emberjs.com/v1.10.0/templates/binding-element-attributes/
Ember Input helpers doesn't allow data-attributes. The list of allowed attributes can be found at https://guides.emberjs.com/v2.6.0/templates/input-helpers/
The solution for problem is that you use html tag itself, when you want to place data-attributes like below
<input type="text" data-value="{{ec.ec_id}}" value="{{ec.ecl_subject}}" placeholder="{{ec.ecl_en_subject}}" name="ecl_subject" class="form-control/>