Last active
March 4, 2020 01:47
-
-
Save lifeart/d146871d896461193db6779ac5bbfc01 to your computer and use it in GitHub Desktop.
Angular Simple DI for controller ES6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
common use: