将数据传递到"router-outlet"子组件(角度2)

Zac*_*ack 80 dependency-injection angular2-routing angular

我有一个父组件进入服务器并获取一个对象.

// 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);
                }
            );
    }
Run Code Online (Sandbox Code Playgroud)

在(孩子中的一个)我的孩子展示

export class ChildDisplay implements OnInit{

    @Input()
    node: Node;

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

}
Run Code Online (Sandbox Code Playgroud)

看起来我没想到只能将数据注入路由器插座.看起来我在Web控制台中收到了错误...

Can't bind to 'node' since it isn't a known property of 'router-outlet'.
Run Code Online (Sandbox Code Playgroud)

这有点道理,但我怎么做以下......

// 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);
                }
            );
    }
Run Code Online (Sandbox Code Playgroud)

看起来路由器插座的工作方式并不相同.

Gün*_*uer 69

唯一的方法是使用共享服务.

<router-outlet [node]="..."></router-outlet> 
Run Code Online (Sandbox Code Playgroud)

只是无效.路由器添加的组件作为兄弟添加<router-outlet>,但不替换它.

另见https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-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);
  }
}
Run Code Online (Sandbox Code Playgroud)
@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);
                }
            );
    }
}
Run Code Online (Sandbox Code Playgroud)
export class ChildDisplay implements OnInit{
    constructor(nodeService:NodeService) {
      nodeService.node$.subscribe(n => this.node = n);
    }
}
Run Code Online (Sandbox Code Playgroud)


AJT*_*T82 40

Günters的答案很棒,我只想指出不使用Observables的另一种方法.

这里我们必须记住这些对象是通过引用传递的,所以如果你想对子对象做一些工作并且不影响父对象,我建议使用Günther的解决方案.但如果无关紧要,或者实际上是期望的行为,我建议如下.

@Injectable()
export class SharedService {

    sharedNode = {
      // properties
    };
}
Run Code Online (Sandbox Code Playgroud)

在您的父级中,您可以分配值:

this.sharedService.sharedNode = this.node;
Run Code Online (Sandbox Code Playgroud)

在您的孩子(和父母)中,在构造函数中注入共享服务.如果您希望在该模块中的所有组件上都使用单件服务,请记住在模块级提供程序数组中提供服务.另外,只需添加服务父的供应商阵列中,那么家长和孩子将共享相同的服务实例.

node: Node;

ngOnInit() {
    this.node = this.sharedService.sharedNode;    
}
Run Code Online (Sandbox Code Playgroud)

正如纽曼亲切地指出的那样,你也可以this.sharedService.sharedNode在html模板或getter中:

get sharedNode(){
  return this.sharedService.sharedNode;
}
Run Code Online (Sandbox Code Playgroud)

  • 你可以/应该直接绑定到html中的sharedService.sharedNode.这样,sharedNode中的更新将保持同步. (5认同)

Don*_*ghn 34

是的,您可以将数据直接传递到路由器插座组件中。遗憾的是,如其他答案中所述,您不能使用 angular 模板绑定来做到这一点。您必须在打字稿文件中设置数据。当涉及 observables 时,有一个很大的警告(如下所述)。

就是这样:

(1)activate在父模板中连接到路由器插座的事件:

<router-outlet (activate)="onOutletLoaded($event)"></router-outlet>
Run Code Online (Sandbox Code Playgroud)

(2)切换到父组件的打字稿文件,并在每次激活子组件时以编程方式设置子组件的输入:

onOutletLoaded(component) {
    component.someProperty = 'someValue';
} 
Run Code Online (Sandbox Code Playgroud)

完毕。

但是,onOutletLoaded为了清楚起见,简化了上述版本。只有当您可以保证所有子组件都具有您分配的完全相同的输入时,它才有效。如果您有不同输入的组件,请使用类型保护:

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

为什么这种方法比服务方法更受欢迎?

这种方法和服务方法都不是与子组件通信的“正确方式”(这两种方法都远离纯模板绑定),因此您只需要决定哪种方式更适合项目。

然而,这种方法避免了与“创建用于通信的服务”方法相关的紧密耦合(即,父级需要该服务,而子级都需要该服务,使得子级在其他地方无法使用)。

在许多情况下,这种方法也感觉更接近“角度方式”,因为您可以继续通过@Inputs 将数据传递给您的子组件。它也非常适合您不想或不能与您的服务紧密耦合的现有或第三方组件。

另一方面,当...

警告

这种方法的警告是,由于您在打字稿文件中传递数据,您不再可以选择使用模板(例如{{ myObservable$ | async }})中使用的管道异步模式来自动使用您的可观察数据并将其传递给子组件。

相反,您需要设置一些东西来在onChildLoaded 调用函数时获取当前的可观察值。这可能还需要对父组件的onDestroy功能进行一些拆卸。这没什么不寻常的,通常情况下需要这样做,例如当使用一个甚至没有到达模板的 observable 时。

  • 谢谢!这是一个很好的答案 (4认同)

ego*_*xyz 6

服务:

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());
  })
}
Run Code Online (Sandbox Code Playgroud)

零件:

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--);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @JavaFreak和egor:不推荐这种方法,请检查:/sf/answers/2525369101/如果你想这样做,请改用主题或行为主题,如Günter所示. (2认同)

Rzv*_*van 5

有3种方式将数据从父母传递给孩子

  1. 通过共享服务:您应该将要与孩子共享的数据存储到服务中
  2. 如果您必须接收其他数据,则通过Children Router Resolver

    this.data = this.route.snaphsot.data['dataFromResolver'];
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果您必须从父级接收相同的数据,则通过父级路由解析器

    this.data = this.route.parent.snaphsot.data['dataFromResolver'];
    
    Run Code Online (Sandbox Code Playgroud)

注意1:您可以在此处阅读有关解析器的信息。还有一个解析器示例,以及如何将解析器注册到模块中,然后如何将数据从解析器检索到组件中。解析器注册在父母和孩子上是相同的。

注意2:您可以在此处阅读有关ActivatedRoute的 信息,以便能够从路由器获取数据


MTZ*_*MTZ 5

遵循这个问题,在 Angular 7.2 中,您可以使用历史状态将数据从父级传递到子级。所以你可以做类似的事情

发送:

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

取回:

constructor(private router: Router) {
  console.log(this.router.getCurrentNavigation().extras.state.example);
}
Run Code Online (Sandbox Code Playgroud)

但要注意保持一致。例如,假设您想使用路由器出口在左侧栏上显示列表,并在右侧显示所选项目的详细信息。就像是:


第 1 项 (x) | ................................................

第 2 项 (x) | ......所选项目详细信息......

第 3 项 (x) | ................................................

第 4 项 (x) | ................................................


现在,假设您已经单击了一些项目。单击浏览器后退按钮将显示上一项的详细信息。但是,如果同时您单击了 (x) 并从列表中删除了该项目,该怎么办?然后执行后退单击,将显示已删除项目的详细信息。