Передача даних у дочірні компоненти “router-outlet”


102

У мене є батьківський компонент, який переходить на сервер і отримує об’єкт:

// parent component

@Component({
    selector : 'node-display',
    template : `
        <router-outlet [node]="node"></router-outlet>
    `
})

export class NodeDisplayComponent implements OnInit {

    node: Node;

    ngOnInit(): void {
        this.nodeService.getNode(path)
            .subscribe(
                node => {
                    this.node = node;
                },
                err => {
                    console.log(err);
                }
            );
    }

І в одному з декількох дитячих дисплеїв:

export class ChildDisplay implements OnInit{

    @Input()
    node: Node;

    ngOnInit(): void {
        console.log(this.node);
    }

}

Здається, я не можу просто вводити дані в router-outlet. Схоже, я отримую помилку у веб-консолі:

Can't bind to 'node' since it isn't a known property of 'router-outlet'.

Це дещо має сенс, але як би я зробив наступне:

  1. Захопити дані "вузла" із сервера з батьківського компонента?
  2. Передати дані, які я отримав із сервера, у дочірню розетку?

Здається, router-outletsробота не однакова.

Відповіді:


84
<router-outlet [node]="..."></router-outlet> 

просто недійсний. Компонент, доданий маршрутизатором, додається як рідний брат <router-outlet>і не замінює його.

Див. Також https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

@Injectable() 
export class NodeService {
  private node:Subject<Node> = new BehaviorSubject<Node>([]);

  get node$(){
    return this.node.asObservable().filter(node => !!node);
  }

  addNode(data:Node) {
    this.node.next(data);
  }
}
@Component({
    selector : 'node-display',
    providers: [NodeService],
    template : `
        <router-outlet></router-outlet>
    `
})
export class NodeDisplayComponent implements OnInit {
    constructor(private nodeService:NodeService) {}
    node: Node;
    ngOnInit(): void {
        this.nodeService.getNode(path)
            .subscribe(
                node => {
                    this.nodeService.addNode(node);
                },
                err => {
                    console.log(err);
                }
            );
    }
}
export class ChildDisplay implements OnInit{
    constructor(nodeService:NodeService) {
      nodeService.node$.subscribe(n => this.node = n);
    }
}

Чи можна це використовувати для надання посвідчення дитини? Я спробував змінити все, nodeде це як тип, stringі це, здається, не працює, або я роблю щось не так
Wanjia

Ви можете передати будь-які дані дочірньому компоненту, але дочірньому компоненту потрібно встановити сам ідентифікатор. Ви можете спробувати отримати ElementRefз події з розетки маршрутизатора, щоб використати його для встановлення ідентифікатора, але я не знаю напам’ять, чи і як це робити (лише на моєму телефоні)
Гюнтер Цохбауер,

46

Відповідь Гюнтерса чудова, я просто хочу вказати на інший спосіб, не використовуючи Observables.

Тут ми, однак, повинні пам’ятати, що ці об’єкти передаються за посиланням, тому, якщо ви хочете виконати якусь роботу над об’єктом у дочірній матері і не впливати на батьківський об’єкт, я б запропонував використовувати рішення Гюнтера. Але якщо це не має значення або насправді є бажаною поведінкою , я б запропонував наступне.

@Injectable()
export class SharedService {

    sharedNode = {
      // properties
    };
}

У вашого батька ви можете призначити значення:

this.sharedService.sharedNode = this.node;

А у своїх дітей (І батьків) внесіть спільну Службу у ваш конструктор. Не забудьте надати послугу на масиві постачальників послуг на рівні модуля, якщо ви хочете отримати єдину службу по всіх компонентах цього модуля. Як альтернативи, просто додайте службу в масиві постачальників в батьківському тільки , то батько і дитина буде один і той же екземпляр служби.

node: Node;

ngOnInit() {
    this.node = this.sharedService.sharedNode;    
}

І як люб’язно вказав Ньюман, ви також можете вказати this.sharedService.sharedNodeв шаблоні html або геттері:

get sharedNode(){
  return this.sharedService.sharedNode;
}

5
Ви можете / повинні прив’язати безпосередньо до sharedService.sharedNode у html. Таким чином, оновлення в sharedNode будуть синхронізовані.
Ньюмен

11

Так, ви можете встановити входи компонентів, що відображаються через розетки маршрутизатора . На жаль, ви повинні робити це програмно, як зазначалося в інших відповідях. У цьому є великий застереження, коли задіяні спостережувані (описані нижче).

Ось як:

(1) Підключіть до activateподії маршрутизатора-розетки в батьківському шаблоні:

<router-outlet (activate)="onOutletLoaded($event)"></router-outlet>

(2) Перейдіть до батьківського файлу машинопису та програмно встановлюйте входи дочірнього компонента щоразу, коли вони активуються:

onOutletLoaded(component) {
    component.node = 'someValue';
} 

Наведена вище версія onOutletLoadedспрощена для ясності, але працює лише в тому випадку, якщо ви можете гарантувати, що всі дочірні компоненти мають ті самі входи, які ви призначаєте. Якщо у вас є компоненти з різними входами, використовуйте захисні пристрої типу:

onChildLoaded(component: MyComponent1 | MyComponent2) {
  if (component instanceof MyComponent1) {
    component.someInput = 123;
  } else if (component instanceof MyComponent2) {
    component.anotherInput = 456;
  }
}

Чому цей метод може бути кращим за метод обслуговування?

Ні цей метод, ні метод обслуговування не є "правильним способом" спілкування з дочірніми компонентами (обидва методи відходять від чистого прив'язки шаблону), тому вам просто потрібно вирішити, який шлях вважається більш доцільним для проекту.

Однак цей метод дозволяє уникнути створення об'єкта-посередника (служби), який щільно поєднує службу та дочірні компоненти. У багатьох випадках це здається наближеним до "кутового шляху", оскільки ви можете продовжувати передавати дані дочірнім компонентам через @Inputs. Він також добре підходить для вже існуючих або сторонніх компонентів, які ви не хочете або не можете тісно поєднати з цією послугою. З іншого боку, це може бути менш схожим на кутовий спосіб, коли ...

Застереження

Застереження цього методу полягає в тому, що, оскільки ви передаєте дані програмно, у вас більше немає можливості передавати спостережувані дані у ваш шаблон (сюрприз!). Це означає, що ви втрачаєте величезну перевагу кутового управління життєвим циклом спостережуваного для себе, коли використовуєте шаблон асинхронної труби.

Натомість вам потрібно буде налаштувати щось, щоб отримувати поточні спостережувані значення при кожному виклику onChildLoaded функції. Для цього, ймовірно, також знадобиться деяке розірвання функції батьківського компонента onDestroy. Це не надто незвично, є й інші випадки, коли це потрібно зробити, наприклад, коли використовується спостережуваний, який навіть не потрапляє до шаблону.


9

Сервіс:

import {Injectable, EventEmitter} from "@angular/core";    

@Injectable()
export class DataService {
onGetData: EventEmitter = new EventEmitter();

getData() {
  this.http.post(...params).map(res => {
    this.onGetData.emit(res.json());
  })
}

Компонент:

import {Component} from '@angular/core';    
import {DataService} from "../services/data.service";       
    
@Component()
export class MyComponent {
  constructor(private DataService:DataService) {
    this.DataService.onGetData.subscribe(res => {
      (from service on .emit() )
    })
  }

  //To send data to all subscribers from current component
  sendData() {
    this.DataService.onGetData.emit(--NEW DATA--);
  }
}

8

Існує 3 способи передачі даних від батьків до дітей

  1. Через спільний сервіс: ви повинні зберігати в службі дані, якими ви хотіли б поділитися з дітьми
  2. Через дитячий маршрутизатор, якщо вам потрібно отримувати різні дані

    this.data = this.route.snaphsot.data['dataFromResolver'];
    
  3. Через батьківський маршрутизатор, якщо вам доведеться отримувати ті самі дані від батьків

    this.data = this.route.parent.snaphsot.data['dataFromResolver'];
    

Примітка1: Ви можете прочитати про вирішувач тут . Також є приклад резольвера та способу реєстрації резольвера в модулі, а потім отримання даних із резольвера в компоненті. Реєстрація вирішувача однакова для батьків та дитини.

Примітка2: Ви можете прочитати про ActivatedRoute тут, щоб отримати дані з маршрутизатора


1

Після цього питання в Angular 7.2 ви можете передавати дані від батьків до дитини, використовуючи стан історії. Тож ви можете зробити щось на зразок

Надіслати:

this.router.navigate(['action-selection'], { state: { example: 'bar' } });

Отримати:

constructor(private router: Router) {
  console.log(this.router.getCurrentNavigation().extras.state.example);
}

Але будьте обережні, щоб бути послідовними. Наприклад, припустимо, ви хочете відобразити список на лівій бічній панелі, а деталі вибраного елемента праворуч за допомогою розетки маршрутизатора. Щось на зразок:


Пункт 1 (x) | ..............................................

Пункт 2 (x) | ...... Вибрані відомості про товар .......

Пункт 3 (x) | ..............................................

Пункт 4 (x) | ..............................................


Тепер, припустимо, ви вже натиснули деякі елементи. Натискання кнопок браузера назад відображає деталі попереднього елемента. Але що, якщо тим часом ви натиснули (x) і видалили зі свого списку цей елемент? Потім, виконавши зворотний клік, ви побачите деталі видаленого елемента.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.