I have custom directives tagPickerTag
, validationMessageTag
and check-valid-article-meta-tags
. Usage:
<div class="my-form-group">
<lable for="create_tags">Tags</lable>
<tag-picker-tag ng-model="entityInfo.meta.tags" editable="true" name="tags" check-valid-article-meta-tags></tag-picker-tag>
<validation-message-tag ctrl="form.tags"></validation-message-tag>
</div>
This is how I define these 3 directives
tagPickerTag:
<div class="tag-picker-tag">
tags
<ui-select ng-model="$parent.ngModel" ng-disabled="! editable" multiple tagging tagging-tokens="SPACE|," tagging-label="(custom 'new' label)" title="Select tags" sortable="true" theme="bootstrap" >
<ui-select-match placeholder="Enter Tags...">{{$item}}</ui-select-match>
<ui-select-choices repeat="tag in suggestedTags | filter:$select.search">
{{tag}}
</ui-select-choices>
</ui-select>
<p>Selected: {{ngModel}}</p>
</div>
'use strict'
var helper = require('../../helper.js')
var app = angular.module('custom_directive')
app.directive('tagPickerTag', [function() {
return {
restrict: 'E',
scope: {
editable: '='
},
templateUrl: '/public/common/directive/tag_picker_tag.html',
require: 'ngModel',
link:
function(scope, element, attrs, ngModelCtrl) {
},
controller:
['$scope',
function($scope) {
//todo: get popular tags from server
$scope.suggestedTags = ['superbowl', '2016election']
}]}}])
checkValidArticleMetaTags:
app.directive('checkValidArticleMetaTags', helper.simpleValidationDirective('article', 'meta', 'tags'))
exports.simpleValidationDirective = function(module, nestedInParent, field) {
return function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$validators.checkValid = function(modelValue, viewValue) {
var validationFunction = exports.validation[module]
if (nestedInParent)
validationFunction = validationFunction[nestedInParent]
validationFunction = validationFunction[field]
var message = validationFunction(modelValue)
ctrl.data = exports.dataAppendedWithMessage({}, 'error', message)
return ! message
}
}}}}
In case you are curious what validationFunction
in above code is (it should be irrelevant, since the validation
directive correctly get the validation error message):
....
,meta: {
tags: passIfListFulfill('tags', 10, 5, 10, false)
}
var passIfListFulfill = function(fieldName, amount, min, max, required) {
return function(input) {
if (!input || input === [])
return messageForNoInput(fieldName, required)
for (var i = 0; i < input.length; i++) {
var token = input[i]
if (token.length < min)
return token + ' is shorter than min: ' + min
else if (token.length > max)
return token + ' is longer than max ' + max
}
return messageForNoMoreThan(fieldName, input, amount)
}
}
ValidationMessageTag:
app.directive('validationMessageTag', [function() {
return {
restrict: 'E',
scope: {
ctrl: '=ngModel'
},
templateUrl: '/public/common/directive/validation_message_tag.html',
controller:
['$scope',
function($scope) {
$scope.$watch('ctrl.data', function(newValue, oldValue) {
$scope.success = newValue ? newValue.success : []
$scope.info = newValue ? newValue.info : []
$scope.warning = newValue ? newValue.warning : []
$scope.error = newValue ? newValue.error : []
}, true)
}]}}])
<div class="validation-message-tag" ng-show="ctrl.$touched && ctrl.data">
<p ng-repeat="message in success track by $index" class="validation-success">{{message}}</p>
<p ng-repeat="message in info track by $index" class="validation-info">{{message}}</p>
<p ng-repeat="message in warning track by $index" class="validation-warning">{{message}}</p>
<p ng-repeat="message in error track by $index" class="validation-error">{{message}}</p>
</div>
When I enter tags ['a']
, in my validation directive, I am able to return false and assign a string "a" is too short
to ctrl (which means my validation directive is correct).
but this message does not get passed into my validation_message_tag
to display. ie the $watch
callback is not invoked.
validtion_message_tag
works fine for and tags, so i think the problem maybe the implementation of my tagPickerTag
custom directive.
the $watch callback is not invoked.
So I couldn't get a simple scenario of $watch
to work.
My thought was that ng-model=""
is two way bound, you pick it up in the directive as scope: { "ngModel" : "=" }
which is a two way binding, so when the value is changed you should see it reflected. So you wouldn't need a $watch
. But I tried both however, neither worked.
So I used events instead.
$scope.$broadcast(EventNames.statusChange, vm.success)
scope.$on(EventNames.statusChange, function (e, val) { scope.show = val });
A side note, to prevent 'Magic Strings' I made a constant out of the event names. This should help eliminate developer spelling mistakes
Done it with a factory as well. With a factory you don't rely on the $scope
so doing controllerAs
stays less verbose in the controller dependency list. Secondly, if it ain't working, you know its because the factory hasn't been used, or a callback hasn't been registered. Rather then messing around with the complex thing that is angular eventing system.
A side note on the Event Aggregator pattern (angular $broadcast
and $on
), in my opinion this creates code that is far too loosely coupled leading to a lot of speghetti code. Another bad point about it to consider is that event enforces these rules:
By creating a service you can enforce developers to listen to things they're broadcasting. In the registercallbacks
function you can throw errors if no one has registered a listener. Also, there is now a dependency on StatusService
meaning we are a bit tighter coupling the components. IMHO the sweet spot in code coupling.
Setting and listening:
StatusService.setState(vm.success);
StatusService.registerCallbacks(function (val) { scope.show2 = val });
Implementations of the factory functions:
function setState(value) {
for (var i = 0; i < cbs.length; i++) {
cbs[i](value);
}
}
function registerCallbacks(cb) {
cbs.push(cb);
}
Essentially, they are the same thing, however using a factory in my opinion is safer, and you can potentially abstract some logic out into the factory. Rather than do it in the callback functions.
It turns out that I have a cookie flag isTesting
set up and forgot to turn it off. When it's testing, I simply return true for all validators.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.