The directive below works fine until you introduce the ng-required
directive to a child div.
The ng-model
property remains required within the form even though the element has been removed. The result being that the user is required to fill in a field which does not exist on the form before being able to submit it.
The current patch-job solution that we have on this directive (which was made by someone no longer at the company) is to introduce an ng-if
directive on the same element as the custom directive. This obviously defeats the purpose of the custom directive and, now that I need to add further conditions, needs to be fixed.
I've read other posts and believe the issue is related to the scope of directive. However, I've played around with the scope level options as well as logged out the scope in the console in order to look for a destroy-style method without any success.
In the case that a commercial sees the view he will, correctly, not see the html below due to the user level being 3. And yet the requirement remains...
CUSTOM DIRECTIVE:
angular.module('tvalorApp').directive('checkPermissions', ['PermissionsServices', function(PermissionsServices) {
return {
restrict: 'A',
scope: {
level:'@'
},
link: function(scope, elem, attrs, ctrl) {
function checkPermission() {
if (attrs.permissions != '') {
var hasPermission = PermissionsServices.hasAccess(attrs.checkPermissions, scope.level);
if (!hasPermission) {
console.log(scope)
elem.remove();
}
} else {
elem.remove();
}
}
scope.$watch('level', function(value) {
checkPermission();
});
}
};
}]);
FACTORY USED BY DIRECTIVE:
angular.module('tvalorApp').factory('PermissionsServices', PermissionsServices);
function PermissionsServices() {
var fields = {
"noAdmin": [false, true, true],
"noValidator": [true, false, true],
"noCommercial": [true, true, false],
"onlyAdmin": [true, false, false],
"onlyValidator": [false, true, false],
"onlyCommercial": [false, false, true],
"allUsers": [true, true, true],
"noUsers": [false, false, false],
};
var readOnlyFields = {
"protectAdmin": [true, false, false],
"protectValidator": [false, true, false],
"protectCommercial": [false, false, true],
"noProtectAdmin": [false, true, true],
"noProtectValidator": [true, false, true],
"noProtectCommercial": [true, true, false],
"noProtect": [false, false, false],
"protectAll": [true, true, true],
};
var hasAccess = function(stateName, level) {
var hasAccess = false,
level = level - 1;
if(fields[stateName][level]) {
hasAccess = true;
}
return hasAccess;
};
var checkReadOnlyField = function(fieldName, level) {
return readOnlyFields[fieldName][level - 1];
}
return {
hasAccess: hasAccess,
checkReadOnlyField: checkReadOnlyField
};
};
HTML:
<div class="form-group" level="{{taskCtrl.level}}" check-permissions="noCommercial">
<label for="a_market">
Situación del mercado <span class="highlight">*</span>
</label>
<select id="a_market" name="a_market" class="form-control" ng-change="taskCtrl.setEditTask()"
ng-options="item.id as item.es for item in mainCtrl.market" ng-model="taskCtrl.task.a_id_market"
ng-required="true">
<option value="">N.A.</option>
</select>
<p class="error-message" ng-show="taskForm.a_market.$dirty && taskForm.a_market.$error.required">Campo
Obligatorio</p>
</div>
The problem here is that, when the custom directive deletes the div element, the form control remains registered to the parent form.
One solution would be to unregister that control when the element gets deleted, using the $removeControl(control);
method. (documentation)
In order to achieve that, first of all you need to inject a parent form reference into the custom directive:
require: '^form',
and the control name, for example:
scope: {
level: '@',
field: '@'
},
Now in link function, you have access to the parent form:
link: function(scope, elem, attrs, ctrl) {
console.log('parent form', ctrl);
...
}
and you can deregister the control which gets deleted:
if (!hasPermission) {
elem.remove();
ctrl.$removeControl(ctrl[scope.field]);
}
After these additions, when element get deleted, the form becomes valid to submit.
Here is a working example: DEMO