Last active
August 29, 2015 14:10
-
-
Save afitterling/5467dc3740bd37aa9280 to your computer and use it in GitHub Desktop.
A simple UI Biz validator (business logic validation) for Angular
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
'use strict'; | |
angular.module('famousAngular') | |
.controller('DataCtrl', ['ValidationActionsStore', '$scope', | |
function (ValidationActionsStore, $scope) { | |
// register UI elements distincively | |
ValidationActionsStore.register('dropdown.lang.to'); | |
ValidationActionsStore.register('dropdown.lang.from'); | |
// you must call ValidationActionStore.updateState(name) in the UI directives or controllers when they change value | |
// ........ | |
// validator | |
var equalsForeign = function (own, foreign) { | |
if (own === foreign) { | |
// or e.g. if own.type === Validation.service.bankAccount.type | |
// set some var for view or call some action | |
return true; | |
} | |
// otherwise do ......... | |
}; | |
// a basic validation triggered on both dropdowns dropdown.lang.to and .from | |
ValidationActionsStore.validation.push('dropdown.lang.to', 'dropdown.lang.from', equalsForeign, 'Equals Foreign', {both: true}); | |
// ValidationActionsStore.validation.push('dropdown.lang.from', 'dropdown.lang.to', equalsForeign, 'Equals Foreign'); | |
// another validation | |
ValidationActionsStore.validation.push('dropdown.lang.to', 'dropdown.lang.from', function (own, foreign) { | |
if (own.name === 'Thai') { | |
return true; | |
} | |
}, 'isThai', {both: true}); | |
// another test with Arrays, but concider it a silly test just to prove you can do something with it | |
ValidationActionsStore.validation.push('dropdown.lang.to', ['dropdown.lang.from', 'dropdown.lang.to'], function (own, foreignObj) { | |
$log.debug('triggered', own, foreignObj); | |
return angular.equals(foreignObj['dropdown.lang.from'], foreignObj['dropdown.lang.to']); | |
}, 'ArrayTest'); | |
// ........ | |
}]); |
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
In many projects I have seen requirements beyond the scope of angular, | |
where angular did not really nicely fit and a lot of javascript code had to be written. | |
I think a good base point where to start UI business logic would be this service. | |
Inject it to Angular. Make sure you distinguably name and register ui components. | |
On model update (in directives or controllers or any place ui components get updated) call the .update of this ValidationStore | |
with the name of the component. | |
Hence when registered and updated the target listener actions/validation get triggered and validation can take place. | |
from here you could use another directive with model attr to receive/display messages from the ui ctrls, or disable or re-set menu items. | |
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
'use strict'; | |
// this validation store handles validation for ui (business) logic | |
angular.module('famousAngular') | |
.factory('ValidationActionsStore', ['$rootScope', function ($rootScope) { | |
var self = this; | |
self.validationStore = $rootScope.$new(); | |
// register the UI element with a name we like to be considered in validation process | |
self.register = function (component) { | |
if (!angular.isDefined(self.validationStore[component])) { | |
self.validationStore[component] = []; | |
} | |
}; | |
// push a validator for the target UI considered to change, that calles the listener with own model and UIForeignElement Model | |
self.push = function (UIElementTriggersValidation, UIForeignElements, listener /* fn: (own model, foreign model) */, name, options) { | |
var single = null; | |
// on single ForeignObj. do | |
if (typeof(UIForeignElements) === 'string') { | |
single = true; | |
self.validationStore[UIElementTriggersValidation].push({UIForeignElements: UIForeignElements, listener: {fn: listener, name: name}, | |
conf: {UIForeignElement: {single: single}}} | |
); | |
// see if options is set | |
if (angular.isDefined(options)) { | |
if (angular.isDefined(options.both)) { | |
// if both: true -> register on foreign element as well | |
self.validationStore[UIForeignElements].push({UIForeignElements: UIElementTriggersValidation, listener: {fn: listener, name: name}, | |
conf: {UIForeignElement: {single: single}} | |
}); | |
} | |
} | |
} | |
// if UIForeignElement is Array do | |
if (Array.isArray(UIForeignElements)) { | |
console.log(UIForeignElements); | |
self.validationStore[UIElementTriggersValidation].push({UIForeignElements: UIForeignElements, listener: {fn: listener, name: name}, | |
conf: {UIForeignElement: {single: false}}} | |
); | |
} | |
}; | |
// update should be called with current model value of UI component in controller or | |
// in angular directive or anything which handles the UI element state | |
// on updateState triggers all listener registered as UIForeignElement on this component | |
// the listener are callbacks supplied with two values: own model and foreign ui model against we want to validate | |
self.updateState = function (component, currentValue) { | |
self.validationStore[component].currentValue = currentValue; | |
// reset validation state of component to 'true' | |
self.validationStore[component].anyValidation = false; | |
self.validationStore[component].anyValidationArray = []; | |
// go trough all listeners and if one is set to false | |
angular.forEach(self.validationStore[component], function (ForeignUIElement) { | |
var fn = ForeignUIElement.listener.fn; | |
var res; | |
// in case it has been registered with foreignElement as string | |
if (ForeignUIElement.conf.UIForeignElement.single) { | |
fn.params = [currentValue, self.validationStore[ForeignUIElement.UIForeignElements].currentValue]; | |
res = fn.apply(null, fn.params); | |
if (res) { | |
self.validationStore[component].anyValidation = res; | |
self.validationStore[component].anyValidationArray.push(ForeignUIElement.listener.name); | |
} | |
} | |
// if registered multiple foreign elements as array/obj | |
if (ForeignUIElement.conf.UIForeignElement.single === false) { | |
var foreignValues = {}; | |
angular.forEach(ForeignUIElement.UIForeignElements, function (foreign) { | |
console.log(foreign, self.validationStore[foreign].currentValue); | |
foreignValues[foreign] = self.validationStore[foreign].currentValue; | |
}); | |
fn = ForeignUIElement.listener.fn; | |
fn.params = [currentValue, foreignValues]; | |
res = fn.apply(null, fn.params); | |
if (res) { | |
self.validationStore[component].anyValidation = res; | |
self.validationStore[component].anyValidationArray.push(ForeignUIElement.listener.name); | |
} | |
} | |
}); | |
}; | |
return { | |
register: self.register, | |
updateState: self.updateState, | |
validation: { | |
push: self.push | |
}, | |
anyValidation: function (component) { | |
return self.validationStore[component].anyValidation; | |
}, | |
anyValidationArray: function (component) { | |
return self.validationStore[component].anyValidationArray; | |
} | |
}; | |
}]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment