Angular 2, How to Share Array Data Between components using service? - javascript

In Angular2, Share Array data between components using service?
I'm designed this structure like below.
object structure
{
data: 'data'
keys: ['key1', 'key2', ... , 'keyN']
}
* Service has a array of total objects.
totalObject = [object1, object2, ... , objectN]
At first I initialized selectedObject of service like this.
selectedObject = totalObject;
Then I initialized selectedObject of Component B on constructor like this.
constructor(private wordService: WordService) {
this.words = wordService.selectedWords;
}
At the first, Component B displayed All objects correctly!!
But, when the service initialize new array to selectedObject, Component B cannot display selected objects.
// It's not working...
// remove
this.selectedWords.splice(0, this.selectedWords.length);
// add
for(let i in WORDS) {
if(WORDS[i].keys.indexOf(key) >= 0) {
this.selectedWords.push(WORDS[i]);
}
}

You can simply create a service and use it as a "singleton" service.
#Injectable()
export class DataService {
public selectedWords:string[] = [];
}
And you provide it at the top level of your application, this way only one instance will be used across your app:
#NgModule({
imports: [ BrowserModule ],
declarations: [ App, OtherComponent ],
bootstrap: [ App ],
providers: [ DataService ]
})
export class AppModule {}
Plunkr example

If I understand what you're trying to do, you are trying to manipulate an object, by reference, across two components, with a service as sort of a broker, such that changes by component A to Object X will be visible in component B. The service more or less acts as just a place to stash references.
You will achieve a lot more stability, make it easier to debug, and make it a lot more extensible, thinking this way:
Component A makes change to Object X (which it houses itself).
Component A updates model in Service (which as several people here say, acts as a singleton, or more technically correct, a "managed instance"), with a copy of Object X. The model/service now has the data, but that data has no external reference that can accidentally (or otherwise) mutate it.
When necessary, Service dispatches a "dirty data" notification, which Component BCDE...etc. is listening for. This notification contains the new data (this is "push").
Component BCDE...etc. uses that data. It is not reliant on a reference outside of it's control concern and it is not tightly coupled to that service.
Any other component that needs to modify data used by other components, just uses the same mechanism.
Any other component that wants to get the data on demand from the service, can grab a copy of it from a getter on that service (this is "pull").
I have tried to do what you're doing (pretty sure we all have). Over time it's just trouble (especially if you throw more components into the mix). Using notifications/events is more staightforward all around, even if it might seem more complex to initially set up. It's easier to test since you just test with the payload from a notification/event (easily triggered in a test), you don't have to set up the other component and have it modify the service/reference used in the target component.
But yeah, the whole "one reference on a singleton everything is looking at" thing is just trouble.

Related

lb4 call another repository in different repository

What is the best way of calling different repository at the main repository? I tried to use Services. For example
...
#injectable({scope: BindingScope.TRANSIENT})
export class ProjectService {
constructor(#repository(ProjectRepository) public projectRepository: ProjectRepository) { }
}
Here is my service code. When I define this service at another repoository, I am able to access projectRepository. But I am not sure this is the best way.
We can take a page from Relations [1] and utilize #repository.getter() [2] to perform dependency injection in the main Repository:
import {Getter} from '#loopback/core';
import {DefaultCrudRepository, repository} from '#loopback/repository';
export class MainRepository() extends DefaultCrudRepository</*...*/>{
constructor(
#repository.getter(ProjectRepository)
private _projectRepositoryGetter: Getter<ProjectRepository>;
)
async yourFunction(): Promise<void> {
let projectRepository = await this._projectRepositoryGetter();
// `projectRepository` now contains an instance of ProjectRepository.
}
}
In the example above, we're utilizing constructor injection and retrieving the ProjectRepository instance in an arbitrary function called yourFunction. However, you can use any flavour of dependency injection [3] and retrieve the instance in any function within the same closure.
Why getters?
Getters to prevent circular dependencies. These are especially prevalent in Repositories as they may have Relations that interconnect between each other. Getters achieve this by delaying the binding resolution until explicitly requested by the LB4 application.
When to use Services?
Services can be considered a higher-level abstract than Repositories. They aren't expected to implement a standard CRUD or KV interface and aren't assumed to be linked to a DataSource. This is useful if you need logic that are out-of-scope of Repositories. For example, a Service may implement a function that adds a new database record and uploads a file to Amazon S3. This keeps a clear separation between the code that interfaces with the Datastore and those that don't, thereby ensuring that the Datastore logic is kept predictable, readable and easy to test.
Links
[1] https://loopback.io/doc/en/lb4/HasMany-relation.html#configuring-a-hasmany-relation (archive)
[2] https://loopback.io/doc/en/lb4/apidocs.repository.repository.getter.html (git permalink)
[3] https://loopback.io/doc/en/lb4/Dependency-injection.html#flavors-of-dependency-injection (git permalink)

Can't we use plain Ember Objects in place of Ember Mixins?

Basically I couldn't understand the difference between Ember Object and an Ember Mixin.
If Ember.Mixin.create() is itself extended from Ember.Object then why don't we mix objects created by Ember.Object.create() instead of Ember.Mixin.create()?
Why dont we use Ember objects like this
let TestObject = Ember.Object.create({
init() {
this._super(...arguments);
this.set('list', Ember.A([]));
}
});
let TestComponent = Ember.Component.extend(TestObject, {});
Instead of using a Ember mixin like below
let TestMixin = Ember.Mixin.create({
init() {
this._super(...arguments);
this.set('list', Ember.A([]));
}
});
let TestComponent = Ember.Component.extend(TestMixin, {});
So I don't want to go into the historical aspects of mixins, other than you shouldn't really use them, and specifically your example will lead to many bugs because of the way state is shared between mixins.
In your mixin example would actually share state between different component instances when mutating the list.
Really, your best bet is to pretend that mixins never existed in the first place.
EDIT: Discussion on mixin depreciation https://github.com/emberjs/rfcs/issues/534

NativeScript: Difference between a class and a service?

I'm trying to get into Nativescript + Angular2, and I read the following in the tutorial:
We’ll build this functionality as an Angular service, which is Angular’s mechanism for reusable classes that operate on data.
What they then do is to create a simple class, like this:
import { Injectable } from "#angular/core";
import { User } from "./user";
#Injectable()
export class UserService {
register(user: User) {
alert("About to register: " + user.email);
}
}
Now, I can't really see the difference between a normal class and a service - this is a very normal class definition.
So, why is it called an "Angular service"?
This creates a basic Angular service with a single method that takes an instance of the User object you created in the previous section.
Also, when using this "service" in the tutorial, it isn't clear to me when this class is instantiated - when is the construction executed? Is the object saved in memory for later use? The only call to the "userservice" in the tutorial is like this:
import { Page } from "ui/page";
import { Component, ElementRef, OnInit, ViewChild } from "#angular/core";
import { User } from "../../shared/user/user";
import { UserService } from "../../shared/user/user.service";
import { Router } from "#angular/router";
import { Color } from "color";
import { View } from "ui/core/view";
#Component({
selector: "my-app",
providers: [UserService],
templateUrl: "./pages/login/login.html",
styleUrls: ["./pages/login/login-common.css", "./pages/login/login.css"]
})
export class LoginComponent implements OnInit {
user: User;
isLoggingIn = true;
#ViewChild("container") container: ElementRef;
constructor(private router: Router, private userService: UserService, private page: Page) {
this.user = new User();
this.user.email = "bla#bla.com";
this.user.password = "1234";
}
//.... methods and stuff...
}
A class, in that context, is a regular class as in any other OO language: a "prototype" of objects which you can create instances simply using:
let myInstance = new MyClass(<arguments...>);
So, actually, an Angular service is also a class.
But consider services a special kind of class. The main difference between regular classes and service classes is their lifecycle, specially who creates their instance (who calls new).
Instances of a service are created - and managed (disposed) - by the Angular "container" (angular injector, actually).
You can also "inject" instances of service classes in constructors of other service classes (or other managed components).
A good resource in the capabilites of services is Angular's Dependency Injection Guide.
When is the construction executed?
The injector executes the construction. When? See below.
Is the object saved in memory for later use?
It could be. Depends on where you registered the service.
Before anything, know that Angular DI is a hierarchical injection system.
If you register the service with an Angular Module, the service will be created by the application's root injector. So everyone below it (aka everyone in that module) will receive the same instance of the service. In other words, Angular (will call the injector only once and) will create only one instance of the service class and pass that same instance to whoever asks for that service. And that instance will live as long as that module lives.
Now, if you register the service with a component, then the service will be registered with that component's injector. So when such component requests an instance of the service, angular will call the injector and create an instance. If any child of that component asks for an instance of such service, angular will provide the same instance. No one else, only children of the component, will receive that same instance. When that component dies, the service instance dies as well.
How does a "regular class" differ? It lacks the Injector?
The difference is not only the lack of an injector.
Angular aside, just JavaScript: you create an instance of a "regular class" by calling let instance = new MyRegularClass() somewhere in your code, right?
This instance has no "magical effects", it does nothing more than any class would (just regular JavaScript methods and properties). Example: if you need instances of other classes as arguments in the constructor, no one will "magically" create you those instances and pass them. You will have to create them manually, when calling new (e.g. new MyClass(new SomeOtherClassIDependOn(), ...)). If you want to instantiate SomeOtherClassIDependOn only once and reuse the same instance everywhere it is needed, you will have to save that instance and pass it wherever it is neeed yourself.
As services, though, angular can take some of that burden off your shoulders.
Now, before anything: since every service, deep down, is a class, someone has to call new MyServiceClass(). The difference is that someone is not you anymore. There is no new UserService() in your code. So, who is it? This someone is the Injector.
When Angular notices someone asks for a service, it calls for the injector to instantiate that service. The injector then calls let serviceInstance = new MyServiceClass(<dependencies>) and adds some "magic" to it (e.g. it can pass - inject - instances of other services to the constructor of a service), and make it available (save it) for anyone that requests that service in the scope you registered it.
Note: You can call new UserService(...) yourself, as it UserService is a class. But this instance is a regular object, not managed by angular, there is no magic (no constructor arguments will be injected, no instance is saved and reused).

Observing a property on a service from a route in EmberJS

I think I'm not understanding a concept here. As far as I know any Ember.object can observe properties on another Ember.object.
So, I have a service, a router, and a component. I need the component and the router to be able to observe a property on the service. It's entirely possible that I'm just structuring the solution in the wrong way, I'll include an overview of what I'm trying to do at the end.
Here is approximately what I have:
/services/thing-manager.js
export default Ember.Service.extend({
observedProperty: 'original value'
});
/components/thing-shower.js
export default Ember.Component.extend({
thingManager: Ember.inject.service(),
myObserver: Ember.observer(
'thingManager.observedProperty',
function() {
// This shows up as anticipated, unlike the one in the routes
console.log('THING SHOWER COMPONENT observed change on thingManager')
}
),
actions: {
changeObservedProperty: function() {
let thingManager = this.get('thingManager')
let newText = thingManager.get('observedProperty') + '!'
// here i am sure to call `set` to make sure observers fire
thingManager.set('observedProperty', newText)
}
}
});
/routes/things.js
export default Ember.Route.extend({
thingManager: Ember.inject.service(),
underObservation: Ember.observer('thingManager.observedProperty', function() {
// This is what I expect to fire, but does not.
console.log('THINGS ROUTE observed change on thingManager')
}),
});
As you can see, I'm expecting console output from both observers in the component and router. Why doesn't this work?
Twiddle here!
My Overall Goals
This is perhaps a separate question, but I'd like to know if there is a better way to accomplish what I'm trying to do. I've been learning about 'data down, actions up', which led me to this approach. I'm building a website that load a json file with a bunch of GPS coordinates and sticks them on a map.
The goal is to click a map marker, and have that load the corresponding data. This should also change the route. So, my thinking was, to keep track of my markers in a service, and when the selected marker changes, the router would observe that and transition to the next route. The component would also notice the changed property and update the map.
Thanks folks!
In things.js route file you haven't used accessed/used thing-manager service, so observer will not be triggered.
routes/thing.js
init(){
this._super(...arguments);
this.get('thingManager');
},
introducing this will make your observer to be fired.
I would say, if you are following the DDAU priniciple, then your component should not mutate the thing-manager service properties. it should send action to service and mutate it.
Note: You can have observers and computed properties inside any Ember.Object which means you have it thing-manager service too.

Proper way to bind to data object in Angular 2 service?

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.
I have created a login service using JWT's.
Once login is successful, I return a user object.
I have a loginComponent ( binds data to template ) and loginService ( which handles the https calls )
I have a userService which maintains the user object.
I have a userComponent which renders the user data.
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.
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()"
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.
Thanks in ADVANCE!
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).
-In your UserService, declare a property user with type Subject<User>.
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(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.
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
There are two rather different approaches you could take:
1. Share data via JavaScript reference types
If you create an object in your 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,
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.
Here's an example 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.
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).
2. "Push" data using 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".
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.
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.
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.

Categories

Resources