Как передать данные от охраны дочернего компонента родительскому компоненту?

У меня есть родительский компонент, у которого есть куча вкладок со ссылками. На каждой вкладке есть ссылка, по которой можно перейти к дочернему компоненту. На родительской вкладке есть данные заголовка, такие как имя клиента, dob, пол, ...

Родительский компонент .HTML выглядит следующим образом:

 <mat-card>
    <mat-card-header>
      <mat-card-title>
        <mat-chip-list aria-label="Title" class="table_title">
          <mat-chip color="primary" selected>Client Detail</mat-chip>
          <mat-chip color="accent" *ngIf="clientName">Client Name: {{ clientName }}</mat-chip>
          <mat-chip color="accent" *ngIf="clientAlias">DOB: {{ clientDOB }}</mat-chip>
          <mat-chip color="accent" *ngIf="clientAlias">Gender: {{ clientGender }}</mat-chip>

        </mat-chip-list>
      </mat-card-title>
    </mat-card-header>
    <mat-card-content>
      <nav mat-tab-nav-bar>
        <a mat-tab-link
          [routerLink]="['./demographics']"
          (click)="activeLink = 'demographics'"
          [active]="activeLink == 'demographics'">
          <mat-icon class="tab-icon">contact_page</mat-icon>
          Identifying Information
        </a>
        <a mat-tab-link
          [routerLink]="['./contacts']"
          (click)="activeLink = 'contacts'"
          [active]="activeLink == 'contacts'">
          <mat-icon class="tab-icon">contacts</mat-icon>
          Contacts
        </a>
      </nav>

      <router-outlet></router-outlet>
    </mat-card-content>
  </mat-card>

Выход маршрутизатора отобразит соответствующий дочерний компонент на основе имени вкладки.

Родительский компонент .ts выглядит следующим образом:

ngOnInit() {
    // Calling the resolver and updating the activeLink
    this.activeLink = this.route.snapshot.data.tab;

    // Set the header data
    this.route.data.subscribe(resolvedData => {
      if (resolvedData.headerData !== null) {
        this.clientName = resolvedData.headerData.fullName;
        this.clientDOB = resolvedData.headerData.dob;
        this.clientGender = resolvedData.headerData.gender;
      } else {
        this.router.navigate(['/clients']);
      }
    });
  }

Навигация (отображение) дочернего компонента (демография) позволит пользователю редактировать информацию о клиенте, такую ​​как имя, пол, dob.

Сохранение страницы демографических данных клиента (компонента) выполняется с помощью средства защиты (Деактивировать), что означает, что пользователь не может перейти на страницу контактов или другую страницу (вкладку), если данные не верны.

Моя деактивированная охрана выглядит следующим образом:

if (model.dirty && model.valid) {
      // call the saving service
      this.clientService.UpdateClientIdentInfo(personId, model.getRawValue()).subscribe(updatedHeaderData => {
        // TODO: PASS updatedHeaderData TO THE PARENT COMPOENET
        this.snackBar.open('Data has been saved successfully!', '', {
          duration: 2000,
          horizontalPosition: 'center',
          verticalPosition: 'bottom',
          panelClass: ['snack-success']
        });
        return true;
      }, error => {
        return false;
      })
    }

Ответ на этот вызов API updatedHeaderData будет содержать новые данные, которые я хочу передать родительскому компоненту, чтобы все данные его заголовка обновились.

Вот моя модель маршрутизации приложений

{
    path: 'clients/:id',
    component: ClientDetailsComponent,
    resolve: {
      tab: ClientDetailsResolver,
      headerData: ClientDetailHeaderDataResolver
    },
    children: [
      {
        path: 'demographics',
        component: ClientIdentInfoComponent,
        resolve: { client: ClientIdentInfoResolver },
        canDeactivate: [ ClientIdentInfoGuard ]
      },
      {
        path: 'contacts',
        component: ClientContactComponent,
        resolve: { PersonContacts: ClientContactsResolver},
        canDeactivate: [ ClientContactsGuard ]
      },
      {
        path: '',
        redirectTo: 'demographics',
        pathMatch: 'full'
      }
    ]
  },

person Riad    schedule 29.05.2021    source источник


Ответы (1)


Воспользуйтесь услугой здесь. Что-то вроде этого.

HeaderDataService

private pHeaderData: Subject<any> = new Subject<any>();

get headerData(): Observable<any> | any {
    return this.pHeaderData.asObservable();
}

set headerData(data: any) {
    this.pHeaderData.next(data);
}

Родительский компонент

const subscription: Subscription = new Subscription();

constructor(private headerDataService: HeaderDataService) { }

ngOnInit(): void {
    this.subscription.add(
        this.headerDataService.headerData.subscribe( data => {
            console.log('delivery from ChildComponent arrived: ', data);
        })
    );
}

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

Детский компонент

constructor(private headerDataService: HeaderDataService) { }

if (model.dirty && model.valid) {
  // call the saving service
  this.clientService.UpdateClientIdentInfo(personId, model.getRawValue()).subscribe(updatedHeaderData => {

    // inform the parent
    this.headerDataService.headerData = updatedHeaderData;

    this.snackBar.open('Data has been saved successfully!', '', {
      duration: 2000,
      horizontalPosition: 'center',
      verticalPosition: 'bottom',
      panelClass: ['snack-success']
    });
    return true;
  }, error => {
    return false;
  })
}
person Lynx 242    schedule 29.05.2021
comment
Привет, Рысь. Спасибо за ваш ответ .. Я не думаю, что это сработает ... если вы посмотрели на маршрутизацию приложения, каждый маршрут использует преобразователь, который его компонент получает разрешенные данные в цикле OnInit .. Родительский компонент уже был бы загружен поэтому вы не можете использовать цикл OnInit, если это имеет смысл. - person Riad; 29.05.2021
comment
Я подумал о BehaviorSubject и наткнулся на то, как обновить элементы компонента после завершения onInit. - person Riad; 29.05.2021
comment
Не поймите меня неправильно, но похоже, что вы не знакомы с концепцией подписки на Observable. Магия в том, что не имеет значения, когда вы подписываетесь. Потому что вы только говорите Observable, что с этого момента хотите получать информацию о каждом изменении объекта, который переносится Observable. Понятно, что ваш родительский компонент работает задолго до того, как оживает дочерний компонент. Но еще раз. Родитель просто сказал Observable получить информацию об изменениях и передать новый объект внутри метода подписки. - person Lynx 242; 29.05.2021
comment
Также возможен BehaviorSubject. Но обычно он вам нужен только тогда, когда у вас есть вариант использования, когда вам нужно получить доступ к значению внутри объекта, не дожидаясь события, инициированного .next(). Обратите внимание, что вы подписываетесь только внутри onInit(). Подписка будет действовать, пока жив компонент. Таким образом, каждый раз, когда какой-либо метод помещает новое значение в Subject и, таким образом, запускает .next(), ваш родительский компонент получает обновление. Это не имеет ничего общего с ngOnInit() в целом. Вы также можете переместить эту подписку в конструктор. Это сработает. - person Lynx 242; 29.05.2021
comment
Попробуйте код. Вы увидите, что это сработает. - person Lynx 242; 29.05.2021
comment
Спасибо, Lynx, за ваш ответ. Я предполагаю, что ваш ответ имеет смысл, когда вы сказали, что не имеет значения, когда вы подписываетесь ... в основном, пока есть подписка, не имеет значения время ... Я собираюсь попробовать, однако, нет мы получим ошибку, потому что ваш набор и средство доступа не имеют того же типа? - person Riad; 30.05.2021
comment
Ага, я получаю эту ошибку - ›ошибка TS2380: аксессоры get и set должны иметь один и тот же тип. - person Riad; 30.05.2021
comment
Это сработало. Спасибо, Lynx .. Пожалуйста, обновите свой ответ, включив в него Observable ‹any› | любой для добытчика - person Riad; 30.05.2021
comment
Выполнено. Я отредактировал ответ. - person Lynx 242; 30.05.2021