#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" />