1

In my angular application, I have a form Form Page in which employee data can be entered and I have a grid page Grid Page where the entered data will be displayed. When I submit the form, the details should get added in the grid page.

Initially, I'll fetch the employees' data in grid from json file.

The issue I face here is, even after submitting the form, the new data is not getting added to the grid. Grid is always loading from json file. The pushed data from the form should get added to the grid which is not happening.

In my service, I have the following code:

import { Employee } from '../employee.model';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs/Subject';
//import 'rxjs/add/operator/map';

@Injectable()
export class BasicServices {

  public newaddFormDetails = new Subject<Employee[]>();
  private _url: string = "/assets/data/employeesData.json";
  public employees: Employee[];

  // private employees: Employee [] =[ {"title":"John", "body":"TL"},
  // {"title":"Kennedy", "body":"SSE"}];
  // getEmployees() {
  //   return this.employees;
  // }

  getEmployees() {
    return this.httpClient.get<Employee[]>(this._url)
    .subscribe(
      (employees:Employee[]) => {
        this.employees = employees;
        this.newaddFormDetails.next(this.employees);
      });
  }

addToGrid(emp: Employee) {
this.employees.push(emp);
this.newaddFormDetails.next(this.employees);
}   

  constructor(private httpClient: HttpClient) {
  }

}

The grid component file is below:

import { Component, OnInit, OnDestroy} from '@angular/core';
import { BasicServices } from '../services/basic.services';
import { Subscription } from 'rxjs/Subscription';
import { Employee } from '../employee.model';

@Component({
  selector: 'app-grid',
  templateUrl: './grid.component.html',
  styleUrls: ['./grid.component.less']
})

export class GridComponent implements OnInit, OnDestroy{

  subscription: Subscription;
  employees: Employee[];

  constructor( private _basicService: BasicServices) {
  }
  ngOnInit() {
    this.subscription = this._basicService.newaddFormDetails.subscribe(  
      (employees: Employee[]) => {
        this.employees = employees;
       } 
    );
    this._basicService.getEmployees();

  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

In my grid component html file, I simply have a table with the code below:


<table class='table table-striped table-condensed' *ngIf='employees && employees.length'>
<thead>
    <tr>        
        <th style="min-width: 40px;">Title</th>
        <th style="min-width: 40px;">Body</th>
    </tr>
</thead>
<tbody>
    <tr *ngFor="let emp of employees">
    <td>{{emp.title| uppercase}}</td>
    <td>{{emp.body}}</td>
    <td>
    </tr>
</tbody>
</table>

Form component file:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { BasicServices } from '../services/basic.services';
import { Router } from '@angular/router';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.less']
})
export class FormComponent implements OnInit {
  private form;
    constructor(private _basicService: BasicServices,
      private formBuilder: FormBuilder,
      private router: Router
      ) { }


  ngOnInit() {
   this._basicService.getEmployees();
   this.form = this.formBuilder.group({
      title: [null, Validators.required],
      body: [null, Validators.required],
    });

  }

  onSubmit(formValue):void {
    this._basicService.addToGrid(formValue);
    this.form.reset();
    this.router.navigate(['/grid']);
  }
}

Form component template



<div class="container">
    <h1> Add Records </h1>
    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
         <div class="form-group">
            <label> Title : </label> 
                <input type="text" class="form-control" required formControlName="title" placeholder="FirstName" [(ngModel)] ="title"> 
            </div>
            <div class="form-group">
                <label> Body : </label> 
                <input type="text" class="form-control" required formControlName="body" placeholder="LastName">
            </div>
            <input type="submit" class="btn btn-info" name="Submit" [disabled]="!form.valid">


    </form>

</div>


My code is here in Stackblitz

John
  • 13
  • 3
  • 1
    Where is `addToGrid` called? Please show the (relevant) code from the form. Also, you do mean that it doesn't get updated right after submitting said form, right? Cause if you mean after refreshing your app, that's perfectly normal (from what you showed). – Jeto Feb 23 '19 at 16:21
  • Please add the form code. – Dream_Cap Feb 23 '19 at 16:26
  • Added the form code – John Feb 24 '19 at 06:14

1 Answers1

1

For this basic case, it may be much easier to do without the Subject.

Service

private _url: string = "/assets/data/employeesData.json";
public employees: Employee[];

constructor(private httpClient: HttpClient) {
   this.getEmployees();
}

addToGrid(emp: Employee) { 
   this.employees.push(emp); 
}

getEmployees() {
  return this.httpClient.get<Employee[]>(this._url)
    .subscribe(
      (employees:Employee[]) => this.employees = employees);

}

  • Remove the Subject and each place it is used.
  • When adding the employee, simply add it to the array.

Component

export class GridComponent implements OnInit{

  get employees(): Employee[]{
     return this._basicService.employees;
  }

  constructor( private _basicService: BasicServices) {
  }

  ngOnInit() {

  }

}
  • Use a getter to ensure that the component always gets the most recent version of the array. Angular's built-in change detection will cause the template to rebind, which calls this getter, any time the array changes.

NOTE: Not syntax checked.

Also, if you do want to keep with using a Subject, you may need a BehaviorSubject. If you are routing between the form component and the grid component, using a Subject only sends the value to any current subscribers. So if you subscribe after routing, the value will not be emitted. Using a BehaviorSubject retains the last emitted value and will then provide that value upon subscription.

I just did a stackblitz of this here: https://stackblitz.com/edit/angular-no-subject-deborahk

I also added to my stackblitz the same example but with a working BehaviorSubject in place of the Subject.

It's not pretty, but it displays two links to route to the version with the simple getter ("no subject") and the one with the BehaviorSubject ("with subject"). Notice that if you change the BehaviorSubject back to Subject it will stop working because it won't receive the notifications.

UPDATE

Based on new information, here are a few notes/suggestions:

1) You can't update a JSON file. So if you are reading data from a JSON file (which it looks like you are), you can't update data back into that file. See this to confirm: Is it possible to write data to a locally json file with nothing but angular?

2) When you navigate to a component, it is reinitialized and its ngOnInit method is executed.

3) In each of your pages, in your ngOnInit you are calling this._basicService.getEmployees();. As far as I can see, this method is re-reading your JSON file, so loading the original data and overwriting any changes you made and handled with your observables.

4) If you look at the stackblitz I provided, you'll notice that in this working example, the service holds the array of data so that the components don't need to keep re-getting it and losing the updates.

Hope this helps.

DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • Hi DeborahK, After removing the subject, the newly added form data is adding to the grid. But when I switch it to form page and click on the grid page again, The recently added one doesn't exist. It is freshly loading again from json. – John Feb 24 '19 at 06:27
  • It worked with BehaviorSubject too (Data is adding to Grid). But the problem here is, after adding the form details, when I'm navigating to Grid page, the new data is added but when I click on Form page (I didn't add this time) and then Grid page again, the previously added data doesn't exist. I have added the images in my post for your reference – John Feb 24 '19 at 06:55
  • Thanks. It was helpful. In your 2nd point, since the component is re-initialized, I'm losing my added data. But the data is getting preserved when I copied the values from json and assigned to employees directly (Refer to the commented code in service in my question). The component is re-initialized here too but I'm getting the preserved data. How is it possible? Also, in 4th point, I referred to your code. Since, it was on the same page, the data is preserved. But in my case, I want to pass my data from Form page to Grid page. Is there any way? – John Feb 24 '19 at 07:45
  • Define the array of data in the *service*. Then both components can read/write to that same array of data. The service shown at the top of my answer does this. – DeborahK Feb 24 '19 at 08:00
  • When I defined in json, it's not working. But when I defined the array of data, it is working. How do I make it work with json? – John Feb 24 '19 at 08:24
  • DeborahK, please have a look at the stackblitz I provided. https://stackblitz.com/edit/angular-z2mbng?file=src%2Fapp%2Fservices%2Fbasic.services.ts – John Feb 24 '19 at 08:40
  • If I understand correctly what you are trying to do, you can find an updated stackblitz here: https://stackblitz.com/edit/angular-qmwiiq I started with the link you provided, forked it, and made a few changes as defined in my answer. – DeborahK Feb 24 '19 at 09:03
  • Thank you so much, DeborahK. It is working now as expected. – John Feb 24 '19 at 13:45