I have a task to develop a system that can update each employee's image in angular
form: FormGroup;
departments: FormGroup;
public fb: FormBuilder as instantiated in the constructor
this.form = this.fb.group({
companyName: [''],
companyAddress: [''],
department: this.fb.array([]),
})
createDepartment() {
this.departments = this.fb.group({
id: [''];
code: [''],
employees: this.fb.array([]),
});
}
get departmentControls() {
return (this.form.get(department) as FormArray).controls
}
export interface Department {
id: number;
code: string;
employees: Employee[];
}
export interface Employee {
id: number;
path: string;
name: string;
}
Here, imagePath is an s3 bucket URL
<div
formArrayName="department"
*ngFor="
let department of departmentControls;
let departmentIndex = index
"
>
<div [formGroupName]="departmentIndex">
// departmentIndex form group
<div
formArrayName="employee"
*ngFor="
let employee of department.get('employees').controls;
let employeeIndex = index
"
>
<div [formGroupName]="employeeIndex">
<input
type="file"
hidden
(change)="onUpdateHandler($event, department.value.id, employee.value.path)"
[id]="'department-image-input-' + employeeIndex"
/>
<img [src]="`${imagePath}${employee.value.path}?tr=w-200,h-200`" class="image-wrapper__img" />
<button
nbButton
status="warning"
(click)="onImageChangeClick(departmentIndex, employeeIndex)"
>
Change
</button>
</div>
</div>
</div>
</div>
I need to open a particular index file to update the image path
below code is tried but excepted image is not changed
onImageChangeClick(departmentIndex: number, employeeIndex: number) {
// below code needs to change
const element = document.getElementById(`department-image-input-${employeeIndex}`) as HTMLInputElement;
element.click();
}
**for example, when 0 departmentIndex's department has 1 employee and 1 departmentIndex's
the department has 1 employee when I click departmentIndex 1 employee's change button to
change image it change 0 departmentIndex's 0 employeeIndex employee image**
if departmentIndex 1 has 3 employees and departmentIndex 2 has 5 employees;
When I click departmentIndex 2 's employeeIndex 2 's image it changes departmentIndex=1 's employeeIndex 2's image
onUpdateHandler(event: Event, departmentId: number, path: string) {
const target = event.target as HTMLInputElement;
const file = target.files[0];
const data = new FormData();
data.append('file', file, file.name);
data.append('path', path);
// code for sent data to backend
}
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
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!
Is there a way to create a reactive form basing on an existing data model with all the validation magic. In the example below author passes whole new object into a formbuilder but what I want to achieve is elegant way to tell formbuilder what field is required or needs some other validation.
https://malcoded.com/posts/angular-fundamentals-reactive-forms
export class PersonalData {
email: string = '';
mobile: string = '';
country: string = '';
}
...
createFormGroupWithBuilderAndModel(formBuilder: FormBuilder) {
return formBuilder.group({
personalData: formBuilder.group(new PersonalData()),
requestType: '',
text: ''
});
}
I just want to skip the process of assigning a FormControl for each field in the model.
#EDIT
After some research and little hint from #xrobert35 I wanted to try and used https://www.npmjs.com/package/#rxweb/reactive-form-validators
They could be "many" way to do what you want to do, but by just extending your actual solution : Your personnal data could look like :
export class PersonalData {
email: string = ['', Validators.required];
mobile: string = ['', Validators.required, Validators.maxLength(10)];
country: string = '';
}
If you need domain base validation (for reusable purpose) you can use rxweb validations.
export class PersonalData {
#required()
email: string;
#required()
mobile: string;
#greaterThan({ fieldName: 'number2' })
number1: number;
#prop()
number2: number;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
formGroup: FormGroup;
constructor( private validation: RxFormBuilder) {
}
ngOnInit() {
let person: PersonalData = new PersonalData();
this.formGroup = this.validation.formGroup(person);
}
}
If I understand you just want to add validators to your field.
https://angular.io/guide/form-validation
I can't be more precise to giving you the official documentation.
ngOnInit(): void {
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'power': new FormControl(this.hero.power, Validators.required)
});
}
In this exemple the fields name and power are required and of course the syntaxe could differ but the flow is the same.
Does it helps you ?
#EDIT
There is the same post for your use case:
https://stackoverflow.com/a/47576916/7133262
Unable to patch values to FormArray resultList.
Anybody can please explain me, what i'm missing?
TS File:
import { Component, OnInit } from '#angular/core';
import { Student } from '../student';
import { FormGroup, FormControl, Validators, FormArray } from '#angular/forms';
#Component({
selector: 'app-container',
templateUrl: './container.component.html',
styleUrls: ['./container.component.css']
})
export class ContainerComponent implements OnInit {
studList: Student[] = [];
myform: FormGroup = new FormGroup({
firstName: new FormControl('', [Validators.required, Validators.minLength(4)]),
lastName: new FormControl(),
gender: new FormControl('male'),
dob: new FormControl(),
qualification: new FormControl(),
resultList: new FormArray([])
});
onSave() {
let stud: Student = new Student();
stud.firstName = this.myform.get('firstName').value;
stud.lastName = this.myform.get('lastName').value;
stud.gender = this.myform.get('gender').value;
stud.dob = this.myform.get('dob').value;
stud.qualification = this.myform.get('qualification').value;
this.studList.push(stud);
this.myform.controls.resultList.patchValue(this.studList);
console.log(JSON.stringify(this.studList));
}
ngOnInit() {
}
}
Model:
export class Student {
public firstName: String;
public lastName: string;
public gender: string;
public dob: string;
public qualification: string;
}
HTML:
<div class="container">
<h3>Striped Rows</h3>
<table class="table table-striped" formArrayName="resultList">
<thead>
<tr>
<th>Firstname</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of myform.controls.resultList.controls; let i = index" [formGroupName]="i">
<td><p formControlName="firstName"></p></td>
</tr>
</tbody>
</table>
</div>
this.studList JSON:
[
{
"firstName":"santosh",
"lastName":"jadi",
"gender":"male",
"dob":"2018-03-31T18:30:00.000Z",
"qualification":"BE"
},
{
"firstName":"santosh",
"lastName":"jadi",
"gender":"male",
"dob":"2018-03-31T18:30:00.000Z",
"qualification":"BE"
}
]
By your question you want to add new Student to resultList.
First of all, you need to know FormArray is an array of AbstractControl.
You can add to array only type of AbstractControl not other.
To simplify task prefer to use FormBuilder:
constructor(private fb: FormBuilder) {}
createForm() {
this.myform = this.fb.group({
firstName: ['', [Validators.required, Validators.minLength(4)]],
lastName: [],
gender: ['male'],
dob: [],
qualification: [],
resultList: new FormArray([])
});
}
As you can see before filling resultList FormArray, it's mapped to FormGroup:
onSave() {
let stud: Student = new Student();
stud.firstName = 'Hello';
stud.lastName = 'World';
stud.qualification = 'SD';
this.studList.push(stud);
let studFg = this.fb.group({
firstName: [stud.firstName, [Validators.required, Validators.minLength(4)]],
lastName: [stud.lastName],
gender: [stud.gender],
dob: [stud.dob],
qualification: [stud.qualification],
})
let formArray = this.myform.controls['resultList'] as FormArray;
formArray.push(studFg);
console.log(formArray.value)
}
FormBuilder - Creates an AbstractControl from a user-specified
configuration.
It is essentially syntactic sugar that shortens the new FormGroup(),
new FormControl(), and new FormArray() boilerplate that can build up
in larger forms.
Also, in html formControlName bound to <p> element, It's not an input and you can't bind to not form elements like div/p/span...:
<tbody>
<tr *ngFor="let item of myform.controls.resultList.controls; let i = index" [formGroupName]="i">
<td><p formControlName="firstName"></p></td> <==== Wrong element
</tr>
</tbody>
So, I think you just want to show added students in table. Then iterate over studList and show it's value in table:
<tbody>
<tr *ngFor="let item of studList; let i = index" [formGroupName]=i>
<td>
<p> {{item.firstName}} </p>
</td>
</tr>
</tbody>
Patching value
Take care when patching array. Because patchValue of FormArray patches values by the index:
patchValue(value: any[], options: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
value.forEach((newValue: any, index: number) => {
if (this.at(index)) {
this.at(index).patchValue(newValue, {onlySelf: true, emitEvent: options.emitEvent});
}
});
this.updateValueAndValidity(options);
}
So, code below it patches the element at index=0: First indexed value of this.myform.controls['resultList'] as FormArray will be replaced with:
let stud1 = new Student();
stud1.firstName = 'FirstName';
stud1.lastName = 'LastName';
stud1.qualification = 'FFF';
formArray.patchValue([stud1]);
Your case doesn't work because patchValue requires some controls in array. In your case there is no controls in array. Look source code.
StackBlitz Demo
First try with this steps and make sure are you on correct way
Because in your scenario you are patching the object to formArray ,so you have to parse that object first & check once have you imported ReactiveFormsModule in your app.module.ts.
you have to co like this , code is taken from angular.io, you need to do setcontrol that will do or go though link there is code for the same it makes uses of Address array
this.setAddresses(this.hero.addresses);
setAddresses(addresses: Address[]) {
const addressFGs = addresses.map(address => this.fb.group(address));
const addressFormArray = this.fb.array(addressFGs);
this.heroForm.setControl('secretLairs', addressFormArray);
}
I'd prefer using FormBuilder to create form.
export class ComponentName implements OnInit {
form: FormGroup;
constructor(private fb: FormBuilder){}
ngOnInit() {
this.buildForm();
}
buildForm() {
this.form = this.fb.group({
firstName: '',
lastName: '',
...
resultList: this.fb.array([])
});
}
}
I believe the studlist will be obtained through API call as an observable rather than an static array. Let's assume, we data as follow.
resultModel =
{
firstName: "John",
lastName: "Doe",
....
resultList: [
{
prop1: value1,
prop2: value2,
prop3: value3
},
{
prop1: value1,
prop2: value2,
prop3: value3
}
...
]
}
Once the data is available, we can patch the values as follow:
patchForm(): void {
this.form.patchValue({
firstName: this.model.firstName,
lastName: this.model.lastName,
...
});
// Provided the FormControlName and Object Property are same
// All the FormControls can be patched using JS spread operator as
this.form.patchValue({
...this.model
});
// The FormArray can be patched right here, I prefer to do in a separate method
this.patchResultList();
}
// this method patches FormArray
patchResultList() {
let control = this.form.get('resultList') as FormArray;
// Following is also correct
// let control = <FormArray>this.form.controls['resultList'];
this.resultModel.resultList.forEach(x=>{
control.push(this.fb.group({
prop1: x.prop1,
prop2: x.prop2,
prop3: x.prop3,
}));
});
}
Array does not contain patchValue method. You have to iterate over controls and patchValue
each of them separately.
I am using the formgroup in formarray as:
this.formGroup = new FormGroup({
clientCode: new FormControl('', []),
clientName: new FormControl('', [Validators.required, Validators.pattern(/^[a-zA-Z0-9 _-]{0,50}$/)]),
type: new FormControl('', [Validators.required]),
description: new FormControl('', []),
industry: new FormControl('', []),
website: new FormControl('', [Validators.required, Validators.pattern(this.settings.regex.website)]),
businessEmail: new FormControl('', [Validators.pattern(this.settings.regex.email)]),
clients: this._formBuilder.array([this._formBuilder.group({
contactPerson: new FormControl('', [Validators.required]),
contactTitle: new FormControl('', [Validators.required]),
phoneNumber: new FormControl('', [Validators.required, Validators.pattern(this.settings.regex.phone)]),
emailId: new FormControl('', [Validators.required, Validators.pattern(this.settings.regex.email)]),
timeZone: new FormControl('', [Validators.required, Validators.pattern(this.settings.zipCode), Validators.minLength(5), Validators.maxLength(12)])
})])
})
For patch value I am using below method as:
let control = _this.formGroup.get('clients') as FormArray
clients.forEach(ele => {
control.push(_this._formBuilder.group({
contactPerson: new FormControl(ele.client_name, [Validators.required]),
contactTitle: new FormControl(ele.contact_title, [Validators.required]),
phoneNumber: new FormControl(ele.phone_number, [Validators.required, Validators.pattern(_this.settings.regex.phone)]),
emailId: new FormControl(ele.email_id, [Validators.required, Validators.pattern(_this.settings.regex.email)]),
timeZone: new FormControl(ele.timezone, [Validators.required, Validators.pattern(_this.settings.zipCode), Validators.minLength(5), Validators.maxLength(12)])
}))
});
Using this method we can validate the nested field as well.
Hope this may help.