I am building an angular 2 application. The documentation has changed quite a bit since the released which has caused confusion. The best I can do is explain what I am trying to do (Which was easy in Angular 1) and hope someone can help me out.
我正在建立一个角度2应用程序。自发布以来,文档发生了很大变化,引起了混乱。我能做的最好的事情就是解释我想要做什么(在Angular 1中这很容易)并希望有人可以帮助我。
I have created a login service using JWT's. Once login is successful, I return a user object.
我使用JWT创建了一个登录服务。登录成功后,我返回一个用户对象。
I have a loginComponent ( binds data to template ) and loginService ( which handles the https calls )
我有一个loginComponent(将数据绑定到模板)和loginService(处理https调用)
I have a userService which maintains the user object.
我有一个维护用户对象的userService。
I have a userComponent which renders the user data.
我有一个userComponent,用于呈现用户数据。
The problem is, once the user has logged in, I am unclear on the best approach for letting the userService retrieve the new data in an object called "user", then the userComponent update its user object on the template. This was easy in angular 1 simply by putting a watcher on the userService.user object.
问题是,一旦用户登录,我不清楚让userService在名为“user”的对象中检索新数据的最佳方法,然后userComponent更新模板上的用户对象。只需将观察者放在userService.user对象上,就可以轻松实现角度1。
I tried Inputs and Outputs to no avail, eventEmitters, Observables and getters and setters. The getters and setters work, but force me to store everything in a "val()"
我尝试输入和输出无效,eventEmitters,Observables和getter和setter。 getter和setter工作,但强迫我将所有内容存储在“val()”中
Can someone please tell me the best way to achieve this?
有人可以告诉我实现这个目标的最佳方法吗?
- User Component renders template with user.firstName, user.lastName etc.
- Initially user if an empty Object
- The login service needs to set the UserService.user
- The userComponent Needs to detect the change and update the DOM.
用户组件使用user.firstName,user.lastName等呈现模板。
最初用户如果是空对象
登录服务需要设置UserService.user
userComponent需要检测更改并更新DOM。
Thanks in ADVANCE!
提前致谢!
2 个解决方案
#1
8
If I'm not wrong, you are looking for a way to 'listen' to changes in your UserService.user
to make appropriate updates in your UserComponent
. It is fairly easy to do that with Subject
(or BehaviorSubject
).
如果我没有错,那么您正在寻找一种方法来“监听”UserService.user中的更改,以便在UserComponent中进行适当的更新。使用Subject(或BehaviorSubject)很容易做到这一点。
-In your UserService
, declare a property user
with type Subject<User>
.
- 在UserService中,声明类型为Subject
user: Subject<User> = new Subject();
-Expose it to outside as observable:
- 将其暴露在外面作为可观察的:
user$: Observable<User>
...
this.user$ = this.user.asObservable();
-Login function will update the private user
Subject.
-Login函数将更新私有用户Subject。
login(userName: string, password: string) {
//...
this.user.next(new User("First name", "Last name"));
}
-In your UserComponent
, subscribe to UserServive
's user$ observable
to update view.
- 在UserComponent中,订阅UserServive的用户$ observable以更新视图。
this.userService.user$.subscribe((userData) => {this.user = userData;});
-In your view, simply use string interpolation:
- 在您看来,只需使用字符串插值:
{{user?.firstName}} {{user?.lastName}}
Here is the working plunker: http://plnkr.co/edit/qUR0spZL9hgZkBe8PHw4?p=preview
这是工作的插件:http://plnkr.co/edit/qUR0spZL9hgZkBe8PHw4?p = preview
#2
4
There are two rather different approaches you could take:
您可以采取两种不同的方法:
1. Share data via JavaScript reference types
1.通过JavaScript引用类型共享数据
If you create an object in your UserService
如果在UserService中创建对象
@Injectable()
export class UserService {
public user = new User();
you can then share that object just by virtue of it being a JavaScript reference type. Any other service or component that injects the UserService will have access to that user
object. As long as you only modify the original object (i.e., you don't assign a new object) in your service,
然后,您可以通过它作为JavaScript引用类型来共享该对象。注入UserService的任何其他服务或组件都可以访问该用户对象。只要您在服务中仅修改原始对象(即,您不指定新对象),
updateUser(user:User) {
this.user.firstName = user.firstName;
this.user.lastName = user.lastName;
}
all of your views will automatically update and show the new data after it is changed (because of the way Angular change detection works). There is no need for any Angular 1-like watchers.
所有视图都会在更改后自动更新并显示新数据(因为Angular更改检测的工作方式)。没有任何类似Angular 1的观察者。
Here's an example plunker.
这是一个示例的plunker。
In the plunker, instead of a shared user
object, it has a shared data
object. There is a change data button that you can click that will call a changeData()
method on the service. You can see that the AppComponent's view automatically updates when the service changes its data
property. You don't have to write any code to make this work -- no getter, setter, Input, Output/EventEmitter, or Observable is required.
在plunker中,它具有共享数据对象,而不是共享用户对象。您可以单击一个更改数据按钮,该按钮将调用服务上的changeData()方法。您可以看到AppComponent的视图在服务更改其数据属性时自动更新。您不必编写任何代码来使其工作 - 不需要getter,setter,Input,Output / EventEmitter或Observable。
The view update automatically happens because (by default) Angular change detection checks all of the template bindings (like {{data.prop1}}
) each time a monkey-patched asynchronous event fires (such as a button click).
视图更新自动发生,因为(默认情况下)角度更改检测每次启动猴子修补的异步事件(例如按钮单击)时都会检查所有模板绑定(如{{data.prop1}})。
2. "Push" data using RxJS
2.使用RxJS“推送”数据
@HarryNinh covered this pretty well in his answer. See also Cookbook topic Parent and children communicate via a service. It shows how to use a Subject to facilitate communications "within a family".
@HarryNinh在他的回答中说得很清楚。另请参阅Cookbook主题父级和子级通过服务进行通信。它显示了如何使用主题来促进“在一个家庭中”的通信。
I would suggest using a BehaviorSubject instead of a Subject because a BehaviorSubject has the notion of "the current value", which is likely applicable here. Consider, if you use routing and (based on some user action) you move to a new route and create a new component, you might want that new component to be able check the "current value" of the user. You'll need a BehaviorSubject to make that work. If you use a regular Subject, the new component will have no way to retrieve the current value, since subscribers to a Subject can only get newly emitted values.
我建议使用BehaviorSubject而不是Subject,因为BehaviorSubject具有“当前值”的概念,这可能适用于此处。考虑一下,如果您使用路由并(基于某些用户操作)移动到新路由并创建新组件,您可能希望该新组件能够检查用户的“当前值”。你需要一个BehaviorSubject才能使它工作。如果使用常规Subject,则新组件将无法检索当前值,因为Subject的订阅者只能获取新发出的值。
So, should we use approach 1. or 2.? As usual, "it depends". Approach 1. is a lot less code, and you don't need to understand RxJS (but you do need to understand JavaScript reference types). Approach 2. is all the rage these days.
那么,我们应该使用方法1.还是2.?像往常一样,“这取决于”。方法1.代码少得多,您不需要了解RxJS(但您需要了解JavaScript引用类型)。方法2.这些天风靡一时。
Approach 2. could also be more efficient than 1., but because Angular's default change detection strategy is to "check all components", you would need to use the OnPush
change detection strategy and markForCheck()
(I'm not going to get into how to use those here) to make it more efficient than approach 1.
方法2.也可以比1.更有效,但是因为Angular的默认变化检测策略是“检查所有组件”,你需要使用OnPush变化检测策略和markForCheck()(我不打算进入如何在这里使用这些)使其比方法1更有效。
#1
8
If I'm not wrong, you are looking for a way to 'listen' to changes in your UserService.user
to make appropriate updates in your UserComponent
. It is fairly easy to do that with Subject
(or BehaviorSubject
).
如果我没有错,那么您正在寻找一种方法来“监听”UserService.user中的更改,以便在UserComponent中进行适当的更新。使用Subject(或BehaviorSubject)很容易做到这一点。
-In your UserService
, declare a property user
with type Subject<User>
.
- 在UserService中,声明类型为Subject
user: Subject<User> = new Subject();
-Expose it to outside as observable:
- 将其暴露在外面作为可观察的:
user$: Observable<User>
...
this.user$ = this.user.asObservable();
-Login function will update the private user
Subject.
-Login函数将更新私有用户Subject。
login(userName: string, password: string) {
//...
this.user.next(new User("First name", "Last name"));
}
-In your UserComponent
, subscribe to UserServive
's user$ observable
to update view.
- 在UserComponent中,订阅UserServive的用户$ observable以更新视图。
this.userService.user$.subscribe((userData) => {this.user = userData;});
-In your view, simply use string interpolation:
- 在您看来,只需使用字符串插值:
{{user?.firstName}} {{user?.lastName}}
Here is the working plunker: http://plnkr.co/edit/qUR0spZL9hgZkBe8PHw4?p=preview
这是工作的插件:http://plnkr.co/edit/qUR0spZL9hgZkBe8PHw4?p = preview
#2
4
There are two rather different approaches you could take:
您可以采取两种不同的方法:
1. Share data via JavaScript reference types
1.通过JavaScript引用类型共享数据
If you create an object in your UserService
如果在UserService中创建对象
@Injectable()
export class UserService {
public user = new User();
you can then share that object just by virtue of it being a JavaScript reference type. Any other service or component that injects the UserService will have access to that user
object. As long as you only modify the original object (i.e., you don't assign a new object) in your service,
然后,您可以通过它作为JavaScript引用类型来共享该对象。注入UserService的任何其他服务或组件都可以访问该用户对象。只要您在服务中仅修改原始对象(即,您不指定新对象),
updateUser(user:User) {
this.user.firstName = user.firstName;
this.user.lastName = user.lastName;
}
all of your views will automatically update and show the new data after it is changed (because of the way Angular change detection works). There is no need for any Angular 1-like watchers.
所有视图都会在更改后自动更新并显示新数据(因为Angular更改检测的工作方式)。没有任何类似Angular 1的观察者。
Here's an example plunker.
这是一个示例的plunker。
In the plunker, instead of a shared user
object, it has a shared data
object. There is a change data button that you can click that will call a changeData()
method on the service. You can see that the AppComponent's view automatically updates when the service changes its data
property. You don't have to write any code to make this work -- no getter, setter, Input, Output/EventEmitter, or Observable is required.
在plunker中,它具有共享数据对象,而不是共享用户对象。您可以单击一个更改数据按钮,该按钮将调用服务上的changeData()方法。您可以看到AppComponent的视图在服务更改其数据属性时自动更新。您不必编写任何代码来使其工作 - 不需要getter,setter,Input,Output / EventEmitter或Observable。
The view update automatically happens because (by default) Angular change detection checks all of the template bindings (like {{data.prop1}}
) each time a monkey-patched asynchronous event fires (such as a button click).
视图更新自动发生,因为(默认情况下)角度更改检测每次启动猴子修补的异步事件(例如按钮单击)时都会检查所有模板绑定(如{{data.prop1}})。
2. "Push" data using RxJS
2.使用RxJS“推送”数据
@HarryNinh covered this pretty well in his answer. See also Cookbook topic Parent and children communicate via a service. It shows how to use a Subject to facilitate communications "within a family".
@HarryNinh在他的回答中说得很清楚。另请参阅Cookbook主题父级和子级通过服务进行通信。它显示了如何使用主题来促进“在一个家庭中”的通信。
I would suggest using a BehaviorSubject instead of a Subject because a BehaviorSubject has the notion of "the current value", which is likely applicable here. Consider, if you use routing and (based on some user action) you move to a new route and create a new component, you might want that new component to be able check the "current value" of the user. You'll need a BehaviorSubject to make that work. If you use a regular Subject, the new component will have no way to retrieve the current value, since subscribers to a Subject can only get newly emitted values.
我建议使用BehaviorSubject而不是Subject,因为BehaviorSubject具有“当前值”的概念,这可能适用于此处。考虑一下,如果您使用路由并(基于某些用户操作)移动到新路由并创建新组件,您可能希望该新组件能够检查用户的“当前值”。你需要一个BehaviorSubject才能使它工作。如果使用常规Subject,则新组件将无法检索当前值,因为Subject的订阅者只能获取新发出的值。
So, should we use approach 1. or 2.? As usual, "it depends". Approach 1. is a lot less code, and you don't need to understand RxJS (but you do need to understand JavaScript reference types). Approach 2. is all the rage these days.
那么,我们应该使用方法1.还是2.?像往常一样,“这取决于”。方法1.代码少得多,您不需要了解RxJS(但您需要了解JavaScript引用类型)。方法2.这些天风靡一时。
Approach 2. could also be more efficient than 1., but because Angular's default change detection strategy is to "check all components", you would need to use the OnPush
change detection strategy and markForCheck()
(I'm not going to get into how to use those here) to make it more efficient than approach 1.
方法2.也可以比1.更有效,但是因为Angular的默认变化检测策略是“检查所有组件”,你需要使用OnPush变化检测策略和markForCheck()(我不打算进入如何在这里使用这些)使其比方法1更有效。