I have a service to handle some data-fetching and I am trying to use tsyringe with it, the function to create the service gets called multiple times (i cant really do anything about that), so it is creating many instances of the service. I tried to wrap it in "container.isRegistered" checks, but when I do that it doesn't register the service at all.
What I have so far is:
#singleton()
#injectable()
export class Service implements IService {
constructor(#inject('arg1') arg1: string, #inject('arg2') arg2: string) {
}
Then I am registering it here in another file:
if (!container.isRegistered('arg1', true)) {
container.register('arg1', {useValue: this.arg1});
}
if (!container.isRegistered('arg2', true)) {
container.register('arg2', {useValue: this.arg2});
}
if (!container.isRegistered('IService', true)) {
container.register('IService', {useClass: Service});
}
In another file I resolve it like so:
this.service = container.resolve('IService');
Trying to check if it is registered stops the Service from being registered at all, but if I don't I have multiple instances running. The service mainly handles data-fetching, it's only supposed to fetch some data once on startup but I am seeing many calls that do so. I put some logs in the service constructor and I can see it being created many times.
Thanks for your help!
When you are creating new registry in IoC using method register, in fact you create another link on service that doesn't related to your decorator #singlton inside of class. You need set additional parameter in the register method. It will describe lifecycle of injectable object. Because be default it doesn't use Singleton. You should do this like that:
import { container, autoInjectable, Lifecycle } from 'tsyringe';
...
container.register<UsersService>('IService', {useClass: UsersService}, { lifecycle: Lifecycle.Singleton } );
More info here and types here
Related
I am gradually improving a codebase that originally had some AngularJs in various versions and some code that was not in a framework at all using various versions of a software API. (For some reason this API is available - to pages loaded through the application - on AngularJS's $window.external...go figure.)
In my pre-ES6, AngularJs 1.8 phase, I have three services that interact with the software's API (call them someAPIget, someAPIset, and someAPIforms). Something like this:
// someAPIget.service.js
;(function () {
var APIget = function ($window, helperfunctions) {
function someFunc (param) {
// Do something with $window.external.someExternalFunc
return doSomethingWith(param)
}
return {
someFunc: someFunc
}
}
angular.module('someAPIModule').factory('someAPIget', ['$window', 'helperfunctions', someAPIget])
})()
I then had a service and module a level up from this, with someAPIModule as a dependency, that aggregated these functions and passed them through under one name, like this:
// apiinterface.service.js
;(function () {
// Change these lines to switch which API service all functions will use.
var APIget = 'someAPIget'
var APIset = 'someAPIset'
var APIforms = 'someAPIforms'
var APIInterface = function (APIget, APIset, APIforms) {
return {
someFunc: APIget.someFunc,
someSettingFunc: APIset.someSettingFunc,
someFormLoadingFunc: APIforms.someFormLoadingFunc
}
}
angular.module('APIInterface').factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
})()
I would then call these functions in various other controllers and services by using APIInterface.someFunc(etc). It worked fine, and if we switch to a different software provider, we can use our same pages without rewriting everything, just the interface logic.
However, I'm trying to upgrade to Typescript and ES6 so I can use import and export and build some logic accessible via command line, plus prepare for upgrading to Angular 11 or whatever the latest version is when I'm ready to do it. So I rebuilt someAPIget to a class:
// someAPIget.service.ts
export class someAPIget {
private readonly $window
private readonly helperfunctions
static $inject = ['$window', 'helperfunctions']
constructor ($window, helperfunctions) {
this.$window = $window
this.helperfunctions = helperfunctions
}
someFunc (param) {
// Do something with this.$window.external.someExternalFunc
return doSomethingWith(param)
}
}
}
angular
.module('someAPImodule')
.service('someAPIget', ['$window', 'helperfunctions', someAPIget])
Initially it seemed like it worked (my tests still pass, or at least after a bit of cleanup in the Typescript compilation department they do), but then when I load it into the live app... this.$window is not defined. If, however, I use a direct dependency and call someAPIget.someFunc(param) instead of through APIInterface.someFunc(param) it works fine (but I really don't want to rewrite thousands of lines of code using APIInterface for the calls, plus it will moot the whole point of wrapping it in an interface to begin with). I've tried making APIInterface into a class and assigning getters for every function that return the imported function, but $window still isn't defined. Using console.log statements I can see that this.$window is defined inside someFunc itself, and it's defined inside the getter in APIInterface, but from what I can tell when I try to call it using APIInterface it's calling it without first running the constructor on someAPIget, even if I make sure to use $onInit() for the relevant calls.
I feel like I am missing something simple here. Is there some way to properly aggregate and rename these functions to use throughout my program? How do alias them correctly to a post-constructed version?
Edit to add: I have tried with someAPIget as both a factory and a service, and APIInterface as both a factory and a service, and by calling APIInterface in the .run() of the overall app.module.ts file, none of which works. (The last one just changes the location of the undefined error.)
Edit again: I have also tried using static for such a case, which is somewhat obviously wrong, but then at least I get the helpful error highlight in VSCode of Property 'someProp' is used before its initialization.ts(2729).
How exactly are you supposed to use a property that is assigned in the constructor? How can I force AngularJS to execute the constructor before attempting to access the class's members?
I am not at all convinced that I found an optimal or "correct" solution, but I did find one that works, which I'll share here in case it helps anyone else.
I ended up calling each imported function in a class method of the same name on the APIInterface class, something like this:
// apiinterface.service.ts
// Change these lines to switch which API service all functions will use.
const APIget = 'someAPIget'
const APIset = 'someAPIset'
const APIforms = 'someAPIforms'
export class APIInterface {
private readonly APIget
private readonly APIset
private readonly APIforms
constructor (APIget, APIset, APIforms) {
this.APIget = APIget
this.APIset = APIset
this.APIforms = APIforms
}
someFunc(param: string): string {
return this.APIget.someFunc(param)
}
someSettingFunc(param: string): string {
return this.APIset.someSettingFunc(param)
}
someFormLoadingFunc(param: string): string {
return this.APIforms.someFormLoadingFunc(param)
}
}
angular
.module('APIInterface')
.factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
It feels hacky to me, but it does work.
Later Update:
I am now using Angular12, not AngularJS, so some details may be a bit different. Lately I have been looking at using the public-api.ts file that Angular12 generates to accomplish the same thing (ie, export { someAPIget as APIget } from './filename' but have not yet experimented with this, since it would still require either consolidating my functions somehow or rewriting the code that consumes them to use one of three possible solutions. It would be nice not to have to duplicate function signatures and doc strings however. It's still a question I'm trying to answer more effectively, I will update again if I find something that really works.
I have a service and I have 2 solutions from this kind of problem and I want to know what is best and when to use the Subject solution above the Service solution.
I have a UserModel that all my components see with my service, the approach that I want is when I change the UserModel from service, changes it for all my application
1 FIRST SERVICE
export class UserService {
private userModel: UserModel = new UserModel();
public userSubject$ = new Subject<any>();
private timeOut = 20000;
private mainConfig: MainConfig;
constructor(private http: HttpClient) {
this.mainConfig = new MainConfig();
}
getUserModel() {
return this.userModel;
}
setUserModel(user) {
this.userModel = user
}
}
And is just to make this call in my HTML from all my components and will work
this.userService.getUserModel().name
The second approach
2 SECOND SERVICE
#Injectable()
export class UserService {
private userModel: UserModel = new UserModel();
public userSubject$ = new Subject<any>();
private timeOut = 20000;
private mainConfig: MainConfig;
constructor(private http: HttpClient) {
this.mainConfig = new MainConfig();
}
getUserModel() {
return this.userModel;
}
setUserModel(user) {
this.userSubject$.next(this.userModel = user);
}
}
And in my HTML file, I just use
{{ userModel.name }}
And I must make this new line on my example-component.ts
ngOnInit
this.subTemp = this.userService.userSubject$.subscribe(
user => this.userModel = user
);
ngOnDestroy
this.subTemp.unsubscribed();
What is the advantage to make the Subject from direct from Service? Because I need to make much more work
If I could paraphrase your question(s), I'm guessing it'd go something like:
Why should I use Angular Services instead of just making async/http calls directly from the component?/Why should I write Service logic in a separate file as a dependency?
and
Why should I use lifecycle methods like ngOnInit and ngOnDestroy in conjunction with Services or async/http calls?
When it comes to questions like these, the Angular framework is more opinionated than other SPA technologies like React, Vue, etc. So while you're not technically forced to follow either of the approaches you listed, you should know of the downsides and problems that emerge if you follow the first approach rather than the traditional injectable Service approach(number 2).
Generally speaking, the Angular team recommends following a unidirectional data flow pattern in your app implemented with Services. This means that data flow should generally come from Services which distribute the data to components and then to view templates.
Within this pattern, there's also an implication of separation of concerns which is a good practice to follow within any app. Services should handle fetching and handling data, components should handle view logic, and templates should be as clean and declarative as possible. Components and their templates should consume data that's been processed already. Relatedly, you should try to keep your components as pure as possible - meaning they produce as few side effects as possible. This is because components are dynamically mounted and unmounted in the course of a user session. Have a look at this article for more information on pure components.
Aside from the above architectural discussion of Services there are some other, more concrete consequences to be aware of:
Failure to unsubscribe from observables can lead to memory leaks in your application. With the first scenario you've outlined above, a component may be loaded 10-20 times in a user session and each time you're setting up a new subscription without tearing it down again. This can have a very real performance impact on your app.
The Angular compiler is optimized to add and remove dependencies dynamically, resulting in better app performance. If you keep all your Service code right in your component, they'll be larger and slower. From a UX perspective, components should be as light and nimble as possible so they can load quickly for the user.
If you register a service as a provider, the Angular compiler will treat it as a singleton meaning there can be only one instance of it. This is as opposed to the many instances of a Service class generated with each component if you were to use the first approach you listed. This is another performance benefit of using injectable Services.
The Angular compiler is optimized to work with the DI framework so your next step may be to learn more about it and the implications of going with one approach or the other. There's a long talk about creating your own Angular Compiler that's a couple years old now that might be helpful.
What you wish to know is the difference between pull based method vs push based method of retrieving data.
Method 1: pull based
As the name suggests the pull based method is traditional method where you for eg. call a function and it returns the value once. If you need the value again, the function should be called again. And you exactly when the data will arrive.
export class UserService {
private userModel: UserModel = new UserModel();
getUserModel() {
return this.userModel;
}
setUserModel(user) {
this.userModel = user
}
}
some.component.ts
export class SomeComponent implements OnInit {
userModel: UserModel;
constructor(private _userService: UserService) { }
ngOnInit() {
// It's a one time call and you control when you get (or `pull`) the data
this.userModel = this._userService.getUserModel();
}
}
Method 2: push based
Here the observable decides when you receive the data. This is the basic of reactive/asynchronous data flow. You subscribe to the data source and wait till it pushes the data. You have no knowledge when the result might arrive.
#Injectable()
export class UserService {
public userSubject$ = new Subject<any>();
getUserModel() {
return this.userSubject$.asObservable();
}
setUserModel(user) {
this.userSubject$.next(this.userModel = user);
}
}
some.component.ts
export class SomeComponent implements OnInit, OnDestroy {
userModel: UserModel;
closed$ = new Subject<any>();
constructor(private _userService: UserService) { }
ngOnInit() {
// The stream is open until closed and the service/observable decide when it sends (or `pushes`) the data
this._userService.getUserModel().pipe(
takeUntil(this.closed$) // <-- close the `getUserModel()` subscription when `this.closed$` is complete
).subscribe(
userModel => { this.userModel = userModel }
);
}
ngOnDestroy() {
this.closed$.next();
this.closed$.complete();
}
}
Angular uses observables extensively due to the nature of data flow in a typical web-application and the flexibility it provides.
For eg. the HTTP client returns an observable that you can latch on to and wait till the server returns any information. And the RxJS provides numerous operators and functions to refine and adjust the data flow.
In my angular project I currently have a service that uses http calls to retrieve data from my java code. Every 10 seconds the service calls the Java side and gets new data from it. I now have another component that needs the data in my service. I need this component to have a field called 'data' that just gets updated automatically when the service gets new information.
How can I set them up so that the service pushes the new information to my other component? I would like to be able to use {{data}} in my component's html and have that be automatically updated without having to reload the page.
My component does have the service 'Autowired' in already. So currently I can just call 'this.data = this.service.getData()' but that call is within my ngOnInit method so it only happens once, and the data field does not get updated when the service's data field gets updated.
You can create a messaging service that publishes data for subscribers or implement this functionality in your original service.
I would suggest having a separate messaging service and have relevant components or services publish/subscribe to it.
messaging.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MessagingService {
private sharedValue = new Subject<string>();
// Observable string streams
sharedValue$ = this.sharedValue.asObservable();
// Service message commands
publishData(data: string) {
this.sharedValue.next(data);
}
You would inject the service like this:
constructor(private messagingService: MessagingService ) {}
Publish to the service:
this.messagingService.publishData(sharedValue);
Subscribe to the service:
this.messagingService.sharedValue$.subscribe(
data => {
this.localSharedValue = data;
});
FROM ANSWER BELOW BY: DeborahK (who's courses on Pluralsight everyone should watch)
Actually, all you need is a getter.
Change your 'data' property to a getter and that will do the trick:
get data(): any {
return this.service.getData();
}
Angular change detection will detect any time that data is changed in
the service which will cause it to re-evaluate its bindings and call
this getter to re-get the data.
No need for a fancy service or Subject. :-)
Actually, all you need is a getter.
Change your 'data' property to a getter and that will do the trick:
get data(): any {
return this.service.getData();
}
Angular change detection will detect any time that data is changed in the service which will cause it to re-evaluate its bindings and call this getter to re-get the data.
No need for a fancy service or Subject. :-)
I want to nest a service behind another one, just as described in the FAQ and this issue
As I understood, you need the .setup property to get access to the app object, on which you can add a listener that you link to your service. So far so good.
However, the service I would need to do that on is not a custom service, on which the setup property is readily available, but a feathers-sequelize service, which seems to be built else where, the .class.js is not even present.
Searching around, I ve seen you can still access the property with the protoype, but not I am reticent in modifying it without knowing it to be something supported.
TL:DR: How to nest a feather-sequelize service behind another one?
You can extend the existing Sequelize ES6 class as documented here:
const { Service } = require( 'feathers-sequelize');
class MyService extends Service {
setup(app, path) {
this.app = app;
// Do stuff here
}
}
app.use('/todos', new MyService({
paginate: {
default: 2,
max: 4
}
}));
Let say I have a model class
class User {
name: string;
email: string;
}
and a service related to user
class UserService {
getInfo (usr: User) {
console.log(usr);
}
}
The usr: User is an input parameter for getInfo method, and no User class is created for that statement since the User just "typing" the usr. Instead, we can replace it with interface, isn't it?
Yeah, I am aware of Angular 2 official guide that suggest try to use class instead of interface
quote: "Consider using a class instead of an interface"
and I can accept it, although it is weird.
My question is, WHY WHY WHY use a class to "type" a variable, since we don't create any instance from it? We can replace the UserService class as shown below, and it works too...
class UserService {
getInfo (usr: any) { //this work too
console.log(usr);
}
}
(please correct me if I am wrong)
[#1] This will create a property that have the class content
class UserService {
usr: User = User; //usr = { name: '', email: ''}
}
[#2] This is used by Angular 2 to do dependency injection (assuming I have listed the User as provider), and in this case, it is similar to #1 as we are just copy the User to usr. (usually, we do DI on service, instead of model, since injecting model will have the same effect as "assigning" it to a variable, am I correct?)
class UserService {
constructor (private usr: User) {}
}
[#3] And this confuse me... because why we still do it since we don't need it? (because nothing is created, no usage here)
class UserService {
usr: User; //usr: any; (?)
}
class UserMoreService {
getInfo (usr: User) {console.log(usr);} //usr: any (?)
}
Lastly, may I know what is the motivation behind to create a "model" class in Angular 2 project?
We have the component to display data, and to get the data, we can ask it from related service, and the service will get it from the server.
Can you see this? we don't need a "third party" model to "hold" the data, because everything can be done without declaring a "model" class. Can please tell me when to create a "model"? I don't see any reason to create that...
It is not a must but just a suggestion.
Why?
An interface-class can be a provider lookup token in Angular dependency injection.
Source: https://angular.io/styleguide#!#03-03
Creating a class is pretty much the same work with respect to creating an interface but you can use these classes as a class-interface in your provider tokens later on.
{ provide: MinimalLogger, useExisting: LoggerService },
This can be used as a type of inheritance among Angular providers.
export abstract class MinimalLogger {
logs: string[];
logInfo: (msg: string) => void;
}
When you use a class this way, it's called a class-interface. The key benefit of a class-interface is that you can get the strong-typing of an interface and you can use it as a provider token in the way you would a normal class.
Source: https://angular.io/docs/ts/latest/cookbook/dependency-injection.html#!#class-interface
Side note: If you're sure that you aren't going to use it as a provider token or so IMO you should be using an interface since they disappear after the code is transpiled to JavaScript. Hence, they don't use your memory.
Using a class as an interface gives you the characteristics of an interface in a real JavaScript object.
Of course a real object occupies memory. To minimize memory cost, the class should have no implementation