Pavel Evdokimov Pavel Evdokimov - 18 days ago 4
TypeScript Question

What is correct way to expose service property in controller?

Imagine, I have a controller ctrlA. It has a serviceB as dependency.
The service has property propertyC. I use Anguler with Typescript.

In UI, controller by the controller I want to show and hide some html based on value of that property.

I could:


  1. make service a public variable and access it like: <... ng-if="serviceB.propertyC">

  2. make a property in controller:

    public get exposedPropertyC(){ return this.serviceB.PropertyC; }



And access the property like:<... ng-if="exposedPropertyC">

What is correct way to do it?

Answer

I wouldn't say correct, but consistent. Both of them will work just fine, however things would start becoming problematic because of the lack of consistence among the responsibility of each component (it may have testing issue as well).

You should expose a property through your controller, ever. It will keep its responsibility solid as it suposed to be. And also, by doing so, your service will maintain its responsibility solid as well.

As it has been told by @LoremIpsum in the comments, another issue is "your HTML not reaching stuff that's too far".

There is a couple scenarios for this issue. Consider the controller declared by class MyController.

Direct access property

public exposedPropertyC: number;

constructor(private serviceB: ServiceB){
    this.serviceB = serviceB;
    this.exposedPropertyC = this.serviceB.PropertyC;
}

Assync aquired property

public exposedPropertyC: number;

constructor(private serviceB: ServiceB){
    this.serviceB = serviceB;
    this.serviceB.getPropertyC().then((propertyC) => {
        this.exposedPropertyC = propertyC;
    });
}

Observer pattern

A little bit of an overkill depending on your scenario, but the observer pattern is mostly an optimal and fancy solution.

The below code is a simple implementation of this pattern. However you may find more powerful libraries using this pattern with typescript (but you got the idea).

Disclaimer. The observer pattern is my favorite one, even though it looks a bit of an overkill, it's much more flexible and compatible with future changes for optimizations and on how data are retrieved from the server, i.e., it brings lots of positive stuff for maintainability and also prevent future headaches because of a poor design ;{D

interface ObservableCallBack<T> {
    (arg: T): void;
}

class Observable<T> {
    public yielded: T;
    public observers: ObservableCallBack<T>[];

    public subscribe(observableCallBack: ObservableCallBack<T>): void {
        this.observers.push(observableCallBack);
        if(this.yielded !== null) observableCallBack(this.yielded);
    }

    public notify(arg: T): void {
        this.yielded = arg;
        this.observers.forEach((observer) => {
            observer(arg);
        });
    }
}

class ServiceB {    
    public propertyC: Observable<number>;

    constructor(private http: IHttpService) {            
        this.propertyC = new Observable<number>();

        this.http.get('').then((res) => {
            this.propertyC.notify(res.data);
        });
    }
}

class MyController {    
    public propertyC: number;

    constructor(private serviceB: ServiceB) {
        this.serviceB.propertyC.subscribe((propertyC) => this.propertyC = propertyC);
    }
}
Comments