I'm trying to set a dynamic form in Angular2.
So, in my ngOnInit function, I made a Ajax request to get a JSON with form data.
Like this :
export class CustomerEditComponent{
private customer : Customer = new Customer();
private customerForm;
constructor(private _CustomersService: CustomersService, private _routeParams: RouteParams, private _fb: FormBuilder){
this.customerForm = _fb.group({
name: [],
job: [],
arrival_date: []
});
}
ngOnInit(){
let id = this._routeParams.get('id');
this._CustomersService.getById(id).subscribe(res => {
this.customer = res;
});
}
onSubmit(event){
console.log(event);
}
}
So, at the component construct, 'customer' is equals to a newest one. (all properties are empty). But just after, we set value to every properties.
No problem for that, my input has the correct values.
But, if I submit my form, the form value is equals to :
Object {name: null, job: null, arrival_date: null}
(But the form in the view is correctly populate).
Here my form (condensed) :
<form [ngFormModel]="customerForm" (ngSubmit)="onSubmit(customerForm.value)">
<input md-input [(value)]="customer.name">
<input md-input [(value)]="customer.job">
<input md-input type="date" [(value)]="customer.arrival_date">
<button type="submit">Save</button>
</form>
I use [(value)] cause ng2-material package. (I already try with ngControl).
I think my code is 'wrong' about this feature, but I dunno where.
Thanks !
EDIT :
I have found the answer !
With ng2-material, we need to set [(value)] and [(ngModel)] together on every input like this :
<form [ngFormModel]="customerForm" (ngSubmit)="onSubmit(customerForm)">
<input md-input [(value)]="customer.name" [(ngModel)]="customer.name">
<input md-input [(value)]="customer.job" [(ngModel)]="customer.job">
<input md-input type="date" [(value)]="customer.arrival_date" [(ngModel)]="customer.arrival_date">
<button type="submit">Save</button>
</form>
[(value)] is used by ng2-material to set the value 'on front'.
I think that the problem is that you didn't associate your form inputs with their controllers within the ngFormControl directive in your template. You should refactor that way:
<form [ngFormModel]="customerForm" (ngSubmit)="onSubmit(customerForm.value)">
<input md-input [(value)]="customer.name"
ngFormControl="name">
<input md-input [(value)]="customer.job"
ngFormControl="job">
<input md-input type="date" [(value)]="customer.arrival_date"
ngFormControl="arrival_date">
<button type="submit">Save</button>
</form>
See this link from ng2-material samples: https://github.com/justindujardin/ng2-material/blob/master/examples/components/input/form_builder.html
Otherwise why don't you use the customer object instead of the customerForm.value one?
Hope it helps you,
Thierry
Related
Here's my stackblitz
When I click on my button (in Stackblitz link) and push a value to the array, I'd like to have the field be valid, but it's remaining invalid. If I hard code in a value, it shows as valid, but not if I push a value from a click event.
QUESTION - Can anyone help me understand why? Do I need to use some sort of formArray type?
Here is my code.
registerForm: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.registerForm = this.formBuilder.group({
practicedStyles: [[], [Validators.required]]
});
}
get practicedStyles() {
return this.registerForm.get('practicedStyles');
}
add() {
this.practicedStyles.value.push(1);
}
<div class="card m-3">
<h5 class="card-header">Angular 8 Reactive Form Validation</h5>
<div class="card-body">
<form [formGroup]="registerForm">
<p>Is valid: {{practicedStyles.valid}}</p>
<p>Is required: {{practicedStyles.errors?.required}}</p>
<button class="btn btn-primary" (click)="add()">Add</button>
</form>
</div>
</div>
Did you try using FormGroup.setValue?
add() {
this.registerForm.get('practicedStyles').value.push(1);
this.registerForm.setValue({practicedStyles: this.registerForm.get('practicedStyles').value});
}
I think pushing the value directly to the array wouldn't trigger the change because you're not changing the object's reference. And if you try to reassign the FormGroup control to a new object:
this.practicedStyles.value = 1
you'll get the following errro message Cannot assign to 'value' because it is a read-only property.
When manipulating FormGroup controls you should use the FormGroup provided methods to do so:
FormGroup.setValue
FormGroup.patchValue
So even though you are not changing the object's reference, you are notifying the framework that the object did change.
How could i update a doc inside my firestore without precisely write in the code the field i want to update , cause already is reached through the form:
.Lets say the HTML tag in Angular already brings both the key and the import(that one to be updated):
HTML Tags
<form [formGroup]="importNgForm" (submit)="addUniqueImport()">
<div class="modal-body">
<div class="form-group">
<label for="key"></label>
<input disabled type="text" value='{{incomeSelected}}' name="key" class="form-control" formControlName="key" />
</div>
<div class="form-group">
<label for="import">Add amount</label>
<input type="number" name="import" class="form-control" formControlName="import" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Add</button>
</div>
......more code
Then on my component for that html :
some imports...
export class UserSheetBalanceComponent implements OnInit {
importNgForm: FormGroup;
constructor(
private service: Service,
private amountBuilder:FormBuilder,
) {
this.importNgForm = this.amountBuilder.group({
key:new FormControl(),
import:new FormControl(),
});
}
addUniqueImport() {
this.service.addingImport(this.importNgForm.value as addImport)
}
and then finally on my service component i just try to pass the parameters the form brings :
addingImport(dataToPass: addImport) {
const path = this.docCreator
.collection('users')
.doc(this.userdata.uid)
.collection('Incomings')
.doc(this.userdata.uid);=====>Path reaching the doc to update
Until declaring the path to access that document where the field is.But then when try to refer the name of the field i want to update through the form (dataToPass.key) ,and the import for this field im doing reference to (dataToPass.import) the error appears.
path.update({dataToPass.key:dataToPass.import}) ====>doesn't work
}
The problem is in the key, let say i instead of accessing my form(dataToPass) i write directly the name of the field to update(some name),i does work
path.update({some name:dataToPass.import}) ======>does work
}
so im wondering how could i access that field key without precisely write it , but dynamically, in order to update my import once the field on query matchs
Thanks in advance!!!!
if you have reference to object in firebase
const path = this.docCreator
.collection('users')
.doc(this.userdata.uid)
.collection('Incomings')
.doc(this.userdata.uid);
you can crete empty object and use key name from dataToPass.key to set property
let foo: any = {};
foo[`${dataToPass.key}`] = dataToPass.import;
path.update(foo);
I have name and values ,calling from the ts side and want to validate it.the values are'nt getting from the event and it's been comming such as (on) .
<form [formGroup]="reactiveForm">
<div *ngFor="let item of checkBoxValueList">
<input
type="radio"
formControlName="yourChoice"
[value]="item"
(change)="event($event)"
>
{{item}}
</div>
</form>
<pre>
{{reactiveForm.value | json}}
</pre>
checkBoxValueList = [
'Reading',
'Watching',
'Traveling',
'Cooking'
];
reactiveForm: FormGroup = new FormGroup({
yourChoice: new FormControl()
});
constructor() {}
edit(eve) {
console.log(eve);
console.log("target", eve.target.value);
}
You can pass directly your value instead of event.
Try it.
(change)="edit(item)".
Hope this helps.
I guess that you only need the selected value on change event and validate it. So here you can access the selected value of radio button by our reactiveForm variable.
event(eve) {
console.log(eve);
// console.log("target", eve.target.value);
console.log(this.reactiveForm.value['yourChoice']); //this will give your selected value
}
Hope this helps!!
I have to build a form like so.
This shows upfront. i.e. without user interaction.
When a user pressed the + button it creates the same kind of UI again like so.
You can see that the user can add any number of same UI parts again and again. Can you tell me how to do this?
I went through number of articles. But it has the whole form created once. i.e. not like my use case. Any direction for this?
Example: https://jasonwatmore.com/post/2019/06/25/angular-8-dynamic-reactive-forms-example
and https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/
Based on the form you need to reproduce :
Create a function returning a formGroup to populate your array
createSchoolPath(): FormGroup {
return this.fb.group({
schoolName: '',
level: '',
topics: [],
inProgress: true
})
}
You then need to create your parent form which include your form array (and populate it once for the first form you want to show upfront) :
constructor( private fb: formBuilder) {}
form: FormGroup = this.fb.group({
schoolPaths: this.fb.array([this.createSchoolPaths])
});
Finally as you want to let users add more sections, you need a way to populate your array :
/* component */
addSchoolPath(): void {
const paths = this.form.get('schoolPaths') as FormArray;
// use the first function to push a new formGroup
paths.push(this.createSchoolPath());
}
<!-- html -->
<button (click)="addSchoolPath()">+</button>
To display it :
<form [formGroup]="form">
<div formArrayName="schoolPaths" *ngFor="let path of form.get('schoolPaths').controls; let i = index">
<div [formGroupName]="i">
<!-- place your inputs here -->
</div>
</div>
</form>
<button (click)="addSchoolPath()">+</button>
To complementary Gérôme's answer, you can also use directly a formArray
createGroup()
{
return new FormGroup({
school:new FormControl(),
level:new FormControl(),
topic:new FormControl(),
progress:new FormControl()
})
}
At first you has a formArray
formArray:FormArray=new FormArray([this.createGroup()])
The add button is simple
add()
{
this.formArray.push(this.createGroup())
}
And the .html
<form [formGroup]="formArray">
<div *ngFor="let group of formArray.controls" [formGroup]="group">
<input formControlName="school">
<input formControlName="level">
<input formControlName="topic">
<input formControlName="progress">
</div>
</form>
See that, in general we use a formArray inside a formGroup, But in case you only want a FormArray, you can loop over the formArray.controls directly
I have a code:
document.getElementById('loginInput').value = '123';
But while compiling the code I receive following error:
Property value does not exist on type HTMLElement.
I have declared a var: value: string;.
How can I avoid this error?
Thank you.
if you want to set value than you can do the same in some function on click or on some event fire.
also you can get value using ViewChild using local variable like this
<input type='text' id='loginInput' #abc/>
and get value like this
this.abc.nativeElement.value
here is working example
Update
okay got it , you have to use ngAfterViewInit method of angualr2 for the same like this
ngAfterViewInit(){
document.getElementById('loginInput').value = '123344565';
}
ngAfterViewInit will not throw any error because it will render after template loading
(<HTMLInputElement>document.getElementById('loginInput')).value = '123';
Angular cannot take HTML elements directly thereby you need to specify the element type by binding the above generic to it.
UPDATE::
This can also be done using ViewChild with #localvariable as shown here, as mentioned in here
<textarea #someVar id="tasknote"
name="tasknote"
[(ngModel)]="taskNote"
placeholder="{{ notePlaceholder }}"
style="background-color: pink"
(blur)="updateNote() ; noteEditMode = false " (click)="noteEditMode = false"> {{ todo.note }}
</textarea>
import {ElementRef,Renderer2} from '#angular/core';
#ViewChild('someVar') el:ElementRef;
constructor(private rd: Renderer2) {}
ngAfterViewInit() {
console.log(this.rd);
this.el.nativeElement.focus(); //<<<=====same as oldest way
}
A different approach, i.e: You could just do it 'the Angular way' and use ngModel and skip document.getElementById('loginInput').value = '123'; altogether. Instead:
<input type="text" [(ngModel)]="username"/>
<input type="text" [(ngModel)]="password"/>
and in your component you give these values:
username: 'whatever'
password: 'whatever'
this will preset the username and password upon navigating to page.
Complate Angular Way ( Set/Get value by Id ):
// In Html tag
<button (click) ="setValue()">Set Value</button>
<input type="text" #userNameId />
// In component .ts File
export class testUserClass {
#ViewChild('userNameId') userNameId: ElementRef;
ngAfterViewInit(){
console.log(this.userNameId.nativeElement.value );
}
setValue(){
this.userNameId.nativeElement.value = "Sample user Name";
}
}