Angular无法通过嵌套组件检测更改。

huangapple go评论82阅读模式
英文:

Angular does not detect changes through nested components

问题

I have found this problem that is like mine. Yet, my implementation does not work despite following the steps.

I have the following component structure:

  • Dashboard
    • ActionButton
    • Milestone
      • Table
    • SupplierSearch
      • Table

I have tried to pass array selectedRows from Table to Dashboard, and then to ActionButton using the CustomEvent and elRef.nativeElement.dispatchEvent. When I tried to console.log to see if it is passed to any parent components (Dashboard or Milestone) from Dashboard/Milestone/Table, the array simply does not get passed.

Please note that my code is super dirty right now because I have been trying to resolve this issue for almost a day and tried many ways to resolve it. Please focus on my way to implement this mentioned solution (CustomEvent elRef.nativeElement.dispatchEvent).

I really appreciate the Stackoverflow community for the shared knowledge, thus, please don't downgrade this post if my English is bad or something is inherently wrong with my problem.

Table

import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  Output,
  TemplateRef,
} from '@angular/core';
import { TableColumnHeader } from './models/table-column-header';

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.scss'],
})
export class TableComponent {
  @Input() rowData;
  @Input() headers: TableColumnHeader[] = [];
  @Input() columnTemplate: TemplateRef<any>;
  @Input() loading: boolean = false;

  @Output() selectedRowsEvent = new EventEmitter<any[]>();

  selectedRows = [];
  constructor(private elRef: ElementRef) {}

  onRowSelect(event) {
    this.selectedRows.push(event.data);
    this.selectedRowsEvent.emit(this.selectedRows);

    const evt = new CustomEvent('myCustomEvent', {
      bubbles: true,
      detail: event,
    });
    this.elRef.nativeElement.dispatchEvent(evt);

    console.log(this.selectedRows);
    console.log(event);
    console.log('from table onRowSelected ');
  }

  onRowUnselect(event) {
    this.selectedRows = this.selectedRows.filter(
      (x) => x.nvtAreaName !== event.data.nvtAreaName
    );
    this.selectedRowsEvent.emit(this.selectedRows);
    console.log(this.selectedRows);
    console.log('from table onRowUnselected ');
  }
}

Milestone

import {
  AfterViewInit,
  Component,
  Inject,
  Input,
  OnChanges,
  OnInit,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { TableColumnHeader } from '../../table/models/table-column-header';
import { NvtAreaDataSource } from '../../../services/nvt-area-data-source.service';
import { AreaProgramDataSource } from '../../../services/area-program-data-source.service';
import { MilestoneTableColumn } from '../../../models/business/milestone-table-column';
import { TableComponent } from '../../table/table.component';

@Component({
  selector: 'app-milestone-search',
  templateUrl: './milestone-search.component.html',
  styleUrls: ['./milestone-search.component.scss'],
})
export class MilestoneSearchComponent
  implements OnInit, OnChanges, AfterViewInit
{
  @Input() selectedGigaArea: string;
  milestoneData = [];
  loading: false;
  private tableComponent!: TableComponent;
  selectedRows = [];

  @Input() pSelectableRows = [];
  @ViewChild(TableComponent)
  columnHeaders: TableColumnHeader[] = [
    { value: 'ONKZ', id: 'onkz', sortable: true },
    { value: 'NVT', id: 'nvtAreaName', sortable: true },
    { value: 'STATUS', id: 'status', sortable: true },
    { value: 'ARVM_START', id: 'arvMStart', date: true },
    { value: 'EXP.ROUGH_START', id: 'expRoughStart', date: true },
    { value: 'EXP.ROUGH_END', id: 'expRoughEnd', date: true },
    { value: 'EXP.FINE_START', id: 'expFineStart', date: true },
    { value: 'EXP.FINE_END', id: 'expFineEnd', date: true },
    { value: 'RM_START', id: 'rmStart', date: true },
    { value: 'AFTER_INST_START', id: 'afterInstStart', date: true },
    { value: 'AFTER_INST_END', id: 'afterInstEnd', date: true },
  ];

  constructor(
    @Inject(NvtAreaDataSource) private nvtAreaDataSource,
    @Inject(AreaProgramDataSource) private areaProgramDataSource
  ) {}

  ngOnInit(): void {
    this.nvtAreaDataSource.connect().subscribe((nvtAreas) => {
      this.milestoneData = [...nvtAreas];
    });
    this.areaProgramDataSource.connect().subscribe((areaPrograms) => {
      this.milestoneData = this.mergeMilestonesData(
        this.milestoneData,
        areaPrograms
      );
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.date) {
      // this.pSelectableRows = changes.date.currentValue;
      this.selectedRows = changes.data.currentValue;
    }
    console.log('from milestone onChanges  ' + this.selectedRows.length);
  }
  ngAfterViewInit(): void {
    this.selectedRows = this.tableComponent.selectedRows;
    console.log('from ngAfterViewInit ');
  }

  onNotify(rowsEmitted: any[]): void {
    console.log('from milestone onNotify ');
    this.selectedRows = rowsEmitted;
  }

  mergeMilestonesData(nvtAreas, areaPrograms) {
    return nvtAreas.map((nvtArea) => {
      const areaProgram = areaPrograms.find(
        (x) => x.nvtAreaId === nvtArea.nvtAreaId
      );
      if (!areaProgram) return nvtArea;
      const { status, milestones } = areaProgram;
      let milestonesColumns = {};
      milestones.map((milestone) => {
        const milestonesColumn = Object.entries(
          new MilestoneTableColumn()
        ).reduce(
          (acc, [key, value]) =>
            value === milestone.milestoneType
              ? {
                  ...acc,
                  [key]: milestone.milestoneDate,
                }
              : acc,
          {}
        );
        milestonesColumns = { ...milestonesColumns, ...milestonesColumn };
      });
      return {
        ...nvtArea,
        ...milestonesColumns,
        status,
      };
    });
  }
}

Dashboard

import {
  AfterViewInit,
  Component,
  OnChanges,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { TableComponent } from '../../table/table.component';
import { AreabarComponent } from '../areabar/areabar.component';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent implements AfterViewInit {
  selectedRowsEvent($event: any) {
    throw new Error('Method not implemented.');
  }
  @ViewChild(TableComponent)
  selectedRows = [];
  private tableComponent!: TableComponent;

  public selectedGigaArea:

<details>
<summary>英文:</summary>

I have found this [problem][1] that is like mine. Yet, my implementation does not work despite following the steps.  
I have the following component structure:  
- Dashboard 
  - ActionButton 
  - Milestone
     - Table
  - SupplierSearch
     - Table

I have tried to pass array `selectedRows` from **Table** to **Dashboard**, and then to **ActionButton** using the `CustomEvent` and `elRef.nativeElement.dispatchEvent`. When I tried to console.log to see if it is passed to any parent components(**Dashboard** or **Milestone**) from **Dashboard/Milestone/Table**, the array simply does not get passed.  

Please note that my code is super dirty right now because I have been trying to resolve this issue for almost a day and tried many ways to resolve it. Please focus on the my way to  implement this mentioned [solution][2] (`CustomEvent` `elRef.nativeElement.dispatchEvent`)   


I really appreciate the Stackoverflow community for the shared knowledge, thus, please don&#39;t downgrade this post if my English is bad or something is inherently wrong with my problem.

Table
```typescript
import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  Output,
  TemplateRef,
} from &#39;@angular/core&#39;;
import { TableColumnHeader } from &#39;./models/table-column-header&#39;;

@Component({
  selector: &#39;app-table&#39;,
  templateUrl: &#39;./table.component.html&#39;,
  styleUrls: [&#39;./table.component.scss&#39;],
})
export class TableComponent {
  @Input() rowData;
  @Input() headers: TableColumnHeader[] = [];
  @Input() columnTemplate: TemplateRef&lt;any&gt;;
  @Input() loading: boolean = false;

  @Output() selectedRowsEvent = new EventEmitter&lt;any[]&gt;();

  selectedRows = [];
  constructor(private elRef: ElementRef) {}

  onRowSelect(event) {
    this.selectedRows.push(event.data);
    this.selectedRowsEvent.emit(this.selectedRows);

    const evt = new CustomEvent(&#39;myCustomEvent&#39;, {
      bubbles: true,
      detail: event,
    });
    this.elRef.nativeElement.dispatchEvent(evt);

    console.log(this.selectedRows);
    console.log(event);
    console.log(&#39;from table onRowSelected &#39;);
  }

  onRowUnselect(event) {
    this.selectedRows = this.selectedRows.filter(
      (x) =&gt; x.nvtAreaName !== event.data.nvtAreaName
    );
    this.selectedRowsEvent.emit(this.selectedRows);
    console.log(this.selectedRows);
    console.log(&#39;from table onRowUnselected &#39;);
  }

  // onPage(event) {
  //   this.selectedRows = [];
  //   this.selectedRowsEvent.emit(this.selectedRows);
  // }
}

Table Template

&lt;ng-template #columnTemplate let-rowObject=&quot;rowObject&quot; let-id=&quot;id&quot;&gt;
  &lt;ng-container [ngSwitch]=&quot;id&quot;&gt;
    &lt;span *ngSwitchDefault&gt;{{ rowObject[id] | translate }}&lt;/span&gt;
  &lt;/ng-container&gt;
&lt;/ng-template&gt;
&lt;ng-template #dateColumnTemplate let-rowObject=&quot;rowObject&quot; let-id=&quot;id&quot;&gt;
  &lt;ng-container [ngSwitch]=&quot;id&quot;&gt;
    &lt;span *ngSwitchDefault&gt;{{ rowObject[id] | localizedDate }}&lt;/span&gt;
  &lt;/ng-container&gt;
&lt;/ng-template&gt;

&lt;p-table
  (onRowSelect)=&quot;onRowSelect($event)&quot;
  (onRowUnselect)=&quot;onRowUnselect($event)&quot;
  [paginator]=&quot;true&quot;
  [rows]=&quot;10&quot;
  [showCurrentPageReport]=&quot;true&quot;
  currentPageReportTemplate=&quot;{{ &#39;PAGINATION&#39; | translate }}&quot;
  [rowsPerPageOptions]=&quot;[10]&quot;
  [value]=&quot;rowData&quot;
  [loading]=&quot;loading&quot;
  [tableStyle]=&quot;{ &#39;min-width&#39;: &#39;79rem&#39; }&quot;
&gt;
  &lt;ng-template pTemplate=&quot;header&quot;&gt;
    &lt;tr&gt;
      &lt;th style=&quot;width: 4rem&quot;&gt;
        &lt;p-tableHeaderCheckbox&gt;&lt;/p-tableHeaderCheckbox&gt;
      &lt;/th&gt;
      &lt;ng-container *ngFor=&quot;let header of headers&quot;&gt;
        &lt;th
          *ngIf=&quot;header.sortable; else simpleHeader&quot;
          [pSortableColumn]=&quot;header.id&quot;
        &gt;
          {{ header.value | translate }}
          &lt;p-sortIcon [field]=&quot;header.id&quot;&gt;&lt;/p-sortIcon&gt;
        &lt;/th&gt;
        &lt;ng-template #simpleHeader&gt;
          &lt;th&gt;
            {{ header.value | translate }}
          &lt;/th&gt;
        &lt;/ng-template&gt;
      &lt;/ng-container&gt;
    &lt;/tr&gt;
  &lt;/ng-template&gt;
  &lt;ng-template pTemplate=&quot;body&quot; let-rowObject&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;p-tableCheckbox [value]=&quot;rowObject&quot;&gt;&lt;/p-tableCheckbox&gt;
      &lt;/td&gt;
      &lt;td *ngFor=&quot;let header of headers&quot;&gt;
        &lt;ng-container
          [ngTemplateOutlet]=&quot;
            header?.date ? dateColumnTemplate : columnTemplate
          &quot;
          [ngTemplateOutletContext]=&quot;{ rowObject: rowObject, id: header.id }&quot;
        &gt;&lt;/ng-container&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/ng-template&gt;
&lt;/p-table&gt;

Milestone

import {
  AfterViewInit,
  Component,
  Inject,
  Input,
  OnChanges,
  OnInit,
  SimpleChanges,
  ViewChild,
} from &#39;@angular/core&#39;;
import { TableColumnHeader } from &#39;../../table/models/table-column-header&#39;;
import { NvtAreaDataSource } from &#39;../../../services/nvt-area-data-source.service&#39;;
import { AreaProgramDataSource } from &#39;../../../services/area-program-data-source.service&#39;;
import { MilestoneTableColumn } from &#39;../../../models/business/milestone-table-column&#39;;
import { TableComponent } from &#39;../../table/table.component&#39;;

@Component({
  selector: &#39;app-milestone-search&#39;,
  templateUrl: &#39;./milestone-search.component.html&#39;,
  styleUrls: [&#39;./milestone-search.component.scss&#39;],
})
export class MilestoneSearchComponent
  implements OnInit, OnChanges, AfterViewInit
{
  @Input() selectedGigaArea: string;
  milestoneData = [];
  loading: false;
  private tableComponent!: TableComponent;
  selectedRows = [];

  @Input() pSelectableRows = [];
  @ViewChild(TableComponent)
  columnHeaders: TableColumnHeader[] = [
    { value: &#39;ONKZ&#39;, id: &#39;onkz&#39;, sortable: true },
    { value: &#39;NVT&#39;, id: &#39;nvtAreaName&#39;, sortable: true },
    { value: &#39;STATUS&#39;, id: &#39;status&#39;, sortable: true },
    { value: &#39;ARVM_START&#39;, id: &#39;arvMStart&#39;, date: true },
    { value: &#39;EXP.ROUGH_START&#39;, id: &#39;expRoughStart&#39;, date: true },
    { value: &#39;EXP.ROUGH_END&#39;, id: &#39;expRoughEnd&#39;, date: true },
    { value: &#39;EXP.FINE_START&#39;, id: &#39;expFineStart&#39;, date: true },
    { value: &#39;EXP.FINE_END&#39;, id: &#39;expFineEnd&#39;, date: true },
    { value: &#39;RM_START&#39;, id: &#39;rmStart&#39;, date: true },
    { value: &#39;AFTER_INST_START&#39;, id: &#39;afterInstStart&#39;, date: true },
    { value: &#39;AFTER_INST_END&#39;, id: &#39;afterInstEnd&#39;, date: true },
  ];

  constructor(
    @Inject(NvtAreaDataSource) private nvtAreaDataSource,
    @Inject(AreaProgramDataSource) private areaProgramDataSource
  ) {}

  ngOnInit(): void {
    this.nvtAreaDataSource.connect().subscribe((nvtAreas) =&gt; {
      this.milestoneData = [...nvtAreas];
    });
    this.areaProgramDataSource.connect().subscribe((areaPrograms) =&gt; {
      this.milestoneData = this.mergeMilestonesData(
        this.milestoneData,
        areaPrograms
      );
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.date) {
      // this.pSelectableRows = changes.date.currentValue;
      this.selectedRows = changes.data.currentValue;
    }
    console.log(&#39;from milestone onChanges  &#39; + this.selectedRows.length);
  }
  ngAfterViewInit(): void {
    this.selectedRows = this.tableComponent.selectedRows;
    console.log(&#39;from ngAfterViewInit &#39;);
  }

  onNotify(rowsEmitted: any[]): void {
    console.log(&#39;from milestone onNotify &#39;);
    this.selectedRows = rowsEmitted;
  }

  mergeMilestonesData(nvtAreas, areaPrograms) {
    return nvtAreas.map((nvtArea) =&gt; {
      const areaProgram = areaPrograms.find(
        (x) =&gt; x.nvtAreaId === nvtArea.nvtAreaId
      );
      if (!areaProgram) return nvtArea;
      const { status, milestones } = areaProgram;
      let milestonesColumns = {};
      milestones.map((milestone) =&gt; {
        const milestonesColumn = Object.entries(
          new MilestoneTableColumn()
        ).reduce(
          (acc, [key, value]) =&gt;
            value === milestone.milestoneType
              ? {
                  ...acc,
                  [key]: milestone.milestoneDate,
                }
              : acc,
          {}
        );
        milestonesColumns = { ...milestonesColumns, ...milestonesColumn };
      });
      return {
        ...nvtArea,
        ...milestonesColumns,
        status,
      };
    });
  }
}

Milestone template

&lt;app-table
  (selectedRowsEvent)=&quot;onNotify($event)&quot;
  [pSelectableRows]=&quot;forms.get(&#39;selectedRows&#39;)&quot;
  *ngIf=&quot;milestoneData?.length&quot;
  [rowData]=&quot;milestoneData&quot;
  [headers]=&quot;columnHeaders&quot;
  [loading]=&quot;loading&quot;
&gt;
&lt;/app-table&gt;

Dashboard

import {
  AfterViewInit,
  Component,
  OnChanges,
  SimpleChanges,
  ViewChild,
} from &#39;@angular/core&#39;;
import { TableComponent } from &#39;../../table/table.component&#39;;
import { AreabarComponent } from &#39;../areabar/areabar.component&#39;;

@Component({
  selector: &#39;app-dashboard&#39;,
  templateUrl: &#39;./dashboard.component.html&#39;,
  styleUrls: [&#39;./dashboard.component.scss&#39;],
})
export class DashboardComponent implements AfterViewInit {
  selectedRowsEvent($event: any) {
    throw new Error(&#39;Method not implemented.&#39;);
  }
  @ViewChild(TableComponent)
  selectedRows = [];
  private tableComponent!: TableComponent;

  public selectedGigaArea: string;
  public totalElements: number;
  @ViewChild(AreabarComponent) areabarComponent: AreabarComponent;
  constructor() {}
  ngAfterViewInit(): void {
    this.selectedRows = this.tableComponent.selectedRows;
    console.log(&#39;from ngAfterViewInit &#39;);
  }

  // ngOnChanges(changes: SimpleChanges): void {
  //   console.log(&#39;from Dashboard ngOnChanges &#39;);
  //   throw new Error(&#39;Method not implemented.&#39;);
  // }

  onNotify(rowsEmitted: any[]): void {
    console.log(&#39;from Dashboard onNotify &#39;);
    this.selectedRows = rowsEmitted;
  }
}

Dashboard template

&lt;div class=&quot;dashboard-wrapper&quot;&gt;
  &lt;div id=&quot;giga-areas&quot; class=&quot;giga-areas&quot;&gt;
    &lt;app-areabar
      (selectedGigaAreaEvent)=&quot;selectedGigaArea = $event&quot;
      (totalElementsChanged)=&quot;totalElements = $event&quot;
    &gt;&lt;/app-areabar&gt;
  &lt;/div&gt;
  &lt;div class=&quot;search-wrapper&quot;&gt;
    &lt;h1 class=&quot;page-header&quot;&gt;{{ &quot;SEARCH.ROLLOUT_PROJECT&quot; | translate }}&lt;/h1&gt;
    &lt;nav class=&quot;nav-bar&quot;&gt;
      &lt;mat-button-toggle-group
        #toggleGroup=&quot;matButtonToggleGroup&quot;
        class=&quot;toggle-btn&quot;
      &gt;
        &lt;mat-button-toggle value=&quot;supplier&quot; checked&gt;{{
          &quot;SUPPLIERS&quot; | translate
        }}&lt;/mat-button-toggle&gt;
        &lt;mat-button-toggle value=&quot;milestone&quot;&gt;{{
          &quot;MILESTONES&quot; | translate
        }}&lt;/mat-button-toggle&gt;
      &lt;/mat-button-toggle-group&gt;
      &lt;app-action-button (myCustomEvent)=&quot;onNotify($event)&quot;&gt;&lt;/app-action-button&gt;
    &lt;/nav&gt;
    &lt;div [className]=&quot;toggleGroup.value === &#39;supplier&#39; || &#39;hide&#39;&quot;&gt;
      &lt;app-supplier-search
        class=&quot;nvt-search&quot;
        [selectedGigaArea]=&quot;selectedGigaArea&quot;
      &gt;&lt;/app-supplier-search&gt;
    &lt;/div&gt;
    &lt;div [className]=&quot;toggleGroup.value === &#39;milestone&#39; || &#39;hide&#39;&quot;&gt;
      &lt;app-milestone-search
        (selectedRowsEvent)=&quot;onNotify($event)&quot;
        class=&quot;nvt-search&quot;
        [selectedGigaArea]=&quot;selectedGigaArea&quot;
      &gt;&lt;/app-milestone-search&gt;
    &lt;/div&gt;
    &lt;div *ngIf=&quot;!selectedGigaArea&quot; class=&quot;infoText&quot;&gt;
      {{ &quot;VIEW_EDIT_NVT_AREA&quot; | translate }}
    &lt;/div&gt;
    &lt;div *ngIf=&quot;!selectedGigaArea &amp;&amp; totalElements &gt; 20&quot; class=&quot;infoText&quot;&gt;
      {{ &quot;GIGAAREA_OVERLOAD_MESSAGE&quot; | translate }}
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;app-search class=&quot;nvt-search&quot; [selectedGigaArea]=&quot;selectedGigaArea&quot;&gt;
  &lt;/app-search&gt;
&lt;/div&gt;

ActionButton


import {
  Component,
  Inject,
  Input,
  OnChanges,
  OnInit,
  SimpleChanges,
} from &#39;@angular/core&#39;;
import { AuthenticationProvider } from &#39;src/app/services/auth/auth-service.injection-token&#39;;
import { AuthService } from &#39;src/app/services/auth/auth.service&#39;;
import { MatDialog } from &#39;@angular/material/dialog&#39;;
import { PopupsComponent } from &#39;../shared/popups/popups.component&#39;;
import { Router } from &#39;@angular/router&#39;;
const backUrl = &#39;/home&#39;;
const createrolloutprojects = &#39;/createrolloutprojects&#39;;
const changeMilestonesUrl = &#39;/changemilestones&#39;;
const changeSupplierUrl = &#39;/changesupplier&#39;;
const viewDetailsUrl = &#39;/viewdetails&#39;;
@Component({
  selector: &#39;app-action-button&#39;,
  templateUrl: &#39;./action-button.component.html&#39;,
  styleUrls: [&#39;./action-button.component.scss&#39;],
})
export class ActionButtonComponent implements OnInit, OnChanges {
  selectedRows = [];

  constructor(
    public dialog: MatDialog,
    @Inject(AuthenticationProvider)
    private permissionService: AuthService,
    private router: Router
  ) {}

  ngOnInit(): void {
    this.router.navigate([backUrl]);
    console.log(&#39;from action button component &#39;);
    console.log(this.selectedRows);
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log(&#39;ngonchanges trigged &#39;);
    console.log(this.selectedRows);
  }

  getPermission(permissionKey: string): boolean {
    return !this.permissionService.hasPermission(permissionKey);
  }

  onNotify(rowsEmitted: any[]): void {
    console.log(&#39;from action button onNotify&#39;);
    this.selectedRows = rowsEmitted;
  }

  openPopupDialog(): void {
    console.log(&#39;from openPopupDialog&#39;);
    const dialogRef = this.dialog.open(PopupsComponent, {
      width: &#39;900px&#39;,
      height: &#39;404px&#39;,
      disableClose: true,
      autoFocus: false,
      data: {
        title: &#39;CHANGE_AREA_PROGRAM_STATE.TITLE&#39;,
        plainTextDescription:
          &#39;CHANGE_AREA_PROGRAM_STATE.PLAIN_TEXT_DESCRIPTION&#39;,
        bulletPointDescription:
          &#39;CHANGE_AREA_PROGRAM_STATE.BULLET_POINT_DESCRIPTION&#39;,
        linkText: &#39;&#39;,
        externalLink: &#39;https://...&#39;, //&lt;- url belonging to lintText
        info: &#39;CHANGE_AREA_PROGRAM_STATE.INFO&#39;,
      },
    });

    dialogRef.afterClosed().subscribe((result) =&gt; {
      console.log(result);
    });
  }
}

ActionButton template

&lt;div&gt;
  &lt;button mat-button [matMenuTriggerFor]=&quot;menu&quot;&gt;
    &lt;!-- *ngIf=&quot;selectedRows.length &gt; 0&quot; --&gt;
    {{ &quot;MENU&quot; | translate }}
  &lt;/button&gt;
  &lt;mat-menu #menu=&quot;matMenu&quot;&gt;
    &lt;button
      mat-menu-item
      (click)=&quot;openPopupDialog()&quot;
      [disabled]=&quot;getPermission(&#39;PP_AREA_PROGRAM#COMMISSION&#39;)&quot;
    &gt;
      {{ &quot;COMMISSIONED&quot; | translate }}
    &lt;/button&gt;
    &lt;button
      mat-menu-item
      (click)=&quot;openPopupDialog()&quot;
      [disabled]=&quot;getPermission(&#39;PP_AREA_PROGRAM#EXPANSION&#39;)&quot;
    &gt;
      {{ &quot;EXPANSION.START&quot; | translate }}
    &lt;/button&gt;
    &lt;button
      mat-menu-item
      (click)=&quot;openPopupDialog()&quot;
      [disabled]=&quot;getPermission(&#39;PP_AREA_PROGRAM#CANCEL&#39;)&quot;
    &gt;
      {{ &quot;CANCEL&quot; | translate }}
    &lt;/button&gt;
  &lt;/mat-menu&gt;
&lt;/div&gt;

答案1

得分: 0

以下是翻译好的部分:

无法使其传递的原因

  • 如果传递复杂对象,则子组件上的Ngonchanges不会触发
  • 所以它停留在仪表板(父组件)处,无法传递到子组件

https://i.stack.imgur.com/NRjIa.png

- 解决方法是传递订阅对象
  • 另一个原因是未传递的原因之一:

    > 我们有这个视图:
    >
    > - 仪表板
    > - 操作按钮
    > - 里程碑
    > - 表
    > - 供应商搜索
    > - 表
    >
    > 我一直在通过表 -> 里程碑 -> 仪表板 -> 操作按钮传递
    >
    > 但是我一直在UI的供应商搜索视图上选择行,因此它从从未传递到里程碑

解决方法

  • 通过事件发射器将数组传递到最上层的父组件(仪表板)

  • 然后创建Subject$(可观察对象)以将复杂数据广播到子组件

    > Subject就像一个Observable,但可以广播给多个观察者。Subjects就像EventEmitters:它们维护多个监听器的注册表。 — 来源 rxjs
    >

    代码片段

    dashboard.ts
    
    selectedRows$ = new Subject&lt;any[]&gt;();
    
    onNotify(rowsEmitted: any[]): void {
        console.log('from Dashboard onNotify');
        this.selectedRows = rowsEmitted;
        this.selectedRowsCount = rowsEmitted.length;
        console.log(this.selectedRows);
        this.selectedRows$.next(rowsEmitted);
      }
    
    • onNotify是将数组传递到父组件的最后一个函数
    • 然后创建匿名观察者并订阅(next()
  • 然后将Subject selectedRows$传递给子组件操作按钮

    dashboard.html
    
    <app-action-button [selectedRows]="selectedRows$">
    </app-action-button>
    
  • 然后创建匿名观察者并订阅

    action-button.ts
    
    ngOnInit(): void {
        // this.router.navigate([backUrl]);
        console.log('from action button component ');
        console.log(this.selectedRows);
        this.selectedRows.subscribe((selectedArray) =>
          console.log('from action button ngOnInit: ' + selectedArray)
    );
    
英文:

Reasons why I could not get it passed

  • Ngonchanges on the child component does not get triggered if pass complex object
  • so it stuck at the dashboard (parent component) and does not get passed to the child component

https://i.stack.imgur.com/NRjIa.png

- workaround is to pass the subscribe object
  • another reason why it was not passed:

    > We have this view:
    >
    > - Dashboard
    > - ActionButton
    > - Milestone
    > - Table
    > - SupplierSearch
    > - Table
    >
    > I have been passing through table -> mileston -> dashboard -> actionbutton
    >
    > but I have been selecting rows on the SupplierSearch view of the table on the ui. thus it has never been passed to the Milestone from the Table
    >

Workaround

  • pass the array up to the parent most component (Dashboard) with event emitters

  • then create the Subject$ (observable) two broadcast the complex data to the child component

    > A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners. — source rxjs
    >

    code snippet

    dashboard.ts
    
    selectedRows$ = new Subject&lt;any[]&gt;();
    
    onNotify(rowsEmitted: any[]): void {
        console.log(&#39;from Dashboard onNotify &#39;);
        this.selectedRows = rowsEmitted;
        this.selectedRowsCount = rowsEmitted.length;
        console.log(this.selectedRows);
        this.selectedRows$.next(rowsEmitted);
      }
    
    • on notify is the last function in the chain to pass the array up to the parent component
    • then it create the anonymous observer and subscribe (next())
  • Subject selectedRows$ then will be passed to the child component action button

    dashboard.html
    
    &lt;app-action-button [selectedRows]=&quot;selectedRows$&quot;&gt;
    &lt;/app-action-button&gt;
    
  • it will the create the anonymous observer and subscribe

    action-button.ts
    
    ngOnInit(): void {
        // this.router.navigate([backUrl]);
        console.log(&#39;from action button component &#39;);
        console.log(this.selectedRows);
        this.selectedRows.subscribe((selectedArray) =&gt;
          console.log(&#39;from action button ngOnInit: &#39; + selectedArray)
    );
    
    
    
    
    

huangapple
  • 本文由 发表于 2023年1月8日 23:09:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75048856.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定