With AngularJS, I'm trying to check validity of a form within a custom directive.
In the template:
<form name="formName" custom-directive="someController.function()">
...
</form>
In JavaScript:
angular.module("myApp")
.directive("customDirective", function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var callbackFn = attrs.customDirective;
// Check form validation, then call callbackFn
scope.$eval(callbackFn);
}
};
}
);
Usually, we can see form validation with scope.formName.$valid, but the form's name can be different, so I need a generic way to access it.
You need to add require: 'form' directive:
.directive("customDirective", function () {
return {
require: 'form',
restrict: 'A',
link: function (scope, element, attrs, formController) {
var callbackFn = attrs.customDirective;
// Check form validation, then call callbackFn
if (formController.$valid) {
scope.$eval(callbackFn);
}
}
};
}
After that form controller will be injected as fourth argument into link function, and you would be able to check form validity with formController.$valid property.
Related
Is it possible to watch the attribute which triggered the directive?
export function minDirective(): ng.IDirective {
return {
restrict: 'A',
require: 'ngModel',
link: (scope, elem, attr, ctrl) => {
scope.$watch(<name of the attribute>, () => {
// Do something
});
}
};
}
I'd like to listen to bb-civic-registration-number-format attribute the example below, except I have no idea it's named that way by the programmer reusing my directive:
I'm trying to create a validation directive which would take an arbitrary expression and use it for validation. A typical example is ngMin and ngMax except I'd like to implement similar functionality for an arbitrary input type:
<input type="number" ng-model="someModel" />
<input type="text" myprefix-max="someModel*0.5" />
You can actually get the name of your directive in the compile function, which should give you the ability to look up the value by comparing it to the attribute collection.
compile: function (elem, attrs) {
console.log(this.name);
}
If I understand question properly you can use the attribute value and [] notation.
<element my-directive scope-var="controllerScopePropertyName"></element >
JS
export function myDirective(): ng.IDirective {
return {
restrict: 'A',
require: 'ngModel',
link: (scope, elem, attr, ctrl) => {
// using ES5 syntax
scope.$watch(function(){
return scope[attr.scopeVar];
}, function(newVal,oldVal){
// do something
});
}
};
}
Alternatively just set up isolated scope and bind directly to the controller
I have the following code:
<div id='parent'>
<div id='child1'>
<my-select></my-select>
</div>
<div id='child2'>
<my-input></my-input>
</div>
</div>
I also have two directives which get some data from the data factory. I need the two directives to talk to each other such that when a value in select box is changed the input in changes accordingly.
Here's my two directives:
.directive("mySelect", function ($compile) {
return {
restrict: 'E',
scope:'=',
template: " <select id='mapselectdropdown'>\
<option value=map1>map1</option> \
<option value=map2>map2</option> \
</select>'",
link: function (scope, element, attrs) {
scope.selectValue = //dont konw how to get the value of the select
}
};
})
.directive("myInput", function($compile) {
return {
restrict: 'E',
controller: ['$scope', 'dataService', function ($scope, dataService) {
dataService.getLocalData().then(function (data) {
$scope.masterData = data.input;
});
}],
template: "<input id='someInput'></input>",
link: function (scope, element, attrs) {
//here I need to get the select value and assign it to the input
}
};
})
This would essentially do the onchange() function that you can add on selects. any ideas?
You could use $rootScope to broadcast a message that the other controller listens for:
// Broadcast with
$rootScope.$broadcast('inputChange', 'new value');
// Subscribe with
$rootScope.$on('inputChange', function(newValue) { /* do something */ });
Read Angular docs here
Maybe transclude the directives to get access to properties of outer scope where you define the shared variable ?
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
-> https://docs.angularjs.org/guide/directive
After much research this is what worked...
I added the following:
.directive('onChange', function() {
return {
restrict: 'A',
scope:{'onChange':'=' },
link: function(scope, elm, attrs) {
scope.$watch('onChange', function(nVal) { elm.val(nVal); });
elm.bind('blur', function() {
var currentValue = elm.val();
if( scope.onChange !== currentValue ) {
scope.$apply(function() {
scope.onChange = currentValue;
});
}
});
}
};
})
Then on the element's link function I added:
link: function (scope, elm, attrs) {
scope.$watch('onChange', function (nVal) {
elm.val(nVal);
});
}
Last added the attribute that the values would get set to in the scope:
<select name="map-select2" on-change="mapId" >
I am trying to implement dynamically configurable fields. I will get validation rules ng-required, ng-hidden, ng-disabled etc attributes as json from the server and set them dynamically through a directive.
I have the following directive code. It displays select values doubled JsBin link is http://jsbin.com/jiququtibo/1/edit
var app = angular.module('myapp', []);
app.directive('inputConfig', function( $compile) {
return {
require: 'ngModel',
restrict: 'A',
scope: '=',
compile: function(tElem, tAttrs){
console.log("compile 2");
tElem.removeAttr('data-input-config');
tElem.removeAttr('input-config');
tElem.attr('ng-required',true);
return {
pre: function (scope, iElement, iAttrs){
console.log('pre');
},
post: function(scope, iElement, iAttrs){
console.log("post");
$compile(tElem)(scope);
}
}
}
};
});
How can I solve this issue? I should be able to add directive dynamically.
To solve your problem you need to remove the following line from your post function:
$compile(tElem)(scope);
It's not clear to me why you are compiling here so I'm not sure if there will be any unintended side effects from this.
I found a solution following code is working.You should first clone, remove directive, prepare dom and compile
app.directive('inputConfig', function( $compile) {
return {
require: '?ngModel',
restrict: 'A',
compile:function (t, tAttrs, transclude){
var tElement = t.clone() ;
tElement.removeAttr('input-config');
tElement.attr('ng-required',true);
t.attr('ng-required',true);
return function(scope){
// first prepare dom
t.replaceWith(tElement);
// than compile
$compile(tElement)(scope);
};
}
}
});
I successfully made a jQuery plugin into a directive.
app.directive('bxSlider', function($timeout)
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
$timeout(function(){element.bxSlider(scope.$eval(attrs.bxSlider))},1);
}
}
});
In my controller (through a click function), I'd like to call a method that is public on the plugin, but I am not sure how to do that. I tried setting the directive to a variable and calling it that way from my controller, but I get the error of ...has no method...
What is the correct way to do this?
You could broadcast an event to trigger your plugin method.
app.directive('bxSlider', function($timeout)
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var slider;
$timeout(function() {
slider = element.bxSlider(scope.$eval(attrs.bxSlider));
}, 1);
scope.$on('reload-slider', function() {
slider.reloadSlider();
});
}
}
});
Then in the controller function you use $scope.$broadcast('reload-slider').
I would like to create a custom input type similar to the way AngularJS implements "email", for example.
<input type="email" ng-model="user.email" />
What I would like to create is an input type like this:
<input type="path" ng-model="page.path" />
Any ideas on how this can be accomplished? So far, I've only been able to figure out how to implement custom directives where 'path' is the name of the tag, attribute or class.
For example, I can get this to work but it is inconsistent with the other form fields and I'd really like them to look the same.
<input type="text" ng-model="page.path" path />
app.directive('path', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) { ... }
};
});
You can create your own input type="path" by creating an input directive with custom logic if the type attribute is set to "path".
I've created a simple example that simply replaces \ with /. The directive looks like this:
module.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
if (attr.type !== 'path') return;
// Override the input event and add custom 'path' logic
element.unbind('input');
element.bind('input', function () {
var path = this.value.replace(/\\/g, '/');
scope.$apply(function () {
ngModel.$setViewValue(path);
});
});
}
};
});
Example
Update: Changed on, off to bind, unbind to remove jQuery dependency. Example updated.
An alternative solution can be achieved by using the $parsers property of the ngModelController. This property represents a chain of parsers that are applied to the value of the input component before passing them to validation (and eventually assigning them to the model). With this, the solution can be written as:
module.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
if (attr.type !== 'path') return;
ngModel.$parsers.push(function(v) {
return v.replace(/\\/g, '/');
});
}
};
});
Note that there is another property $formatters which is a pipeline of formatters that transform a model value into the value displayed in the input.
See here for the plunker.
Considering compile function is the first in line, would it not be better with:
module.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
compile: function Compile(tElement, tAttrs) {
if (tAttrs.type !== 'path') return;
return function PostLink(scope, element, attr, ngModel) {
// Override the input event and add custom 'path' logic
element.unbind('input');
element.bind('input', function () {
var path = this.value.replace(/\\/g, '/');
scope.$apply(function () {
ngModel.$setViewValue(path);
});
});
}
}
};
});