"라우터 콘센트"하위 구성 요소로 데이터 전달


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

그리고 여러 childdren 디스플레이 중 하나에서 :

export class ChildDisplay implements OnInit{

    @Input()
    node: Node;

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

}

.NET Framework에 데이터를 주입 할 수있는 것 같지 않습니다 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그것이 작동하지 않는 것 또는 내가 뭔가 잘못하고 있어요
만가

모든 데이터를 하위 구성 요소에 전달할 수 있지만 하위 구성 요소는 ID 자체를 설정해야합니다. 당신은을 얻기 위해 시도 할 수 ElementRefID를 설정하는 데 사용하는 라우터 콘센트에서 이벤트에서하지만 난 마음으로 알 수없는 경우 또는 (단지 내 전화에) 그 작업을 수행하는 방법에
귄터 Zöchbauer

46

Günters 대답은 훌륭합니다. Observables를 사용하지 않고 다른 방법을 지적하고 싶습니다.

여기서 우리는 이러한 객체가 참조로 전달된다는 것을 기억해야합니다. 따라서 자식의 객체에 대해 약간의 작업을 수행하고 부모 객체에 영향을주지 않으려면 Günther의 솔루션을 사용하는 것이 좋습니다. 그러나 그것이 중요하지 않거나 실제로 원하는 행동 이라면 다음을 제안합니다.

@Injectable()
export class SharedService {

    sharedNode = {
      // properties
    };
}

부모에게 다음 값을 할당 할 수 있습니다.

this.sharedService.sharedNode = this.node;

그리고 자식 (및 부모)에서 생성자에 공유 서비스를 삽입합니다. 해당 모듈의 모든 구성 요소에 대해 단일 서비스를 원하면 모듈 수준 공급자 배열에서 서비스를 제공해야합니다. 또한, 단지 부모의 제공 배열의 서비스를 추가 , 그 부모와 자식이 서비스의 동일한 인스턴스를 공유합니다.

node: Node;

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

그리고 newman이 친절하게 지적했듯이 this.sharedService.sharedNodehtml 템플릿이나 getter에 다음 을 포함 할 수도 있습니다 .

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

5
HTML에서 sharedService.sharedNode에 직접 바인딩 할 수 있습니다. 이렇게하면 sharedNode의 업데이트가 동기화 상태로 유지됩니다.
newman

11

예, 라우터 콘센트를 통해 표시되는 구성 요소의 입력을 설정할 수 있습니다 . 슬프게도 다른 답변에서 언급했듯이 프로그래밍 방식으로해야합니다. 관찰 가능 항목이 관련 될 때 큰주의 사항이 있습니다 (아래 설명).

방법은 다음과 같습니다.

(1)activate 상위 템플릿에서 라우터-아웃렛의 이벤트에 연결 :

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

(2) 부모의 typescript 파일로 전환하고 활성화 될 때마다 프로그래밍 방식으로 자식 구성 요소의 입력을 설정합니다.

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기능 에서 일부 분해가 필요할 수 있습니다. 이것은 너무 드문 일이 아니며, 템플릿에 도달하지 않는 Observable을 사용할 때와 같이 이것이 수행되어야하는 다른 경우가 있습니다.


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. 다른 데이터를 받아야하는 경우 Children Router Resolver를 통해

    this.data = this.route.snaphsot.data['dataFromResolver'];
    
  3. 부모로부터 동일한 데이터를 받아야하는 경우 부모 라우터 확인자를 통해

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

참고 1 : 여기에서 해석기에 대해 읽을 수 있습니다 . 해석기의 예와 해석기를 모듈에 등록한 다음 해석기에서 구성 요소로 데이터를 검색하는 방법도 있습니다. 리졸버 등록은 부모와 자식에서 동일합니다.

Note2 : 여기 에서 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)를 클릭하고 목록에서 해당 항목을 삭제하면 어떻게됩니까? 그런 다음 뒤로 클릭하면 삭제 된 항목의 세부 정보가 표시됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.