Search code examples
javascriptangularjsecmascript-6dryangularjs-components

Keep my code DRY for `$onInit` and `$onChanges` methods


I have a piece of code that is repeated in almost all of my components:

import {deviceCommands} from "../../../core/data/Commands";


let _generalProperties = {
    'deviceName': deviceCommands.NAME.key,
    'deviceModel': deviceCommands.MODEL.key
};

export default class GeneralDeviceSettingsCtrl {
    constructor($scope) {
        let $ctrl = this;
        $ctrl.$onChanges = function (changes) {
            for(let prop in _generalProperties) {
                $ctrl[prop] = $ctrl.test.vm.data[_generalProperties[prop]];
            }
        };

        $ctrl.$onInit = function () {
            for(let prop in _generalProperties) {
                $scope.$watch(() => $ctrl.test.vm.data[_generalProperties[prop]],
                    (newValue, oldValue) => {
                        if (newValue !== oldValue) {
                            $ctrl[prop] = $ctrl.test.vm.data[_generalProperties[prop]];
                        }
                    });
            }
        };
    }
}

The only thing that is different is the _generalProperties variable, which is specific to my view.

How can I do to keep it DRY? Make a base class? use decorators?


Solution

  • I think there is plenty of different approaches to this but if you stick with classes and inheritance you can supply the _generalProperties to your parent constructor and reuse the whole implementation.

    For example:

    export class BaseSettingsComponent {
        constructor(properties){
            this._properties = properties;
        }
    
        $onInit(){ /* ... implement base component stuff*/ }
    }
    

    Then, on each of your pages that uses the same component logic you can extend the base class, provide the properties to the parent class and let the base class do the job.

    import { BaseSettingsComponent } from '../common/base-settings-component.js';
    
    let _generalProperties = {
        'deviceName': deviceCommands.NAME.key,
        'deviceModel': deviceCommands.MODEL.key
    };
    
    export class GeneralDeviceSettingsCtrl extends BaseSettingsComponent {
        constructor(){
            super(_generalProperties);
        }
    
        $onInit(){ super.$onInit(); /* ... implement base component stuff*/ }
    }
    

    The one thing important to keep in mind when using this approach is that if you need to implement $onInit or $onChanges on the child classes you have to call the super.$onInit() otherwise you lose the parent behavior due to an override.

    Finally, for an even cleaner code, you can also discard the declaration of _generalProperties supplying it directly into the super constructor.

    import { BaseSettingsComponent } from '../common/base-settings-component.js';
    
    export class GeneralDeviceSettingsCtrl extends BaseSettingsComponent {
        constructor(){
            super({
                'deviceName': deviceCommands.NAME.key,
                'deviceModel': deviceCommands.MODEL.key
            });
        }
    }