Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Same form for creating and editing data Angular4

Here is a form with two way-binded input fields. The goal was to create the same form to edit and create data. That was accomplished, but I am pretty sure that there could be a better way to do this (like using AbstractControl features). I also miss one fix here - if clickEdit() being clicked need to update form values with that object which user wants to edit. Thanks for any help and especially explanations about AbstractControl and NgModel..

<div>
<form (ngSubmit)="clicked ? onEditSubmit($event) : onRegisterSubmit($event)" [formGroup] = "form">
  <div class="form-group">
    <label>Full Name</label>
    <input type="text" [(ngModel)]="fullname" formControlName="fullname" class="form-control" >

  </div>
  <div class="form-group">
    <label>Username</label>
    <input type="text" [(ngModel)]="username" formControlName="username" class="form-control" >
  </div>
  <div class="form-group">
    <label>Email</label>
    <input type="text" [(ngModel)]="email" formControlName="email" class="form-control" >
  </div>
  <div class="form-group">
    <label>Password</label>
    <input type="password" [(ngModel)]="password" formControlName="password" class="form-control">
  </div>
  <button type="submit" class="btn btn-primary" [disabled]="!form.valid"> Submit </button>
</form>
</div>

<br>
<br>

<table  border="2" class="table table-striped">
<tr>
  <th>Full Name</th>
  <th>Username</th>
  <th>Email</th>
  <th>Password</th>
  <th>Delete</th>
  <th>Edit</th>
</tr>
<div > </div>
<tr *ngFor="let user of userDetails; index as i">
  <td>{{user.fullname}}</td>
  <td>{{user.username}}</td>
  <td>{{user.email}}</td>
  <td>{{user.password}}</td>
  <td><button (click)="userDelete()">X</button></td>
  <td><button (click)="clickEdit(i)">Edit</button></td>
</tr>
</table>

And

import { Component } from '@angular/core';
import { initializeApp, database } from 'firebase';
import { FormControl, FormGroup, Validators } from '@angular/forms';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {

  fullname : string;
  username : string;
  email : string;
  password : string;

  clicked = false;

  userDetails:Array<object>;

  form;

  ngOnInit() {
   this.userDetails=[];
    this.form = new FormGroup({
      fullname : new FormControl("", Validators.required),
      username : new FormControl("", Validators.required),
      email : new FormControl("", Validators.required),
      password : new FormControl("", Validators.required)
    });
  }

  onRegisterSubmit(e){
    let user = {
      fullname : this.fullname ,
      username : this.username,
      email : this.email ,
      password : this.password
    }
     this.userDetails.push(user);
     this.clearInputFields(e);
   }

   editIndex = null;

  clickEdit(i){
    this.clicked = !this.clicked;
    this.editIndex = i;
  }

  onEditSubmit(e) {
    let editUser = {
      fullname : this.fullname ,
      username : this.username,
      email : this.email ,
      password : this.password
    }
    this.userDetails[this.editIndex] = editUser;
    this.clearInputFields(e);  
    this.clicked = !this.clicked;  
  }

  clearInputFields(e){
   let all = e.target.querySelectorAll('input');
    Object.keys(all).forEach(key => {
        console.log(all[key].value = '');   
    });    
  }
}
like image 373
Julius Dzidzevičius Avatar asked Jul 28 '17 07:07

Julius Dzidzevičius


People also ask

What are template and reactive forms?

Template Driven Forms are based only on template directives, while Reactive forms are defined programmatically at the level of the component class. Reactive Forms are a better default choice for new applications, as they are more powerful and easier to use.

What is reactive forms in angular?

Reactive forms include a set of validator functions for common use cases. These functions receive a control to validate against and return an error object or a null value based on the validation check. Import the Validators class from the @angular/forms package.


2 Answers

Dummy Forms

Creating Form that are reusable.

Folder Structure

enter image description here

Usage

User Form Component

<form (ngSubmit)="submit(form)" [formGroup] = "form">
  <input type="text" formControlName="username" >
  <button type="submit"[disabled]="!form.valid"> Submit</button>
  <button type="button" (click)="cancel()"></button>
</form>
@Input() formData;
@Output() onSubmit = new EventEmitter();
@Output() onCancel = new EventEmitter();

ngOnInit(){
    this.form = this.fb.group({
      username : ['', Validators.required]
    });

    // Edit Mode will have formData.
    if(this.formData){
        this.form.patchValue(this.formData)
    }
}

submit(){
   if(this.form.valid){
       this.onSubmit.emit(this.form.value)
   }
}

cancel(){
  this.onCancel.emit();
}

Add User Component

<user-form                  
    (onSubmit)="addUser($event)"  
    (onCancel)="cancel()">
</user-form>
addUser(data){
   // Call Http Service to save data
}

cancel(){
   // navigate to different route or something else
}

Edit User Component

<user-form 
    [formData]="formData"                 
    (onSubmit)="updateUser($event)"  
    (onCancel)="cancel()">
</user-form>
ngOnInit(){
   this.formData = //fetch data from server 
}

updateUser(data){
   // Call Http Service to save data
}

cancel(){
   // navigate to different route or something else
}

Pros

  1. Single Component for single use
  2. Can reuse the dummy forms anywhere throughout the application.
  3. Easy to maintain and extend the form

Cons

  1. For each page you will have 3 components ( add, edit, form).
like image 159
Renil Babu Avatar answered Sep 20 '22 13:09

Renil Babu


I would make quite a few changes to your form. ngModel is totally redundant here as you are using a reactive form. Utilize that instead and remove all ngModel's. The object you are getting from the form, is matching your user, so what you can do, is just push that value as is to the array.

So your template should look something like this (shortened, as the rest of code):

<form (ngSubmit)="onRegisterSubmit(form)" [formGroup] = "form">
  <input type="text" formControlName="username" class="form-control" >
  <input type="submit" class="btn btn-primary" value="Submit" [disabled]="!form.valid">
</form>

In the build of the form I have in this case used a hidden field, that is also excluded from the form object as it is disabled. This is a helper for us, so we can differentiate if this is a new user, or if we are editing a user. The value holds the index of the user from your array. So if that value exists, we know to update the object in the array, if the value does not exist, let's push the user to the array.

This could be solved by numerous ways, but above is one option.

this.form = this.fb.group({
  index: [{value: null, disabled:true}]
  username : ['', Validators.required],
  email : ['', Validators.required],
});

So according to the above, we can modify the onRegisterSubmit to look like the following:

onRegisterSubmit(form) {
  // since field is disabled, we need to use 'getRawValue'
  let index = form.getRawValue().index
  if(index != null) {
    this.userDetails[index] = form.value
  } else {
    this.userDetails.push(form.value)      
  }
  this.form.reset() // reset form to empty
}

When we want to edit a user, we pass the index and the user in template

<tr *ngFor="let user of userDetails; let i = index">
  <td>{{user.username}}</td>
  <td>{{user.email}}</td>
  <td><button (click)="userEdit(user, i)">Edit</button></td>
</tr>

And then we use setValue (or patchValue) to enter the fields with the existing values:

userEdit(user, i) {
  this.form.setValue({
    index: i,
    username: user.username,
    email: user.email
  })
}

That should do it! So now we can see how much we could simplify your code and get rid of some unnecessary things! :)

like image 20
AT82 Avatar answered Sep 21 '22 13:09

AT82