Skip to content

Instantly share code, notes, and snippets.

@afitterling
Last active August 29, 2015 14:10
Show Gist options
  • Save afitterling/5467dc3740bd37aa9280 to your computer and use it in GitHub Desktop.
Save afitterling/5467dc3740bd37aa9280 to your computer and use it in GitHub Desktop.
A simple UI Biz validator (business logic validation) for Angular
'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');
// ........
}]);
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.
'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