PatchForm not working after api call Angular 8 - javascript

i am using patch value to update my form but i am unable to do that, my view is not getting updated , though i have tried both approaches async/await and observable. Although i found a workaround by using set timeout but that is not a proper solution.
Thanks in advance.
Approach 1 using arrow function and async/await (promise)
constructor(
public storage: Storage,
public route: Router,
public navCtrl: NavController,
public toastController: ToastController,
public alertController: AlertController,
public loadingController: LoadingController,
public formBuilder: FormBuilder,
public dataInteractionService: DataInteractionService,
public router: ActivatedRoute
) {
super(storage, route, navCtrl, toastController, alertController, loadingController);
this.initialize_Sub_for_Param();
}
initialize_Sub_for_Param() {
this.router.queryParams.subscribe(params => {
debugger;
this.initialize_Form();
if (this.route.getCurrentNavigation().extras.state) {
debugger;
this.petDetails = this.route.getCurrentNavigation().extras.state.user;
console.log(this.petDetails, "petDetails")
this.get_Data_animal(this.petDetails);
}
});
}
ngOnInit() {}
initialize_Form() {
this.edit_animal_Form = this.formBuilder.group({
animal_image: ['', Validators.required],
animal_name: ['', Validators.required],
species_id: ['', Validators.required],
breed_id: ['', Validators.required],
date_of_birth: ['', Validators.required],
electronic_number: ['', Validators.required],
})
}
get_Data_animal = async (form_Value) => {
try {
let breed_Species_data = await this.dataInteractionService.get_Animal_Spcices().toPromise();
this.get_species = breed_Species_data['species'];
this.get_breeds = breed_Species_data['breeds'];
debugger;
//Workaround using settimeout but i know this is not a proper sollution//
setTimeout(() => {
this.edit_animal_Form.patchValue(form_Value);
console.log(this.edit_animal_Form.value);
}, 100);
} catch (error) {
console.log(error)
}
}
Approach 2 using observables
constructor(
public storage: Storage,
public route: Router,
public navCtrl: NavController,
public toastController: ToastController,
public alertController: AlertController,
public loadingController: LoadingController,
public formBuilder: FormBuilder,
public dataInteractionService: DataInteractionService,
public router: ActivatedRoute
) {
super(storage, route, navCtrl, toastController, alertController, loadingController);
this.initialize_Sub_for_Param();
}
initialize_Sub_for_Param() {
this.router.queryParams.subscribe(params => {
this.initialize_Form();
if (this.route.getCurrentNavigation().extras.state) {
this.petDetails = this.route.getCurrentNavigation().extras.state.user;
this.get_Data_animal(this.petDetails);
}
});
}
ngOnInit() {}
initialize_Form() {
this.edit_animal_Form = this.formBuilder.group({
animal_image: ['', Validators.required],
animal_name: ['', Validators.required],
species_id: ['', Validators.required],
breed_id: ['', Validators.required],
date_of_birth: ['', Validators.required],
electronic_number: ['', Validators.required],
})
}
get_Data_animal = async (form_Value) => {
try {
this.dataInteractionService.get_Animal_Spcices().subscribe(res => {
this.get_species = breed_Species_data['species'];
this.get_breeds = breed_Species_data['breeds'];
this.edit_animal_Form.patchValue(form_Value);
});
} catch (error) {
console.log(error)
}
}

I have no idea what is going on in your code. But here is a fair example which i use to patch values in my form. I can give you the basic concept. Patch your form right after when you receive the values. For example:
getOrderById(id) {
this._order.getOrderWithId(id).subscribe((res: any) => {
this.orderForm.patchValue(res);
});
}
In this case you dont have to use setTimeout or any other async await stuff.

Related

Problem with storing data in Firebase (_isScalar: false)

I have created a form that stores the course type data in the Firebase. When I try to save a new element it creates this: https://i.stack.imgur.com/jXO1z.jpg.
Here's my code in create-course Component:
form: FormGroup = this.fb.group({
name: [''],
code: [''],
ects: [''],
lekt: ['']
})
constructor(private fb: FormBuilder, private dataStorageService : DataStorageService) { }
ngOnInit(): void {}
onSaveData(){
this.dataStorageService.storeCourses();
}
Here's the code in the service component :
storeCourses(){
const courses = this.courseService.getCourses();
this.http.put(
'https://projekti-ff52b-default-rtdb.firebaseio.com/courses.json',
courses)
.subscribe(response => {
console.log(response);});
}
getCourses() {
return of(this.courses); }
private courses: Course[] = [];
And Course [] is an exported interface from course model.ts

Add/ remove custom validators to form array in angular 8

Hi I am trying to set/ take away custom validators for different elements in a form array that can change around, so far what I have tried to do is create a switch statement and loop through all of the input types that are set so I could set the validation rule as well as send a message to the user if the rule isn't met. The problem I am having is the form is initialized before the form data is set.
So my question is how can I loop through the array and set the validation rules. If someone could let me know if im along the right tracks with using a switch statement but have code in the wrong place or if there is a different and better approach it would be most helpful thank you
export class ReactiveComponent implements OnInit {
public form: FormGroup;
public fieldList: any;
types: Array<any>;
formData: any;
Param: string;
setData: any;
formLabelNames: any;
get contactFormGroup() {
return this.form.get('inputs') as FormArray;
}
constructor(
private route: ActivatedRoute,
private fb: FormBuilder,
private api: FormService,
private notifiy: NotificationService,
private auth: AuthService,
private router: Router) { }
ngOnInit() {
this.form = this.fb.group({
name: ['', Validators.compose([Validators.required])],
organization: ['', Validators.compose([Validators.required])],
inputs: this.fb.array([this.createForm()])
});
this.route.paramMap.subscribe(params => {
this.Param = params.get('id');
this.getForm(this.Param);
});
// set fieldslist to this field
this.fieldList = this.form.get('inputs') as FormArray;
}
// formgroup
createForm(): FormGroup {
return this.fb.group({
type: ['', Validators.compose([Validators.required])],
name: ['', Validators.compose([Validators.required])],
value: ['', this.validators()]
});
}
getForm(id) {
this.api.getForm(id).subscribe(
(data: any) => this.setForm(data)
);
}
getFieldsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
getContactsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
setForm(data) {
const d = data.results;
this.setData = d;
this.formLabelNames = d[0].fields;
this.form.patchValue({
name: [d[0].form_name],
organization: [d[0].org],
});
this.form.setControl('inputs', this.setExistingFields(d[0].fields));
}
setExistingFields(fields: any): FormArray {
const formArray = new FormArray([]);
this.fieldList = formArray;
fields.forEach(f => {
formArray.push(this.fb.group({
name: f.name,
type: f.type,
value: f.value
}));
});
return formArray;
}
/* This is where I have tried to create a switch statement but I get a undefined error because the setform function is being called after this one */
validators() {
this.formLabelNames.type.forEach((field: any) => {
switch (field.type) {
case 'email':
}
});
}
submit() {
if (this.form.valid) {
const formId = this.Param;
const local = this.auth.decodePayload();
const userId = local.sub;
this.router.navigateByUrl('/dashboard');
this.api.sendForm(this.form.value, formId, userId).subscribe();
this.form.reset();
} else {
this.notifiy.showFailure('Form is not valid', 'Error');
}
}
}
You have several problems, as far as I can judge:
You don't initialize neither the formControls nor the formArray correctly inside of you formGroup. It should rather look like this:
this.form = this.fb.group({
name: new FormControl( "", {validators: [Validators.required]}),
organization: new FormControl( "", {validators: [Validators.required]}),
inputs: this.fb.array([new FormControl(), new FormControl()]),
});
Besides: What is the point of using a formArray when it consists of only one formGroup?
Off course you can set the validators for any abstractControl be it a formControl or a formGroup. For the array it would look like something like this:
this.formArray.controls.map((ctrl) => {
ctrl.setValidators([Validators.required, Validators.email]);
});
It doesn't matter where in the component class you put your method. Though it surely matters when that method is invoked!

I want to take the data into the form of already signup members?

I have a form,and when i submit the form the form data is submitting to the angular firebase database.so if we sigin again i want to show the data into the form which has been submitted before.
my .ts file is below
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder,Validators } from "#angular/forms";
import { AF } from "app/providers/af";
import { FirebseService } from "app/firebse.service";
import { Router } from "#angular/router";
import { AngularFireDatabase, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2/database';
#Component({
selector: 'app-candidate-registration',
templateUrl: './candidate-registration.component.html',
styleUrls: ['./candidate-registration.component.css']
})
export class CandidateRegistrationComponent implements OnInit {
itemsAsObjects = [{value: 0, display: 'Angular'}, {value: 1, display: 'React'}];
complexForm : FormGroup;
contents:any;
constructor(fb: FormBuilder,
private firebaseService:FirebseService,
private router: Router,
private db: AngularFireDatabase) {
var Userid=localStorage.getItem('user');
console.log(Userid);
let content= this.db.object('/candidates_list/'+Userid)
content.subscribe(data => {
console.log(data);
this.contents=data;
console.log(this.contents);
})
if(this.contents){
this.complexForm = fb.group({
// To add a validator, we must first convert the string value into an array. The first item in the array is the default value if any, then the next item in the array is the validator. Here we are adding a required validator meaning that the firstName attribute must have a value in it.
'firstName' : ["pranav", Validators.required],
// We can use more than one validator per field. If we want to use more than one validator we have to wrap our array of validators with a Validators.compose function. Here we are using a required, minimum length and maximum length validator.
'lastName': ["kk", Validators.compose([Validators.required, Validators.minLength(1), Validators.maxLength(10)])],
'gender' : [null, Validators.required],
'email' : [null, Validators.required],
'contact_number':[null, Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(10)])],
'experience':[null, Validators.required],
'skills':[null, Validators.required],
'notice_period':[null, Validators.required],
})
}else
{
this.complexForm = fb.group({
// To add a validator, we must first convert the string value into an array. The first item in the array is the default value if any, then the next item in the array is the validator. Here we are adding a required validator meaning that the firstName attribute must have a value in it.
'firstName' : [null, Validators.required],
// We can use more than one validator per field. If we want to use more than one validator we have to wrap our array of validators with a Validators.compose function. Here we are using a required, minimum length and maximum length validator.
'lastName': [null, Validators.compose([Validators.required, Validators.minLength(1), Validators.maxLength(10)])],
'gender' : [null, Validators.required],
'email' : [null, Validators.required],
'contact_number':[null, Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(10)])],
'experience':[null, Validators.required],
'skills':[null, Validators.required],
'notice_period':[null, Validators.required],
})
}
}
ngOnInit() {
}
submitForm(user){
console.log(user);
this.firebaseService.addtolist(user);
this.complexForm .reset();
this.router.navigate(['/reg-complete']);
}
}
this below code(part of .ts file) is working fine.i am getting the signed in users data into console.But i dont know how to use it into if condition, to set the data of the already signed in users into the registration form.
anyone please help me?Thanks in advance.
var Userid=localStorage.getItem('user');
console.log(Userid);
let content= this.db.object('/candidates_list/'+Userid)
content.subscribe(data => {
console.log(data);
this.contents=data;
console.log(this.contents);
})
Well, the main problem here is that when you check this statement:
if (this.contents) { ... }
It will always go to else.
Why? Because the async operation where you define this.contents isn't resolved yet, and as you didn't defined an initial value for this.contents, it's undefined.
For a deep explanation I'd suggest you to check this question.
That said, I want to suggest another approach to solve your problem:
Instead of having the if/else with full duplicate code, let's break it into a method, like this:
initForm(data: Object = {}) {
this.complexForm = fb.group({
firstName: [data.firstName, Validators.required],
lastName: [data.lastName, [Validators.required, Validators.minLength(1), Validators.maxLength(10)],
gender: [data.gender, Validators.required],
email: [data.email, Validators.required],
contact_number: [data.contact_number, [Validators.required, Validators.minLength(10), Validators.maxLength(10)],
experience: [data.experience, Validators.required],
skills: [data.skills, Validators.required],
notice_period: [data.notice_period, Validators.required]
});
}
Explanation:
In the signature method I'm initializing data as a clean object, so if nothing or undefined | null is passed to the function it'll automatically become a clean object like this {}.
It's useful because it prevents the undefined.<property> errors.
Full code:
constructor(private fb: FormBuilder,
private router: Router,
private db: AngularFireDatabase,
private firebaseService:FirebseService) {
const userId = localStorage.getItem('user');
if (userId) {
this.db.object(`/candidates_list/${userId}`)
.subscribe(data => {
this.contents = data; // This variable isn't used anymore.
this.initForm(data);
});
} else {
this.initForm();
}
}
initForm(data: Object = {}) {
this.complexForm = fb.group({
firstName: [data.firstName, Validators.required],
lastName: [data.lastName, [Validators.required, Validators.minLength(1), Validators.maxLength(10)],
gender: [data.gender, Validators.required],
email: [data.email, Validators.required],
contact_number: [data.contact_number, [Validators.required, Validators.minLength(10), Validators.maxLength(10)],
experience: [data.experience, Validators.required],
skills: [data.skills, Validators.required],
notice_period: [data.notice_period, Validators.required]
});
}
Notes:
1 - The Validators.compose isn't needed, you can just pass an array, or if it's a single validator, the validator itself, in both parameters (2nd and 3rd).
2 - I'd suggest you to move this code from constructor to ngOnInit.
3 - You can face some error in template since the complexForm won't be filled until the async operation is resolved (when you have a user stored, of couse).
To be more specific it'll throw this error:
ORIGINAL EXCEPTION: formGroup expects a FormGroup instance. Please
pass one in.
So, you'll have to use some flag to handle the form in template.
Supposing that you've something like this in your template:
<form [formGroup]="complexForm">
...
</form>
You can solve solve this doing the following:
Component:
import 'rxjs/add/operator/finally';
...
isLoading: boolean = true;
...
this.db.object(`/candidates_list/${userId}`)
.finally(() => this.isLoading = false)
.subscribe(data => {
this.contents = data;
this.initForm(data);
});
Template:
<ng-container *ngIf="!isLoading">
<form [formGroup]="complexForm">
...
</form>
</ng-container>

Dynamic Validation with nested form

I'm trying to create a nested form with dynamic validation. There's the parent component (finalizaNegociacao) which hold the nested forms (dadosTitular). On the nested form I have a checkbox (handleType) that changes the validation of some fields, like cpf. What should be happening is: When I click on the checkbox cpfis no longer a required field. But even if I click on it it still required. Can someone help me?
Here's the code:
FinalizaNegociacao (parent component), the main logic here is in the recebeValidators :
export class FinalizaNegociacaoComponent implements OnInit, OnChanges {
public dados:dadosAcordo;
public formDadosBancarios: FormGroup;
public submitted: boolean;
public events: any[] = [];
public servError:any;
public servSuccess:any;
cpf_REGEXP = /^\d+$/;
constructor(private _fb: FormBuilder, private finalizaAcordo:FinalizaNegociacaoService) {}
ngOnInit() {
this.formDadosBancarios = this._fb.group({
id: [''],
dados_titular: this._fb.array([
this.initTitular()
])
})
}
initTitular(){
return this._fb.group({
titular: ['', [<any>Validators.required, <any>Validators.minLength(3)]],
cnpj: [''],
cpf: ['', <any>Validators.required],
data_nasc: [''],
agencia: ['', <any>Validators.required ],
banco: ['', <any>Validators.required],
conta: ['', <any>Validators.required],
tipo: ['', <any>Validators.required],
pessoa_juridica: ['']
})
}
addTitular(){
const control = <FormArray> this.formDadosBancarios.controls['dados_titular'];
control.push(this.initTitular());
}
recebeValidators(e:any){
let array = <FormArray> this.formDadosBancarios.get('dados_titular');
let cpf = array.at(0).get("cpf");
let cpfCtrl = this.formDadosBancarios.get(['dados_titular',0,'cpf']);
let cnpj = array.at(0).get('cnpj');
let data_nasc = array.at(0).get('data_nasc');
cpfCtrl = e;
console.log(cpfCtrl);
}
DadosTitular, (Child component), the main logic here is the handleType:
export class TitularComponent {
// we will pass in address from App component
#Input('group')
public titularForm: FormGroup;
#Output() modelChanged = new EventEmitter();
public formDadosBancarios: FormGroup;
cpf_REGEXP = /^\d+$/;
constructor(private _fb: FormBuilder, private finalizaAcordo:FinalizaNegociacaoService) {}
ngOnInit() {
this.formDadosBancarios = this._fb.group({
id: [''],
dados_titular: this._fb.array([
this.initTitular()
])
})
}
initTitular(){
return this._fb.group({
titular: ['', [<any>Validators.required, <any>Validators.minLength(3)]],
cnpj: [''],
cpf: ['', <any>Validators.required],
data_nasc: [''],
agencia: ['', <any>Validators.required ],
banco: ['', <any>Validators.required],
conta: ['', <any>Validators.required],
tipo: ['', <any>Validators.required],
pessoa_juridica: ['']
})
}
handleType(isJuridica: boolean, i:number, e:any): void {
let array = <FormArray> this.formDadosBancarios.get('dados_titular');
let cpf = array.at(0).get("cpf");
let cnpj = array.at(0).get('cnpj');
let data_nasc = array.at(0).get('data_nasc');
const cpfCtrl = this.formDadosBancarios.get(['dados_titular',0,'cpf']);
const cnpjCtrl = this.formDadosBancarios.get(['dados_titular',0, 'cnpj']);
const data_nascCtrl = this.formDadosBancarios.get(['dados_titular',0,'data_nasc']);
const reqValidators: ValidatorFn[] = [Validators.required, Validators.pattern(this.cpf_REGEXP)];
if (isJuridica) {
cpfCtrl.setValidators(null);
data_nascCtrl.setValidators(null);
cnpjCtrl.setValidators(reqValidators);
this.modelChanged.emit(cpfCtrl);
}else{
cpfCtrl.setValidators(reqValidators);
data_nascCtrl.setValidators(reqValidators)
cnpjCtrl.setValidators(null);
this.modelChanged.emit(cpfCtrl);
}
cpfCtrl.updateValueAndValidity();
data_nascCtrl.updateValueAndValidity();
cnpjCtrl.updateValueAndValidity();
}
}
and there's the html:
<div formArrayName="dados_titular">
<div *ngFor="let dados_titular of formDadosBancarios.controls.dados_titular.controls; let i=index">
<div [formGroupName]="i">
<titular (modelChanged)="recebeValidators($event)" [group]="formDadosBancarios.controls.dados_titular.controls[i]"></titular>
</div>
</div>
</div>

Custom Validation with parameter

I am using Ionic 2 with Angular 2 beta 11.
I have a custom validator that works perfectly. However, I now need to add another custom validation method, but can't seem to work out how to pass a parameter to it.
As you can see below, I have a custom validation function:
ValidationService.userNameExists(control, this.employeeService)
'control' does not exist
How do I pass the control (FormControl) to the userNameExists function? Or, I need to pass the field value (value of username).
Thanks
register.ts
constructor(private nav: NavController, private builder: FormBuilder, employeeService: EmployeeService) {
this.employeeService = employeeService;
this.registerForm = builder.group({
'username': ['', [Validators.required, Validators.minLength(5), Validators.maxLength(55), ValidationService.userNameValidator, ValidationService.userNameExists(control, this.employeeService)]],
'password1': ['', [Validators.required, Validators.minLength(5), Validators.maxLength(45), ValidationService.passwordValidator]],
'password2': ['', [Validators.required, Validators.minLength(5), Validators.maxLength(45), ValidationService.passwordValidator]]
}, { 'validator': ValidationService.matchPassword('password1', 'password2') }
);
}
'control' does not exist
ValidationService.ts
public static userNameExists(control: FormControl, employeeService: EmployeeService): any {
return (control: FormControl) => {
let userNameInput = control.value;
console.log('userNameExists: '+control.value);
let promise: Promise<EmployeeModel> = employeeService.getEmployeByUserName(userNameInput);
promise.then((employeeModel: EmployeeModel) => {
if (employeeModel.userName === userNameInput) {
return { userNameExists: true };
} else {
return null;
}
});
}
}
Remember that when you are instantiating FormControl/FormGroup, you are passing the validation function in, not calling it. Change your username declaration as following:
'username': ['',
[Validators.required,
Validators.minLength(5),
Validators.maxLength(55),
ValidationService.userNameValidator,
(control) => ValidationService.userNameExists(control, this.employeeService)]]

Categories