Search code examples
angularangular2-observables

Best practice for angular 2 component


I have an application with a user object where all the user information is stored and is updated with an observable that is given from the userService. Now am I wondering what the best practice is for using that user observable with components?

I have a page (TestPage) with a calendar component (Calendar). The calendar makes use of the user object because it must know of it has the show the week view or the month view.

Option 1: The first option is to create one observable in the TestPage and gives the user object with a property and then in the Calendar, it gets the User with @input.

TestPage typescript file:

export class TestPage {
    private user: User;
    private userSubscription: Subscription;

    constructor( private usersService: UsersService) {
        this.log('constructor');
        this.userSubscription = this.usersService.$currentUser.subscribe((user: User) => {
            this.log('$currentUser: user update', user);
            this.user = user;
        });
    }

    ngOnDestroy(): void {
        this.log('ngOnDestroy: unsubscribing userSubscription');
        this.userSubscription.unsubscribe();
    }
}

TestPage html:

<flex-calendar [user]="user"></flex-calendar>

Calendar:

export class Calendar {
    @Input() user: User; // Got the user from the TestPage
}

Option 2: Create an observable in the TestPage and in the Calendar. This option has the advantage that the Calendar has less connection with the page.

Calendar:

export class Calendar {
    private user: User;
    private userSubscription: Subscription;

    constructor( private usersService: UsersService) {
        this.log('constructor');
        this.userSubscription = this.usersService.$currentUser.subscribe((user: User) => {
            this.log('$currentUser: user update', user);
            this.user = user;
        });
    }

    ngOnDestroy(): void {
        this.log('ngOnDestroy: unsubscribing userSubscription');
        this.userSubscription.unsubscribe();
    }
}

Can someone explain what the best option would be and why?


Solution

  • An alternate option I have used to success a few times now is to hand a config class/object which can contain a callback function. This way you are getting the best of both worlds; the Calendar Component can subscribe directly to the service and yet remain dumb.

    It would look something like this:

    TestPage

    ----Template----
    <flex-calendar [calendarCallback]="calendarCallback"></flex-calendar>
    
    ----TS----
    export class TestPage implements OnInit {
        private calendarCallback: Function;
        private userSubscription: Subscription;
    
        constructor( private usersService: UsersService) {
            this.log('constructor');
        }
    
        ngOnInit(): void {
            this.calendarCallback = this.getUser.bind(this);
        }
    
        getUser(){
            let currentUser = new Subject<User>();
            this.usersService.$currentUser
                .subscribe(
                    (user: User)=> {
                        this.log('$currentUser: user update', user);
                        currentUser.next(user);
                    }
                );
            return currentUser.asObservable();
        }
    }
    

    Calendar

    ----TS----
    export class Calendar implements OnInit{
        private calendarCondition;
        @Input("calendarCallback") calendarCallback;
    
        getCalendarDependancy(){
            this.calendarCallback.subscribe(
                (result) => {
                    this.calendarCondition = result;
                }
            )
        }
    }
    

    It is best, as Chandermani stated, to keep components as dumb as possible. With this said the main advantage of this approach is that the calendar component is no longer dependent on the parent component to provide the result of the subscription, which isn't a problem in the example as it stand, but may become one if say the lookup is triggered within the calendar component itself (on click for example).

    Regards