Передача значения от родительского компонента к дочернему не работает

#angular #parent-child

Вопрос:

У меня есть раскрывающийся список родительского компонента, и когда пользователь выбирает значение, я хочу передать это значение дочернему элементу, который будет передан в другом вызове метода в базу данных. Проблема в том, что моя ценность в ребенке всегда неопределенна.

родительский HTML

 <div>
   <div style="margin-bottom: 20px;">
    <kendo-label [for]="reportList" text="Report:" style="margin-right: 10px;margin-bottom: 20px;"></kendo-label>
    <kendo-dropdownlist #reportList
                        [data]="reportListData"
                        [defaultItem]=defaultSelection
                        textField="reportName"
                        valueField="url"
                        (selectionChange)="reportDropDownChange($event)"
                        width="550px"></kendo-dropdownlist>
  </div>
  <div>
    <kendo-label [for]="usersubscriptionadmin" text="Distributor Admin:" *ngIf="isAdmin()" style="margin-right: 10px;"></kendo-label>
    <kendo-combobox #usersubscriptionadmin
                      [data]="userSubscriptionAdministrators"
                      [textField]="'userName'" 
                      [valueField]="'userId'" 
                      *ngIf="isAdmin()"
                      (selectionChange)="userSubscriptionAdminChange($event)"
                      style="width: 500px;"></kendo-combobox>
  </div>
     <div style="margin-top: 20px;">
       <router-outlet></router-outlet>
     </div>
</div>
 

родитель.ts

 import { Component, OnInit } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';

import {Notifications} from './../core/notifications/notifications';
import {UserService} from './../user/user.service';

import {ReportListModel} from './../user/models/report-list-model';
import { UserSubscriptionAdministratorModel } from '../user/models/user_subscription_administrator_model';

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

  public reportListData: ReportListModel[] = [{reportName: "Subscription Invoice Summary", url: "reports/invoice-report"},
                                              {reportName: "Subscription Invoice Details", url: "reports/invoice-detail"},
                                              {reportName: "Admin Report", url: "reports/admin-report"}];

  public defaultSelection: ReportListModel = {reportName: 'Select a report', url:""};
  public userSubscriptionAdministrators: UserSubscriptionAdministratorModel[] = [];
  public href: any;
  public reportURL: string='';
  public selectedAdminUserId: number;
 

  constructor(private notifications: Notifications, private router: Router, private userService: UserService) {
    router.events.subscribe((event) =>{event instanceof NavigationEnd? this.href = event: null}); //get the page URL if there is a refresh        
   }

  ngOnInit() {
    //if the array is greater than one, then we know the user refreshed the page and already selected a report
    //so now set the report drop down
    this.reportURL = this.href.url.split('/')[2];
    //now set the default report selection to be the page URL
    if(this.reportURL != undefined)
    {
      console.log('setting default selection');
      this.defaultSelection = this.reportListData.find(x => x.url.includes(this.reportURL));
    }

    this.userService.GetUserSubscriptionAdministrators().subscribe(response => {
      response.forEach(a => {
        let admin = new UserSubscriptionAdministratorModel();
        admin.subscriptionId = a.subscriptionId;
        admin.userId = a.userId;
        admin.userName = a.firstName   ' '   a.lastName;
        if (a.customerName !== null) {
          admin.userName  = ' - '   a.customerName;
        }
        if (a.ctsid !== 0) {
          admin.userName  = ' ('   a.ctsid   ')';
        }
        this.userSubscriptionAdministrators.push(admin);
      });
    })
  }

  public reportDropDownChange($event)
  {
      if($event.url != ""){
        this.router.navigate([$event.url]);
      }
  }

  public isAdmin() {
    if (sessionStorage.getItem("UserType") == 'Administrator') {
      return true;
    } else {
      return false;
    }
  }

  public userSubscriptionAdminChange($event){
    console.log('Parent value change '   $event.userId);
    this.selectedAdminUserId = $event.userId;
  }

}
 

Дочерний HTML

 form [formGroup]="form" class="k-form-horizontal">
<div style="display: inline-block;">
    <div style="display: inline-block; width: 175px;">
        <kendo-label [for]="fromDate" text="From:" style="padding-right: 6px;"></kendo-label> 
        <kendo-datepicker
            calendarType="classic"
            formControlName="fromDate"
            format="MM/yyyy"
            placeholder="MM/YYYY"
            #fromDate
            style="width: 125px;">
        </kendo-datepicker>
    </div>
    <div style="display: inline-block; width: 175px;">
        <kendo-label [for]="toDate" text="To:" style="padding-right: 6px;"></kendo-label>  
        <kendo-datepicker
            calendarType="classic"
            formControlName="toDate"
            format="MM/yyyy"
            placeholder="MM/YYYY"
            #toDate
            style="width: 125px;">
        </kendo-datepicker>
    </div>
    <div style="display: inline-block; width: 200px;">
        <kendo-label [for]="txtInvoiceNumber" text="Invoice Number:" style="padding-right: 6px;"></kendo-label>
        <kendo-textbox #txtInvoiceNumber formControlName="txtInvoiceNumber" style="width: 200px;"></kendo-textbox>
    </div>

    <div style="display: inline-block; width: 150px;padding-left: 6px;">
        <button type="submit" class="k-button k-primary" (click)="RunReport()" >Search</button>
    </div>
</div>

<div style="display: inline-block; padding-top: 5px;">
<kendo-grid
        [data]="gridData"
        [resizable]="true" 
        filterable="menu" 
        [sortable]="true" 
        [filter]="state.filter"
        [skip]="state.skip" 
        [sort]="state.sort" 
        [pageSize]="state.take"
        [pageable]="true"
        [height]="375" >
    <ng-template kendoGridToolbarTemplate>
        <button type="button" kendoGridExcelCommand icon="file-excel">Export to Excel</button>
    </ng-template>
    <kendo-grid-column field="invoiceDate" title="Date" width="100"></kendo-grid-column>
    <kendo-grid-column field="poNumber" title="PO Number" width="100"></kendo-grid-column>
    <kendo-grid-column field="invoiceNumber" title="Invoice Number" width="100"></kendo-grid-column>
    <kendo-grid-column field="invoiceAmount" title="Amount" width="100" format="{0:c}"></kendo-grid-column>
    <kendo-grid-excel fileName="InvoiceSummaryReport.xlsx" [fetchData]="excelExportData">
        <kendo-excelexport-column field="invoiceDate" title="Date"></kendo-excelexport-column>
        <kendo-excelexport-column field="ponumber" title="PO Number"></kendo-excelexport-column>
        <kendo-excelexport-column field="invoiceNumber" title="Invoice Number"></kendo-excelexport-column>
        <kendo-excelexport-column field="invoiceAmount" title="Amount"></kendo-excelexport-column>
    </kendo-grid-excel>
</kendo-grid>
</div>
</form>
 

childcomponent.ts

 import { Component, Input, OnInit, } from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
import {Router} from '@angular/router';
import {DatePipe} from '@angular/common';

import {addYears} from '@progress/kendo-date-math';
import {State, process} from '@progress/kendo-data-query'
import { ExcelExportData } from '@progress/kendo-angular-excel-export'

import {InvoiceSummaryReportModel} from '../models/invoice-summary-model';
import {ReportService} from '../report.service';
import { InvoiceSummarySearchModel } from '../models/invoice-summary-search-model';
import { Notifications } from '../../core/notifications/notifications';

@Component({
  selector: 'app-invoice-report',
  templateUrl: './invoice-report.component.html',
  styleUrls: ['./invoice-report.component.css']
})
export class InvoiceReportComponent implements OnInit {

  @Input('selectedAdminUserId') public parentSelectedAdminUserId: any;

public form: FormGroup;
public exportState: State;
public gridData: InvoiceSummaryReportModel[];
public exportData: any;
public tempDate:any;
public userId: number = Number(sessionStorage.getItem('UserId'));
public invoiceSearch: InvoiceSummarySearchModel;
public href: any;
public state: State = {
  take: 20
}

  constructor(private reportService: ReportService, 
              private router: Router, 
              private datepipe: DatePipe, 
              private notifications: Notifications) { 
    this.form = new FormGroup ({
      fromDate: new FormControl(addYears(new Date(), -1)),
      toDate: new FormControl(new Date()),
      txtInvoiceNumber: new FormControl()
    });

    this.excelExportData = this.excelExportData.bind(this);
  }

  ngOnInit() { }

  public RunReport(){
    this.invoiceSearch = new InvoiceSummarySearchModel();
    console.log('parent user id '   this.parentSelectedAdminUserId);

      if(this.isAdmin())
      {
        this.invoiceSearch.userId =  this.parentSelectedAdminUserId;
      }
      else
      {
        this.invoiceSearch.userId = this.userId;
      }

      this.tempDate = this.datepipe.transform(this.form.controls.fromDate.value, 'MM/dd/yyyy').split("/");

      this.invoiceSearch.fromDateMonth =  parseInt(this.tempDate[0]);
      this.invoiceSearch.fromDateYear = parseInt(this.tempDate[2]);

      this.tempDate = this.datepipe.transform(this.form.controls.toDate.value, "MM/dd/yyyy").split("/");
      this.invoiceSearch.toDateMonth = parseInt(this.tempDate[0]);
      this.invoiceSearch.toDateYear = parseInt(this.tempDate[2]);
      this.invoiceSearch.invoiceNumber = this.form.controls.txtInvoiceNumber.value;

      this.reportService.GetInvoiceSummaryReport(this.invoiceSearch).subscribe(response => {
        if(response.message == "Success"){
            this.gridData = response.resu<
        }
        else{
          this.notifications.error(response.message);
        }
      });
  }

  public excelExportData(): ExcelExportData {
    //set a filter for export if the user has filtered the result set.
    this.exportState = {
      filter: this.state.filter
    };
    //now set all the data to be exported.  Using gridData will only return the first page because the array is spliced to into 20 records.
    this.exportData = process<InvoiceSummaryReportModel>(this.gridData, this.exportState)
    const result: ExcelExportData = {
      data: this.exportData.data
    };
    return resu<
  }

  isAdmin() {
    if (sessionStorage.getItem("UserType") == 'Administrator') {
      return true;
    } else {
      return false;
    }
  }
}
 

Как установить компонент parentSelectedAdminUserId в дочернем компоненте?

Комментарии:

1. Вам также нужно показать родительский вид.

2. Итак, где вы создаете экземпляр дочернего компонента, мы предполагаем, что мы увидим подобное <invoice-report-component [parentSelectedAdminUserId]="selectedAdminUserId"></invoice-report-component> , и, возможно, вы просто пропускаете обновление, потому что в вашем примере нет использования ngOnChanges или подобного, которого мы нигде не видим? Может быть, пример стекблица для воспроизведения? Поскольку он находится, похоже, что значение не задано в конструкторе или onInit , как undefined можно было бы ожидать, в качестве начального значения.

3. @ataravati — Я обновил свой пост полным кодом как для родителя, так и для ребенка.

Ответ №1:

Вы не показываете, как RunReport() запускается функция в дочернем компоненте. Но в целом я мог видеть @Input , что доступ к переменной не осуществляется динамически. Вы могли бы сделать одно из следующих действий

Вариант 1: @Input в качестве сеттера

Дочерний компонент

 export class InvoiceReportComponent implements OnInit {
    @Input() set parentSelectedAdminUserId(value: any) {
        console.log('parent user id:', value);
        // do something else
    }
}
 

Вариант 2: ngOnChanges крючок

Дочерний компонент

 import { Component, OnInit, OnChanges, SimpleChanges, Input } from '@angular/core';

export class InvoiceReportComponent implements OnInit, OnChanges {
    @Input() parentSelectedAdminUserId: any

    ngOnChanges(changes: SimpleChanges) {
        if (
            !!changes amp;amp; 
            !!changes.parentSelectedAdminUserId amp;amp; 
            !!changes.parentSelectedAdminUserId.currentValue
        ) {
            console.log('parent user id:', this.parentSelectedAdminUserId);
            // do something else
        }
    }
}
 

Комментарии:

1. Да, нравятся эти варианты, и 1 за подробный ответ.

Ответ №2:

Допустим, селектор вашего InvoiceReportComponent есть invoice-report . Затем в html родительского компонента вы должны передать это свойство следующим образом:

 <invoice-report [parentSelectedAdminUserId]="selectedAdminUserId" />