Skip to content

Instantly share code, notes, and snippets.

@lifeart
Last active March 4, 2020 01:47
Show Gist options
  • Save lifeart/d146871d896461193db6779ac5bbfc01 to your computer and use it in GitHub Desktop.
Save lifeart/d146871d896461193db6779ac5bbfc01 to your computer and use it in GitHub Desktop.
Angular Simple DI for controller ES6
class MyAngularController {
static injections() {
return ['$scope', 'lodash', '$moment', '$timeout'];
}
static ngConstruct() {
return [...this.injections().map(el=>el.split(':').pop()), this];
}
constructor(...args) {
this.constructor.injections().forEach((el,index) => this._defineKey(el,args[index]));
this.setupController(args);
}
_defineKey(key, value) {
Object.defineProperty(this, key.split(':')[0], {enumerable: true, value});
}
setupController(args) {
this.userId = 42;
this.init();
}
init() {
console.log('some init stuff');
console.log(this.$moment);
console.log(this.$scope);
console.log(this.lodash);
console.log(this.$timeout);
}
}
angular.module('app').controller('MyAngularController', MyAngularController.ngConstruct());
class AngularController {
static injections() {
return ['$scope', 'lodash', '$moment', '$timeout'];
}
static requredInjections() {
return ['lodash', '$timeout'];
}
static getInjections() {
let injections = this.injections();
this.requredInjections().map(injectionName => {
if (!injections.includes(injectionName)) {
injections.push(injectionName);
}
});
return injections;
}
static ngConstruct() {
return [...this.getInjections().map(el => el.split(':').pop()), this];
}
constructor(...args) {
this.constructor.getInjections().forEach((el, index) => this._defineKey(el, args[index]));
this._definePropertyStore();
this._loopTimeout = null;
this._runLoopsCount = 0;
this._computedItems = {};
this._needToBeRecalculated = {};
this._computedLoopMap = {};
this._computedLoopId = 0;
this._calculatedResults = {};
this._bindActionsToScope(this.setActions());
this.setupController(args);
this.setComputed('fullName', ['firstName', 'lastName'], {
get: function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
},
set: function(value) {
if (typeof value !== 'string') {
value = '';
}
value = value.trim();
let creds = value.split(' ');
this.setProperties({
firstName: creds[0] ? creds[0].trim(): '',
lastName: creds[1] ? creds[1].trim() : ''
});
return `${value}`;
}
});
this.setComputed('nickName', ['fullName'], function() {
return `${String(this.get('fullName')).split('').join(', ')}`;
});
this.setComputed('nickNameCharSumm',['nickName'],function(){
return String(this.get('nickName')).length;
});
console.log(this);
}
_defineKey(key, value) {
if (this.hasOwnProperty(key)) {
return;
}
Object.defineProperty(this, key.split(':')[0], {
enumerable: true,
value
});
}
_definePropertyStore() {
this._getScope().props = {};
}
_getScope() {
if (this.hasOwnProperty('$scope')) {
return this.$scope;
} else {
return this;
}
}
_bindActionsToScope(actionsObj = {}) {
const actions = Object.keys(actionsObj);
if (!actions.length) {
return;
}
const $scope = this._getScope();
actions.forEach(action => {
$scope[action] = actionsObj[action].bind(this);
});
}
_applyScope() {
const $scope = this._getScope();
this._recalculateComputed();
this._computedLoopId++;
if ($scope && typeof $scope.$apply === 'function') {
this.incrementProperty('_runLoopsCount');
$scope.$$phase || $scope.$digest();
} else {
this.$timeout(() => {
this.incrementProperty('_runLoopsCount');
}, 20);
}
}
_recalculateComputed() {
Object.keys(this._computedItems).forEach(computedName => {
if (this._needToBeRecalculated[computedName]) {
this.notifyPropertyChange(computedName);
}
});
}
_scheduleRecalculateComputed(name) {
if (!name) {
return;
}
Object.keys(this._computedItems).forEach(computedName => {
if (name === computedName) {
return;
}
if (this._computedItems[computedName].includes(name)) {
this._needToBeRecalculated[computedName] = true;
}
});
}
_invokeRunLoop(property) {
this._scheduleRecalculateComputed(property);
clearTimeout(this._loopTimeout);
this._loopTimeout = setTimeout(this._applyScope.bind(this), 20);
}
_getNumericProperty(name) {
let property = this.get(name, 0);
if (typeof property !== 'number') {
property = parseInt(property, 10);
if (isNaN(property) || !isFinite(property)) {
property = 0;
}
} else if (isNaN(property)) {
property = 0;
}
return property;
}
incrementProperty(name, step = 1) {
let property = this._getNumericProperty(name);
property += step;
this.set(name, property);
}
decrementProperty(name, step = 1) {
let property = this._getNumericProperty(name);
property -= step;
this.set(name, property);
}
toggleProperty(name) {
this.set(name, !this.get(name));
}
_methodNotFound(name) {
return () => {
console.log(`method "${name}" not found`);
};
}
_isComputedProperty(name) {
return this._computedItems.hasOwnProperty(name);
}
setComputed(name, args, fn) {
if (!fn) {
fn = args;
args = [];
}
args.forEach(el=>{
if (!this._getScope().hasOwnProperty(el)) {
this._defineProperty(el);
}
});
this._computedItems[name] = args;
this._needToBeRecalculated[name] = true;
this.set(`computed[${name}]`, fn);
const value = this.notifyPropertyChange(name);
this.set(name, value);
return this;
}
_isComputedInCurrentLoop(name) {
if (!this._computedLoopMap.hasOwnProperty(name)) {
return false;
}
return this._computedLoopId === this._computedLoopMap[name];
}
notifyPropertyChange(name) {
if (this._isComputedProperty(name) && !this._isComputedInCurrentLoop(name)) {
const nodes = this._computedItems[name];
nodes.forEach(el => {
this.notifyPropertyChange(el);
});
const changer = this.lodash.get(this._getScope(), `computed[${name}]`, this._methodNotFound(`computed property "${name}"`));
this._calculatedResults[name] = typeof changer === 'function' ? changer.call(this) : changer.get.call(this);
this._needToBeRecalculated[name] = false;
this._computedLoopMap[name] = this._computedLoopId;
this.set(name, this._calculatedResults[name]);
return this._calculatedResults[name];
}
}
get(item, fallbackValue) {
if (this._isComputedProperty(item)) {
if (this._needToBeRecalculated[item]) {
return this.notifyPropertyChange(item);
} else {
return this._calculatedResults[item];
}
} else {
return this.lodash.get(this._getScope(), `${item}` , fallbackValue);
}
}
_defineProperty(item) {
const scope = this._getScope();
const _this = this;
Object.defineProperty(scope, item, {
get: function() {
return scope.props[`${item}`];
},
set: function(value) {
if (value === scope.props[`${item}`]) {
return value;
}
if (_this._isComputedProperty(item)) {
const changer = _this.get(`computed[${item}]`, _this._methodNotFound(`computed property "${name}"`));
if (typeof changer === 'object') {
value = changer.set.call(_this,value);
}
}
_this._invokeRunLoop(item);
scope.props[`${item}`] = value;
return value;
}
});
}
isPrivateProperty(name) {
return ['_runLoopsCount', '_computedLoopId'].includes(name);
}
setProperties(items) {
const propertyNames = Object.keys(items);
propertyNames.forEach(propertyName => {
this.set(propertyName, items[propertyName]);
});
return this;
}
set(item, value) {
if (!this.isPrivateProperty(item)) {
//this._invokeRunLoop(item);
}
if (!this._getScope().hasOwnProperty(item)) {
this._defineProperty(item, value);
}
return this.lodash.set(this._getScope(), `${item}`, value);
}
send(name, ...args) {
return this.get(`${name}`, this._methodNotFound(name)).apply(this, args);
}
setupController(args) {
this.set('userId', 42);
this.init();
}
init() {
console.log('some init stuff');
console.log(this.$moment);
console.log(this.$scope);
console.log(this.lodash);
console.log(this.$timeout);
}
// scope actions list
setActions() {
return {
firstScopeAction() {
// this === controller.this
},
secondScopeAction() {
// this === controller.this
},
firdScopeAction(element) {
console.log(element);
}
};
}
}
class SpecialController extends MyAngularController {
static injections() {
// 'a:b' equals expression -> inject "b" and assign name "a" to it;
return ['scope:$scope', '_:lodash', 'moment:$moment', 'timeout:$timeout'];
}
setupController(args) {
this.userId = 42;
this.init();
}
init() {
console.log('some init stuff');
console.log(this.$moment);
console.log(this.$scope);
console.log(this.lodash);
console.log(this.$timeout);
}
}
angular.module('app').controller('SpecialController', SpecialController.ngConstruct());
@lifeart
Copy link
Author

lifeart commented Jun 9, 2017

common use:

class SomeController extends MyAngularController {
    static injections() {
        return ['$scope', 'lodash', '$moment', '$timeout'];
    }
    setupController(args) {
        this.userId = 42;
    }
}

angular.module('app').controller('SomeController ', SomeController.ngConstruct());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment