Pass values to child component Angular 7

Robert Nish picture Robert Nish · Jan 16, 2019 · Viewed 12.6k times · Source

I went though this, and understand that after declaring a data-bound input property, Angular should automatically update the input value. In the component I created, it doesn't seems like that.

When I click on a item on the grid on the parent it shows me the details properly. When I click on another item after that, it doesn't update the child component. I have put a console.log to monitor the selected record. It keeps changing according to the user selection.

Can you please have a look and help me to understand where the issue is?

lists.component.html [Parent]

<div class="container-fluid">
  <div class="row mt-2 mb-2">
    <div class="col-8">
      <app-list-item-add *ngIf="showAddnewScreen" (notifyParentOnUpdate)='onAddItem($event)'></app-list-item-add>
      <app-list-item-view *ngIf="showViewScreen" [studentObject]="selectedstudent" (notifyParentOnUpdate)='onViewItem($event)'></app-list-item-view>
    </div>
  </div>
</div>

lists.component.ts [Parent]

import { Component, OnInit, ViewChild } from '@angular/core';
import { studentsService, student } from '../services/students.service';
import { Router, ActivatedRoute } from '@angular/router';
import { GridComponent, ToolbarItems, SortEventArgs, RowSelectEventArgs, SelectionSettingsModel } from '@syncfusion/ej2-ng-grids';
import { ClickEventArgs } from '@syncfusion/ej2-ng-navigations';
import * as moment from 'moment';
import { Internationalization } from '@syncfusion/ej2-base';

@Component({
  selector: 'app-lists',
  templateUrl: './lists.component.html',
  styleUrls: ['./lists.component.scss']
})
export class ListsComponent implements OnInit {

  constructor(public router: Router, private route: ActivatedRoute, private studentsService: studentsService) { }
  selectedstudent: student = null;
  students: student[] = new Array<student>();
  intl: Internationalization = new Internationalization();
  showAddnewScreen = false;
  showViewScreen = false;

  // Syncfusion GRID settings for the students grid.
  // Documentation: https://ej2.syncfusion.com/16.1.32/angular/documentation/grid/getting-started.html
  studentsGridId = 'studentsGrid';
  @ViewChild('studentsGrid')
  studentsGrid: GridComponent;
  toolbar: ToolbarItems[];

  // https://ej2.syncfusion.com/16.1.32/angular/documentation/grid/api-filterSettings.html
  studentsFilteringSettings = {
  };

  // https://ej2.syncfusion.com/16.1.32/angular/documentation/grid/api-pageSettings.html
  studentsPageSettings = {
    currentPage: 1,
    enableQueryString: true,
    pageSizes: [10, 25, 50, 100],
    pageSize: 10
  };

  // https://ej2.syncfusion.com/16.1.32/angular/documentation/grid/api-selectionSettings.html
  studentsSelectionOptions: SelectionSettingsModel;

  studentsToolbarClick(args: ClickEventArgs) {
    // handles multiple grids on the page by prepending the Grid ID to the _eventname
    // E.g.
    // if (args.item.id == studentsGrid_excelexport)....
    if (args.item.id === (this.studentsGridId + '_excelexport')) {
      this.studentsGrid.excelExport();
    }
    if (args.item.id === (this.studentsGridId + '_pdfexport')) {
      this.studentsGrid.pdfExport();
    }
  }

  studentsRowSelected(args: RowSelectEventArgs) {
    const selectedrowindex: number[] = this.studentsGrid.getSelectedRowIndexes();  // Get the selected row indexes.
    console.log(selectedrowindex);
    const selectedRecords: student[] = this.studentsGrid.getSelectedRecords() as student[];  // Get the selected records.
    const selectedRecord = selectedRecords[0];
    if (selectedRecord) {
    }
  }

  gridActionHandler(args: SortEventArgs) {
    console.log(args.requestType + ' ' + args.type);
  }

  ngOnInit() {
    this.toolbar = ['Print', 'Search', 'ExcelExport', 'PdfExport'];
    this.studentsSelectionOptions = {
      type: 'Single',
      mode: 'Row'
    };

    this.studentsService.getstudents(1000).subscribe((students) => {
      this.students = students;
      this.students.sort(this.sortBystudentNumber);
      this.studentsGrid.dataSource = this.students;
    });

    // Listen for changes to list items
    this.studentsService.studentAdded$.subscribe(student => {
      // convert the students date strings into dates
      student.createdOn = moment(student.createdOn).toDate();
      student.modifiedOn = moment(student.modifiedOn).toDate();
      // Add the new student to the list
      this.students.push(student);
      // resort the grid data
      this.students.sort(this.sortBystudentNumber);
      // refresh the grid
      this.studentsGrid.refresh();
    });
    this.studentsService.studentChanged$.subscribe(student => {
      // convert the students date strings into dates
      student.createdOn = moment(student.createdOn).toDate();
      student.modifiedOn = moment(student.modifiedOn).toDate();
      // Update the student in the list.
      this.students.splice(this.students.findIndex(s => s.id === student.id), 1, student);
      // resort the grid data
      this.students.sort(this.sortBystudentNumber);
      // refresh the grid
      this.studentsGrid.refresh();
    });
    this.studentsService.studentDeleted$.subscribe(id => {
      // Remove the student from the list
      this.students.splice(this.students.findIndex(s => s.id === id), 1);
      // resort the grid data
      this.students.sort(this.sortBystudentNumber);
      // refresh the grid
      this.studentsGrid.refresh();
    });

  }

  addNew() {
    this.showAddnewScreen = true;
    this.showViewScreen = false;
  }

  viewstudent(data: student) {
    console.log(data);
    this.selectedstudent = data;
    this.showViewScreen = true;
    this.showAddnewScreen = false;
  }

  onAddItem(student: student): void {
    this.showAddnewScreen = false;
  }

  onViewItem(command: string) {
    this.showViewScreen = false;

    if (command === 'cancel') {
    } else if (command === 'save') {
    } else if (command === 'delete') {
    }
  }

  sortBystudentNumber = (s1, s2) => s1.studentNumber - s2.studentNumber;
}

list-item-view.component.html [Child]

<div class="row">
  <div class="col-12">
    <section class="studentDetails">
      <app-section-title heading="student Details" level="4"></app-section-title>

      <form #studentForm="ngForm" class="pt-2">
        <div class="row">
          <div class="col-10">

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>student number</span>
                </div>
                <div class="col-6">
                  <input type="text" class="form-control" aria-label="student number"
                         [(ngModel)]="student.studentNumber" name="student Number" />
                </div>
              </div>
            </div>

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>student name</span>
                </div>
                <div class="col-6">
                  <input type="text" class="form-control" aria-label="student name"
                         [(ngModel)]="student.studentName" name="student Name" />
                </div>
              </div>
            </div>

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>student description</span>
                </div>
                <div class="col-6">
                  <input type="text" class="form-control" aria-label="student description"
                         [(ngModel)]="student.description" name="Description" />
                </div>
              </div>
            </div>

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>Created date</span>
                </div>
                <div class="col-6">   
                  <ejs-datepicker placeholder='Enter date' format="dd-MM-yyyyy" aria-label="Created date" [readonly]="true"
                                  [(ngModel)]="student.createdOn" name="Created On"></ejs-datepicker>
                  <label class="col-8 col-lg-9 col-form-label">({{student.createdOn | timeago}})</label>
                </div>
              </div>
            </div>

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>Created by</span>
                </div>
                <div class="col-6">
                  <input type="text" class="form-control" aria-label="Created by" [readonly]="true"
                         [(ngModel)]="student.createdBy" name="Created By" />
                </div>
              </div>
            </div>

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>Modified date</span>
                </div>
                <div class="col-6">
                  <ejs-datepicker placeholder='Enter date' format="dd-MM-yyyyy" aria-label="Modified date"
                                  [(ngModel)]="student.modifiedOn" name="Modified On"></ejs-datepicker>
                  <label *ngIf="student.modifiedOn" class="col-8 col-lg-9 col-form-label">({{student.modifiedOn | timeago}})</label>
                </div>
              </div>
            </div>

            <div class="form-group">
              <div class="row">
                <div class="col-4">
                  <span>Modified by</span>
                </div>
                <div class="col-6">
                  <input type="text" class="form-control" aria-label="Modified by"
                         [(ngModel)]="student.modifiedBy" name="Modified By" />
                </div>
              </div>
            </div>

          </div>
        </div>
      </form>
    </section>
  </div>
</div>

list-item-view.component.ts [Child]

import { Component, OnInit, ViewChild, EventEmitter, Output, Input } from '@angular/core';
import { studentsService, student } from '../services/students.service';
import { ActivatedRoute } from '@angular/router';
import { DatePicker } from '@syncfusion/ej2-calendars';
import * as moment from 'moment';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-list-item-view',
  templateUrl: './list-item-view.component.html',
  styleUrls: ['./list-item-view.component.scss']
})

export class ListItemViewComponent implements OnInit {
  @Output() notifyParentOnUpdate: EventEmitter<any> = new EventEmitter<any>();
  @Input() studentObject: student;

  studentNumber: number;

  constructor(private route: ActivatedRoute, private studentsService: studentsService) { }

  @ViewChild(NgForm) studentForm: NgForm;

  public student = new student();

  ngOnInit() {
    this.student = this.studentObject;
  }

  save() {
    this.studentsService.updatestudent(this.student).subscribe(newstudent => {
      this.notifyParentOnUpdate.emit('save');
    });

  }

  delete() {
    this.studentsService.deletestudent(this.student.id).subscribe(newstudent => {
      this.notifyParentOnUpdate.emit('delete');
    });
  }

  editOnBlur() {
    this.notifyParentOnUpdate.emit('editOnBlur');
  }

  cancel() {
    this.notifyParentOnUpdate.emit('cancel');
  }
}

Answer

Kushan Randima picture Kushan Randima · Jan 16, 2019

When you write a child component in Angular 7, that updates its content whenever input changes. So, you can add all necessary computations to the ngOnChanges lifecycle hook. ngOnInit called once only.

Please add the following code to list.item-view.component.ts

  ngOnChanges() {
    this.school = this.schoolObject;
  }

Good Luck!