【问题标题】:Proper way to bind to data object in Angular 2 service?在 Angular 2 服务中绑定数据对象的正确方法?
【发布时间】:2016-12-20 02:16:16
【问题描述】:

我正在构建一个 Angular 2 应用程序。自发布以来,文档发生了很大变化,这引起了混乱。我能做的最好的就是解释我想要做什么(这在 Angular 1 中很容易)并希望有人能帮助我。

我已经使用 JWT 创建了一个登录服务。 登录成功后,我返回一个用户对象。

我有一个 loginComponent(将数据绑定到模板)和 loginService(处理 https 调用)

我有一个维护用户对象的 userService。

我有一个呈现用户数据的 userComponent。

问题是,一旦用户登录,我不清楚让 userService 在名为“user”的对象中检索新数据的最佳方法,然后 userComponent 在模板上更新其用户对象。这在 Angular 1 中很容易,只需在 userService.user 对象上放置一个观察者即可。

我尝试了 Inputs 和 Outputs 无济于事,eventEmitters、Observables 和 getter 和 setter。 getter 和 setter 工作,但迫使我将所有内容存储在“val()”中

有人可以告诉我实现这一目标的最佳方法吗?

  1. 用户组件使用 user.firstName、user.lastName 等呈现模板。
  2. 如果对象为空,则初始用户
  3. 登录服务需要设置UserService.user
  4. userComponent 需要检测变化并更新 DOM。

提前感谢!

【问题讨论】:

    标签: javascript angularjs angular


    【解决方案1】:

    如果我没记错的话,您正在寻找一种方法来“监听”您的 UserService.user 中的更改,以便在您的 UserComponent 中进行适当的更新。使用Subject(或BehaviorSubject)很容易做到这一点。

    -在您的UserService 中,声明一个属性user 类型为Subject<User>

    user: Subject<User> = new Subject();
    

    -将其暴露在可观察的外部:

    user$: Observable<User>
    ...
    this.user$ = this.user.asObservable();
    

    -登录功能将更新私人user主题。

    login(userName: string, password: string) {
      //...
      this.user.next(new User("First name", "Last name"));
    }
    

    -在您的UserComponent 中,订阅UserServiveuser$ observable 以更新视图。

    this.userService.user$.subscribe((userData) => {this.user = userData;});
    

    -在您看来,只需使用字符串插值:

    {{user?.firstName}} {{user?.lastName}}
    

    这里是工作插件:http://plnkr.co/edit/qUR0spZL9hgZkBe8PHw4?p=preview

    【讨论】:

    • 所以本质上这与角度 1 中的观察者相同?服务中的数据更改时视图会更新吗?
    • 另外,我们的目的是什么?分数?可选变量?
    • 是的。您可以在提供的 plunker 中使用它。存在运算符 (?.) 用于安全地检索可为空对象的属性值。如果user 为null 或未定义,这将温和地将空字符串渲染到标记上,而使用user.firstName 会抛出一些错误。在 plunker 中,我将所有内容都包装在 ngIf 中,所以我不需要它。
    • 服务中的数据以这样一种方式更新,使其他人能够订阅其更改 (Subject.next)。我们通过在任何对此感兴趣的组件中调用 user$.subscribe 来监听该事件。
    • 我最初像这样绑定我的模型数据... ****如何使绑定成为可选,以便用户对象在它不存在时不会失败?我将 user = {} 添加到构造函数中。这是正确的方法吗?
    【解决方案2】:

    您可以采取两种截然不同的方法:

    1.通过 JavaScript 引用类型共享数据

    如果您在 UserService 中创建对象

    @Injectable()
    export class UserService {
       public user = new User();
    

    然后,您可以仅凭借它是 JavaScript 引用类型来共享该对象。注入 UserService 的任何其他服务或组件都可以访问该 user 对象。只要您在服务中只修改原始对象(即不分配新对象),

       updateUser(user:User) {
           this.user.firstName = user.firstName;
           this.user.lastName  = user.lastName;
       }
    

    您的所有视图都会在更改后自动更新并显示新数据(因为 Angular 更改检测的工作方式)。有no need for any Angular 1-like watchers

    这是一个示例plunker

    在 plunker 中,不是共享的 user 对象,而是共享的 data 对象。您可以单击一个 更改数据 按钮,该按钮将调用服务上的 changeData() 方法。您可以看到,当服务更改其data 属性时,AppComponent 的视图会自动更新。您无需编写任何代码即可完成这项工作——不需要 getter、setter、Input、Output/EventEmitter 或 Observable。

    视图更新会自动发生,因为(默认情况下)每次触发猴子补丁的异步事件(例如单击按钮)时,Angular 更改检测都会检查所有模板绑定(例如 {{data.prop1}})。

    2。使用 RxJS 的“Push”数据

    @HarryNinh 在他的回答中很好地涵盖了这一点。另请参阅食谱主题 Parent and children communicate via a service。它展示了如何使用主题来促进“家庭内部”的交流。

    我建议使用BehaviorSubject 而不是主题,因为 BehaviorSubject 具有“当前值”的概念,这很可能适用于此。考虑一下,如果您使用路由并且(基于某些用户操作)移动到新路由并创建一个新组件,您可能希望该新组件能够检查用户的“当前值”。你需要一个 BehaviorSubject 来完成这项工作。如果您使用常规主题,新组件将无法检索当前值,因为主题的订阅者只能获取新发出的值。


    那么,我们应该使用方法 1. 还是 2.?像往常一样,“这取决于”。方法 1. 代码少很多,而且您不需要了解 RxJS(但您确实需要了解 JavaScript 引用类型)。方法 2. 现在风靡一时。

    方法 2. 也可能比 1. 更有效,但是因为 Angular 的默认变更检测策略是“检查所有组件”,所以您需要使用 OnPush 变更检测策略和 markForCheck()(我是不打算在这里讨论如何使用它们)以使其比方法 1 更有效。

    【讨论】:

    猜你喜欢
    • 2017-03-16
    • 1970-01-01
    • 2017-03-17
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 2017-12-28
    • 2017-03-11
    • 2013-03-25
    相关资源
    最近更新 更多