【问题标题】:Angular2 - Transfer data outside router-outlet to another componentAngular2 - 将路由器出口外的数据传输到另一个组件
【发布时间】:2017-01-28 14:01:39
【问题描述】:

我是 Angular2 的新手,但我已经制作了 30 多个组件和服务,并使用了 Input、Output、ChangeDetectorRef、Pipes。到目前为止,对 Angular2 已经非常了解了。我的 app.component.ts 看起来像这样

import { Component } from '@angular/core';
@Component({
    selector: 'soc',
    template: `
        <header-block></header-block>
        <router-outlet></router-outlet>
    `,
})
export class AppComponent { }

因为我在每个页面中都需要我的标题块,所以我将它添加到路由器块之外,所以我不必在任何地方添加它。现在我遇到的问题是在路由器出口内的视图中执行操作后更改标头块中变量的值。

例如,我有一个菜单,只有在用户登录后才能看到。

{{ user_is_logged }}
<ul class="nav navbar-nav navbar-right" *ngIf="user_is_logged == '1'">
    <li><a href="#" (click)="logout($event)">Logout</a></li>
</ul>

user_is_logged 是一个变量,其值来自 header.block.component.ts

中的 localStorage
user_is_logged:string = localStorage.getItem("logged");

当用户未登录时,即 home.component.ts 未定义 user_is_logged (其中写入登录逻辑),我要求用户登录并在成功登录后更新值user_is_logged 变量并更新变量的 localStorage 值,然后使用 ChangeDetectionRef(cdr) 触发检测更改,然后路由到用户配置文件

this.user_is_logged = "1";
localStorage.setItem('logged', '1');
this.cdr.detectChanges();
this.router.navigate(['u/'+user_id']); //user_id is from login response from server

问题是当我成功登录后到达用户配置文件时,即使使用变量本身或使用 localStorage 中的值甚至调用检测,{{ user_is_logged }} 的值也不会更新。

是否可以这样做,或者我必须分别为每个页面添加标题块?如果我遗漏了什么,请询问更多信息。


编辑 ============>

所以在经历了 Subject、BehaviourSubject、AsyncSubject、ReplaySubject、Observables 等等之后,我就是无法让它工作。这是我写的最后一个代码:

home.page.component.ts(登录发生并且必须发出事件的地方)

import { HomeService } from '../services/home.service';
// No observable or subject imported here.

login() {
    // Sending status 1 to HomeService
    this.HomeService.changeStatus("1");
}

home.service.ts(由两个组件导入)

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
// I imported Subject, BehaviourSubject, Observables but cant get it to work

export class HomeService {

    // This looked more reliable than Subject so used this; init with value "0"
    public subjectStream = new BehaviorSubject<string>('0');


    constructor(
    ){
        this.subjectStream.subscribe(value => {
            // Successfully logs value "1" sent by changeStatus below
            console.log(value);
        });
    }


    changeStatus(value: any){

        // Gives value "1" when called by home.page.component.ts
        console.log(value);

        // Looks correct because it successfully sent value to construct
        this.subjectStream.next(value);

    }

}

header.block.component.ts

import { HomeService } from '../services/home.service';
// No observable or subject imported here also.


constructor(
){
    this.HomeService.subjectStream.subscribe(value => {
        // Logs value "0" at beginning but never logs value "1"
        console.log(value);
    });
}

我的日志

// logged by constructor when imported by header.block.component
home.service.ts:31                  0

// logged by constructor of header.block.component itself
header.block.component.ts:25        0

// logged by constructor when imported by home.page.component (I think)
home.service.ts:31                  0

// ===================
// Login called here
// ===================

// logged by changeStatus after login after called by home.component
home.service.ts:36                  1 

// logged by constructor after getting event from changeStatus
home.service.ts:31                  1

我在 header.block.component.ts 中缺少什么?由于值已在 home.service.ts 中成功更新,但从未进入标题。

【问题讨论】:

  • 只需创建一个服务。将它注入到两个组件中,让它们都订阅服务持有的发射器(EventEmitter 或 Subject,甚至只是一个简单的小观察者模式)。当一个人需要通知另一个人时,一个人只需告诉服务,“发出一个事件”。订阅它的所有东西都会听到它。使用常量能够按名称过滤事件,或者使用一个小工厂来发出类型化的事件,但是你想去做。网络上有很多例子,主要是在标题下,“在没有父/子关系的组件之间发送数据”。
  • 仅供参考,我做的和你做的一样;我的大多数应用程序中都有页眉、路由器插座和页脚。路由器出口发生变化,页眉和页脚保持不变,它们完全通过发射器服务进行通信,这使得整个事情变得如此简单。
  • @TimConsolazio 您是在使用 Günter 在他的评论中建议的 Observables,还是在使用 EventEmitter?
  • 我目前使用 EventEmitter(我问了一下,这并不罕见),因为它扩展了 Subject(所以它是可观察的)。但是,我知道这可能会发生变化,并且 EventEmitter 可能不再以这种方式构建。我猜你可能会担心它是否以及何时发生,但对于向前移动的代码,最好靠近 RxJS 并远离 Angular 事件机制,所以使用 Subject 是合理的。

标签: angular


【解决方案1】:

如果您在主页和标题中提供HomeService,您将获得HomeService 的2 个实例。如果要共享服务,则仅在父组件上提供一次。如果您想与整个应用程序共享单个实例,请仅在 @NgModule() 或 AppModule 处提供它

【讨论】:

  • 如果我从两个组件中的任何一个的@Component 的提供者中删除 HomeService,则会收到此错误 No provider for HomeService! 实际上问题是主页和标题不是父子。就像您在我的 app.component.ts 中看到的问题一样,我为标题选择了两个 并且我的主页在 中,因此它们绝对不是父子,而是更像兄弟姐妹或叔侄,因为主页是 的孩子
  • 您需要在某处提供它。您应该只提供一次。您应该在两者都从中获取它的地方提供它。这意味着如果一个组件是另一个组件的父组件,则在父组件处提供它。如果他们是兄弟姐妹,请提供给共同的父母。在AppModule 中仅提供它始终使其成为应用程序范围的单例。
  • 他们拥有的唯一父级是 app.component.ts 本身。将在那里尝试并更新您。
  • AppComponent 与 AppModule 几乎相同(除了添加到 AppModule 的其他提供程序可能会注入服务)
  • 圣合钼!这确实有效!在 AppModule、Header 和 Home 中导入 HomeService,但仅在 AppModule 中提供,现在可以正常工作。非常感谢先生。
【解决方案2】:

被要求发布此内容,这是一项基本的活动/主题服务,可帮助您入门。我使用“notifier/notes/note”只是为了不让命名法与“事件”和其他框架-api 机制混淆。

// Constant of named "notes" analogous to named events. 
// I always found it easier to have one event type with a "name",
// rather than creating a specific typed event for every action. 
const Notes = {
    HEADER_BUTTON_CLICKED : 'header_button_clicked'
};

// Any component that wants to send data creates a new "Note" to send.
// Just makes sure you know what all callbacks are getting.
class Note {
    constructor ( name, data ) {
        this.name = name; // Should be from your Notes constant.
        this.data = data; // Can be anything at all.
    }
}

// You inject this service into any component that needs it. 
class NotifierService {

    constructor() {

        let Rx = require ( 'rx' );
        // We'll use Subject here. 
        this.subject = new Rx.Subject ( );
    }

    // You only subscribe if you want to hear notes. 
    subscribe ( callback ) {
        // Subscribing component feeds in a callback.
        // The return of this can be used to unsubscribe as well.
        return this.subject.subscribe ( b => callback ( b ) );
    }

    // Any component with access to this service can just create a note
    // and send it to all subscribers to this service. 
    sendNote ( note ) {
       // Component uses the service to emit a "note" with a 
       // named event and a payload. All subscribers hear it in the
       // callbacks they subscribed with, and can filter on the name
       // in the "note" to make sure it's the event they care about. 
       // This is pretty naive but should give the idea.
       this.subject.onNext ( note );
    }
}
export { NotifierService } // Service to inject
export { Note } // A useful type to send out to subscribers 
export { Notes } // Constant of event/note names.

典型用法:

import { NotifierService, Notes, Note } from './notifier.service';
... // inject in your constructor either TS or ES6 style...

// I want to hear notes
this.noteSvc.subscribe ( b => this.myCallback ( b ) );

// I want to send a note
this.noteSvc.sendNote ( new Note ( Notes.HEADER_BUTTON_CLICKED, { btnName : 'Home' } );

   // In the callback 
   myCallback ( note ) {
     if ( note.name === Notes.HEADER_BUTTON_CLICKED ) {
        console.log ( note.data ); // whatever the payload is
     }
   }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 2016-08-28
    • 2019-12-26
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    相关资源
    最近更新 更多