Is dependeny injection possible when using the new operator? - javascript

Actually I want to implement some sort of strategy pattern. More precisely, I want to decide on runtime which class to instanciate.
Of course this is pretty simple.
if(...) {
this.service = new ServiceA();
} else {
this.service = new ServiceB();
}
But what if ServiceA and ServiceB use dependency injection in their classes? Do I have to pass those instances or is there a nicer way to let nest inject them automatically?

You can inject both services and then assign them to your variable dynamically:
service: Service;
constructor(private serviceA: ServiceA, private serviceB: ServiceB) {}
dynamicMethod() {
if (...) {
this.service = this.serviceA;
} else {
this.service = this.serviceB;
}
}
If the condition can be evaluated on startup (e.g. environment variabels) you can use a custom provider instead.

Related

How to use InversifyJS to inject named constructor parameters?

I'm new to Inversify but want to utilise it in the codebase I'm working in. In this codebase, the dependencies are injected via an args object as a means to simulate named constructor parameters. What would be the best way to decorate this object for injection with Inversify, since I'm not able to simply do something like constructor(#inject("FooService") fooService, #inject("BarService") barService)?
interface FooService {
...
}
interface BarService {
...
}
type FooBarArgs = {
fooService: FooService;
barService: BarService;
}
#injectable()
class FooBar
{
private fooService: FooService;
private barService: BarService;
constructor(args:FooBarArgs) {
this.fooService = args.fooService;
this.barService = args.barService;
}
}

How to Inject dependencies inside a class

I have 2 services. CarsController and CarsFetcherService
I also have a class called Car.
The CarsController has a method createCar() that creates an instance of Car everytime it is called.
Each Car instance needs to be provided with the CarsController service.
With the code below, each Car instances gets undefined instead of the CarsController dependency.
I use the library typedi to illustrate the problem I want to know in general how this is done. An answer with other library link inversify or Nestjs or even Angular would also be helpful.
export class Car {
#Inject() // this is not working here
private carsController: CarsController; // this becomes undefined
private carRecipe: string;
constructor(carRecipe: string) {
this.carRecipe = carRecipe;
this.carsController.notifyNetwork('HelloWorld, I am created!');
}
}
#Service()
export class CarsController {
#Inject()
private carsNetworkService: CarsFetcherService;
private cars: Car[] = [];
constructor() {
}
createCar() {
const carRecipe = this.carsNetworkService.getRequestSomething();
const car = new Car(carRecipe);
this.cars.push(car);
}
notifyNetwork(msg: string) {
this.carsNetworkService.postRequestSomething(msg);
}
}
#Service()
export class CarsFetcherService {
getRequestSomething() {
return 'this is how to make a car';
}
postRequestSomething(msg:string) {
console.log(msg)
}
}
const carsController = Container.get(CarsController)
// Creating 4 cars
carsController.createCar()
carsController.createCar()
carsController.createCar()
carsController.createCar()
I think, that when using typedi, and other similar DI frameworks, you should separate your classes into two categories. First categories would be services: typedi would guarantee that there's only one instance of each, typedi would create those instances and inject everything. Second category would be ordinary objects, that you create and manage on your own.
So, if you want to create as many instances of Car as you like, don't use DI for it - you already create it in a service that has direct references to everything Car needs:
export class Car {
// deleted inject decorator
constructor(private carsController, private carRecipe: string) {
this.carsController.notifyNetwork('HelloWorld, I am created!');
}
}
#Service()
export class CarsController {
#Inject()
private carsNetworkService: CarsFetcherService;
private cars: Car[] = [];
constructor() {
}
createCar() {
const carRecipe = this.carsNetworkService.getRequestSomething();
const car = new Car(this, carRecipe);
this.cars.push(car);
}
notifyNetwork(msg: string) {
this.carsNetworkService.postRequestSomething(msg);
}
}

Typescript: unable to access public variable in a function

I want to access myVariable inside init() but the variable is undefined, but I am able to access the variable inside convert(). Any suggestions what I am missing here?
export const MyComponent: ng.IComponentOptions = {
templateUrl: 'MyView.html',
bindings: {
myVariable: '<',
},
controller: common.createController(MyController)
};
export class MyController {
public myVariable: MyVariable;
constructor($scope) {
this.scope = $scope;
this.init().then(() => {
this.convert();
});
private init(): Promise<void> {
console.log("init(): ", this.myVariable); //Error --> undefined
//Call REST API and return a promise
}
private convert(): void {
console.log("convert(): ", this.myVariable); //No Error
}
}
Generally this happens because init() is being called before the base constructor - the base constructor is usually what sets the variable in view libraies.
I don't know what view library you're using but it looks like angular. In this case, angular will set the binding after your constructor runs (it needs to create the class before it can assign the binding, and since it doesn't provide a base class it can't utilize that for binding in the constructor)
The Angular API exposes life-cycle methods similar to react, you need the $onInit hook which will be called after the constructor and binding has finished.
You can read more here https://docs.angularjs.org/guide/component

Static function inheritance

I've created a few classes as Controllers for my routes in Node.js and I'd like them to be able to work on a singleton basis (not required, it's an extra)
Main Controller class
class RouteController {
static getInstance() {
if (!RouteController.singleton) {
RouteController.singleton = new RouteController();
}
return RouteController.singleton;
}
}
Now I want to create a route for example viewing posts and I want that class to have the singleton pattern as well, but not cop
PostController class
class PostController extends RouteController {
}
If I do this and check it console.log(RouteController.getInstance() === PostController.getInstance()) it returns true when I want each (sub)class to have their own singleton instance.
How can I do this correctly?
One simple way would be to see if your singleton property is an instance of this. This will work even if you call RouteController.getInstance() before doing so on any of the derived classes.
class RouteController {
log() {
return 'Route';
}
static getInstance() {
if (!(this.singleton instanceof this)) {
// Only called twice so you can verify it's only initialized
// once for each class
console.log('Creating singleton...');
this.singleton = new this();
}
return this.singleton;
}
}
class PostController extends RouteController {
log() {
return 'Post';
}
}
console.log(RouteController.getInstance().log());
console.log(PostController.getInstance().log());
console.log(RouteController.getInstance().log());
console.log(PostController.getInstance().log());
The normal rules of functions apply. Hence you can use this inside the function to refer to the object the method was invoked on (the constructor function itself in this case):
class RouteController {
static getInstance() {
if (!this.singleton) {
this.singleton = new this();
}
return this.singleton;
}
}
However, this will still not work if RouteController.getInstance() is called directly, because then all subclasses inherit a static singleton property, causing the !this.singleton test to fail for each subclass.
There are multiple ways to solve this. One would be to use getOwnProperty instead of accessing it directly:
if (!this.getOwnProperty('singleton')) {
}

Angular 2.x watching for variable change

I'm migrating from angular 1.x to 2.x but my brains still think in angular 1.x so sorry for silly questions.
What I need is to take some action when one of my scope variables component properties changes. I found a solution but I think there should be better solution
export class MyApp {
router: Router;
location: Location;
fixed: boolean = true;
private set isFixed(value:boolean) {
this.fixed = value;
//TODO: look here
console.log('isFixed changed', value);
}
private get isFixed():boolean {
return this.fixed;
}
constructor(router: Router, location: Location) {
this.router = router;
this.location = location;
}
}
Look at the line console.log('isFixed changed', value); It's what I need and it's working. But I made it by declaring getter and setter, but isn't there a better solution to watch variables? Like in angular 1.x was $scope.$watch?
I think my component code should look like
export class MyApp {
router: Router;
location: Location;
isFixed: boolean = true;
//TODO: $watch for isFixed change {
console.log('isFixed changed', value);
// }
constructor(router: Router, location: Location) {
this.router = router;
this.location = location;
}
}
You might want to implement the OnChanges interface and implement the ngOnChanges() method.
This method is called whenever one of the components input or output binding value changes.
See also https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
Dart code example
#Input() bool fixed;
#override
void ngOnChanges(Map<String, SimpleChange> changes) {
print(changes);
}
You might find this answer to Delegation: EventEmitter or Observable in Angular2 helpful (worked for me).
Essentially you could use a BehaviorSubject, which allows you to set an initial value for the property you're interested in, then subscribe to changes to that property wherever that service is injected.
e.g.
export class SomeService {
private fixed = new BehaviorSubject<boolean>(true); // true is your initial value
fixed$ = this.fixed.asObservable();
private set isFixed(value: boolean) {
this.fixed.next(value);
console.log('isFixed changed', value);
}
private get isFixed():boolean {
return this.fixed.getValue()
}
constructor(router: Router, location: Location) {
this.router = router;
this.location = location;
}
}
Then in a class (e.g. Component) that's interested in the fixed value:
export class ObservingComponent {
isFixed: boolean;
subscription: Subscription;
constructor(private someService: SomeService) {}
ngOnInit() {
this.subscription = this.someService.fixed$
.subscribe(fixed => this.isFixed = fixed)
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Update value:
export class Navigation {
constructor(private someService: SomeService) {}
selectedNavItem(item: number) {
this.someService.isFixed(true);
}
}
To auto get updated value by this service
NOTE: I tested it in Angular 9
my service file
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SharedService {
private fixed= new BehaviorSubject<boolean>(false);
fixed$ = this.fixed.asObservable();
constructor() {}
updateFixedValue(value: boolean) {
this.fixed.next(value);
console.log('fixed changed', value);
}
}
Now you can get value in any component (within ngOnInit or anywhere you want) like below
NOTE: this value will be change automatically after update
this.sharedService.fixed$.subscribe(val=>{ this.isFixed = val; });
and you can update or set new value from any component like below
this.sharedService.updateFixedValue(your_boolean_value);
Thanks, I hope it's work for you.
See Angular2 Component Interaction (has code examples).
The short answer to your question is that it really just depends on what you are trying to do. Even then, there are multiple ways to do what you want to do even if it's not really intended for it. So, I think it's best if you just take a few minutes to look at their documentation about Component Interaction and Forms.
My personal preference is to use events when a property has changed. The ngOnChanges event can be used for this but I prefer to work with #Input and #Output, and form value changed events (Angular2 Forms).
Hope this helps and gives you a direction you want to take.
I think it is possible to use simple getter and setter for this purpose.
class Example {
private _variable: string = "Foo";
set variable(value: string) {
this._variable = value;
console.log("Change detected: ", this.variable);
}
get variable(): string {
return this._variable;
}
}
let example = new Example();
console.log(example.variable);
example.variable = "Bar";
console.log(example.variable);
And output will be:
Foo
Change detected: Bar
Bar

Categories

Resources