Reputation: 3483
I am slowing migrating from AngularJS to Angular(2+) and just stumbled across something.
In AngularJS, I used a lot of factories and services for sharing data across directives, controllers and other services. It is easy to update the service in one place and have it update everywhere else automatically.
However, I am trying to use a service in a similar manner in Angular 5 and "nothing is happening" when I change the service variables.
I've seen some solutions that involve creating functions that "pull" the new data or suggestions to update the "Angular change service" to bind events to the variable.
However, my app has many variables used in many locations. It doesn't seem correct that I have to subscribe to every single variable separately, within every single component that uses them, and change the service to emit a change for every single one.
Am I just missing something?
Thanks!!! Wayne
For Example:
A component for a home page.
import { Component, OnInit } from '@angular/core';
import { HomeButtonDirective } from '../home-button.directive';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
The directive for a button that is reused on the home page:
import { Directive, Input, OnInit, HostListener } from '@angular/core';
import { InfoService } from './info.service';
@Directive({
selector: '[appHomeButton]',
providers: [InfoService]
})
export class HomeButtonDirective {
@HostListener('click', ['$event']) onclick($event) {
this.info.showHome = false;
}
constructor(private info: InfoService) { }
ngOnInit() {
}
}
The app component. Will show the home page if showHome===true:
import { Component } from '@angular/core';
import { InfoService } from './info.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
providers: [InfoService]
})
export class AppComponent {
title = 'Testing a service';
showHome = true;
constructor(private info: InfoService) {
this.showHome = this.info.showHome; // this works...showHome becomes false per the service (see below)
}
}
And finally, the service:
import { getTestBed } from '@angular/core/testing';
import { Injectable } from '@angular/core';
@Injectable()
export class InfoService {
showHome = false;
}
Upvotes: 1
Views: 6083
Reputation: 5284
I had the same observations when migrating from AngularJS to Angular.
I'll use some sample code to describe the problem. Let's say we're building a CommentComponent
to show comments, and the data is coming from a service named CommentService
. We would then write something along the lines of:
this.comments: Array<Comment> = this.commentsService.commments;
This works perfectly fine during initialization. However, when data changes in the service (for instance when a new comment is pushed to the array), we won't see the changes reflect to our component. This is because the code above is just a direct (one-time) assignment inside the constructor, meaning there is no two-way binding.
There are multiple solutions to tackle the abovementioned problem. I'll share the ones that I'm aware of, but feel free to expand the answer.
Using Observables
In this case I prefer using BehaviorSubject as it will immediately return the initial value or the current value on subscription.
private comments$: BehaviorSubject<Comment> = new BehaviorSubject([]);
... and subscribe to the observable in your component or whatever place you need the changes to be reflected:
this.commentsService.subscribe(comments => this.comments = comments);
Referencing from templates
Another solution (or workaround) is to reference to the service property directly from the template, which allows for two-way binding:
<div *ngFor="let comment of commentsService.comments">
{{ comment | json }}
</div>
Change Detection Strategy
Another solution might be to adjust the Change Detection Strategy.
Upvotes: 1
Reputation: 12036
It is easy to update the service in one place and have it update everywhere else automatically.?
Register the service with an Angular module rather than a component.
from Angular docs
Angular module providers
(@NgModule.providers)
are registered with the application's root injector. Angular can inject the corresponding services in any class it creates. Once created, a service instance lives for the life of the app and Angular injects this one service instance in every class that needs it.
To summarize
If we want an instance of a dependency to be shared globally and share state
across the application we configure it on the NgModule.
If we want a separate instance of a dependency to be shared across each instance of a component and it’s children we configure it on the components providers
property.
Upvotes: 5
Reputation: 332
in my experience, the variable you said may be an object, and you change the value of its property but itself.
you should change the object itself instead of changing his property.
so, if you want to change a complex object of a service, you'd better code looks like this:
this.oldObject = anotherObject
duplicate the old object to a new one if necessary.
Hope it's helpful.
Upvotes: 0