Truyền dữ liệu vào các thành phần con “bộ định tuyến-đầu ra”


102

Tôi có một thành phần mẹ đi đến máy chủ và tìm nạp một đối tượng:

// 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);
                }
            );
    }

Và trong một số màn hình dành cho trẻ em:

export class ChildDisplay implements OnInit{

    @Input()
    node: Node;

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

}

Có vẻ như tôi không thể chỉ đưa dữ liệu vào router-outlet. Có vẻ như tôi gặp lỗi trong bảng điều khiển web:

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

Điều này hơi có lý, nhưng tôi sẽ làm như thế nào như sau:

  1. Lấy dữ liệu "nút" từ máy chủ, từ bên trong thành phần mẹ?
  2. Chuyển dữ liệu tôi đã truy xuất từ ​​máy chủ vào ổ cắm bộ định tuyến con?

Có vẻ như nó không router-outletshoạt động theo cùng một cách.

Câu trả lời:


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

chỉ là không hợp lệ. Thành phần do bộ định tuyến thêm vào sẽ được thêm vào dưới dạng anh chị em <router-outlet>và không thay thế nó.

Xem thêm 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);
    }
}

Điều này có thể được sử dụng để cung cấp một ID cho đứa trẻ? Tôi đã cố gắng thay đổi tất cả các nodenơi nó như một loại để stringvà nó dường như không làm việc hoặc tôi đang làm điều gì sai
Wanjia

Bạn có thể chuyển bất kỳ dữ liệu nào cho thành phần con nhưng thành phần con cần phải tự đặt id. Bạn có thể thử để có được một ElementReftừ một sự kiện ra khỏi ổ cắm router để sử dụng nó để thiết lập id nhưng tôi không biết bằng trái tim nếu hoặc làm thế nào để làm điều đó (chỉ trên điện thoại của tôi)
Günter Zöchbauer

46

Câu trả lời của Günters là tuyệt vời, tôi chỉ muốn chỉ ra một cách khác mà không cần sử dụng Observables.

Ở đây, chúng ta phải nhớ rằng các đối tượng này được truyền bằng tham chiếu, vì vậy nếu bạn muốn thực hiện một số công việc trên đối tượng con và không ảnh hưởng đến đối tượng mẹ, tôi khuyên bạn nên sử dụng giải pháp của Günther. Nhưng nếu nó không quan trọng, hoặc thực sự là hành vi mong muốn , tôi sẽ đề xuất những điều sau.

@Injectable()
export class SharedService {

    sharedNode = {
      // properties
    };
}

Trong cha mẹ của bạn, bạn có thể gán giá trị:

this.sharedService.sharedNode = this.node;

Và ở con bạn (VÀ cha mẹ), hãy đưa Dịch vụ được chia sẻ vào hàm tạo của bạn. Hãy nhớ cung cấp dịch vụ ở mảng nhà cung cấp cấp mô-đun nếu bạn muốn có một dịch vụ singleton trên tất cả các thành phần trong mô-đun đó. Ngoài ra, chỉ cần thêm các dịch vụ trong mảng cung cấp dịch vụ trong các phụ huynh chỉ , sau đó phụ huynh và trẻ em sẽ chia sẻ cùng một ví dụ của dịch vụ.

node: Node;

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

Và như người mới vui lòng chỉ ra, bạn cũng có thể có this.sharedService.sharedNodetrong mẫu html hoặc getter:

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

5
bạn có thể / nên liên kết trực tiếp với sharedService.sharedNode trong html. Bằng cách này, các bản cập nhật trong sharedNode sẽ được đồng bộ hóa.
newman

11

Có, bạn có thể đặt đầu vào của các thành phần được hiển thị qua cửa hàng bộ định tuyến . Đáng buồn thay, bạn phải làm điều đó theo chương trình, như đã đề cập trong các câu trả lời khác. Có một cảnh báo lớn đối với điều đó khi các vật thể quan sát được tham gia (mô tả bên dưới).

Đây là cách thực hiện:

(1) Kết nối với activatesự kiện của bộ định tuyến-đầu ra trong mẫu mẹ:

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

(2) Chuyển sang tệp phân loại của phụ huynh và đặt đầu vào của thành phần con theo chương trình mỗi khi chúng được kích hoạt:

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

Phiên bản trên của onOutletLoadedđược đơn giản hóa để rõ ràng hơn, nhưng chỉ hoạt động nếu bạn có thể đảm bảo tất cả các thành phần con có cùng đầu vào chính xác mà bạn đang gán. Nếu bạn có các thành phần với các đầu vào khác nhau, hãy sử dụng loại bảo vệ:

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

Tại sao phương pháp này có thể được ưu tiên hơn phương thức dịch vụ?

Cả phương pháp này và phương thức dịch vụ đều không phải là "cách phù hợp" để giao tiếp với các thành phần con (cả hai phương pháp đều không có liên kết mẫu thuần túy), vì vậy bạn chỉ cần quyết định cách nào phù hợp hơn cho dự án.

Tuy nhiên, phương pháp này cho phép bạn tránh tạo đối tượng trung gian (dịch vụ), đối tượng này kết hợp chặt chẽ giữa dịch vụ và các thành phần con. Trong nhiều trường hợp, điều này gần giống với "cách góc cạnh" hơn vì bạn có thể tiếp tục truyền dữ liệu cho các thành phần con của mình thông qua @Inputs. Nó cũng phù hợp với các thành phần đã có sẵn hoặc của bên thứ ba mà bạn không muốn hoặc không thể kết hợp chặt chẽ với dịch vụ đó. Mặt khác, nó có thể cảm thấy ít giống kiểu góc cạnh hơn khi ...

Cảnh báo trước

Lưu ý với phương pháp này là vì bạn đang truyền dữ liệu theo chương trình, nên bạn không còn có tùy chọn chuyển dữ liệu có thể quan sát được trong mẫu của mình (ngạc nhiên!). Điều đó có nghĩa là bạn mất đi lợi thế to lớn của việc quản lý vòng đời của thiết bị có thể quan sát một cách góc cạnh cho bạn khi bạn sử dụng mô hình pipe-async.

Thay vào đó, bạn sẽ cần thiết lập thứ gì đó để nhận các giá trị có thể quan sát được hiện tại bất cứ khi nào onChildLoaded hàm được gọi. Điều này có thể cũng sẽ yêu cầu một số phần nhỏ trong onDestroyhàm của thành phần mẹ . Điều này không có gì quá bất thường, có những trường hợp khác cần phải thực hiện điều này, chẳng hạn như khi sử dụng một thiết bị có thể quan sát mà thậm chí không vào được mẫu.


9

Dịch vụ:

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());
  })
}

Thành phần:

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

Có 3 cách để chuyển dữ liệu từ Cha mẹ sang Con cái

  1. Thông qua dịch vụ có thể chia sẻ: bạn nên lưu trữ vào một dịch vụ dữ liệu mà bạn muốn chia sẻ với trẻ em
  2. Thông qua Bộ phân giải bộ định tuyến trẻ em nếu bạn phải nhận dữ liệu khác nhau

    this.data = this.route.snaphsot.data['dataFromResolver'];
    
  3. Thông qua Parent Router Resolver nếu bạn phải nhận cùng một dữ liệu từ cha mẹ

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

Lưu ý 1: Bạn có thể đọc về trình giải quyết ở đây . Ngoài ra còn có một ví dụ về trình giải quyết và cách đăng ký trình phân giải vào mô-đun và sau đó truy xuất dữ liệu từ trình giải quyết vào thành phần. Việc đăng ký trình phân giải giống nhau trên cha và con.

Lưu ý 2: Bạn có thể đọc về ActivateRoute tại đây để có thể lấy dữ liệu từ bộ định tuyến


1

Theo câu hỏi này , trong Angular 7.2, bạn có thể chuyển dữ liệu từ cha mẹ sang con bằng cách sử dụng trạng thái lịch sử. Vì vậy, bạn có thể làm điều gì đó như

Gửi:

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

Lấy lại:

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

Nhưng hãy cẩn thận để được nhất quán. Ví dụ: giả sử bạn muốn hiển thị danh sách trên thanh bên trái và thông tin chi tiết của mục đã chọn ở bên phải bằng cách sử dụng ổ cắm bộ định tuyến. Cái gì đó như:


Mục 1 (x) | ...

Mục 2 (x) | ...... Chi tiết mặt hàng đã chọn .......

Mục 3 (x) | ...

Mục 4 (x) | ...


Bây giờ, giả sử bạn đã nhấp vào một số mục. Nhấp vào các nút quay lại của trình duyệt sẽ hiển thị các chi tiết từ mục trước đó. Nhưng nếu trong khi đó, bạn đã nhấp vào (x) và xóa mục đó khỏi danh sách của mình thì sao? Sau đó, thực hiện nhấp lại, sẽ hiển thị cho bạn thông tin chi tiết của một mục đã xóa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.