I'm trying to fill a form with existing values using the controller $scope to pass those to the DOM.
Now, I'm using a directive to read and write values in a specific select menu (where I use bootstrap form helpers) menu:
app.directive('currencypicker', function () {
return {
restrict: 'A',
require: '?ngModel',
scope: {
ngModel: '='
},
link: function (scope, elem, attrs, ctrl) {
elem.addClass('bfh-currencies');
elem.addClass('bfh-selectbox');
elem.bfhselectbox({
filter: (elem.attr('data-filter') == 'true') ? true : false
}).on('change.bfhselectbox', function() {
return scope.$apply(function () {
return scope.ngModel = elem.val();
});
});
return elem.bfhcurrencies({
flags: (elem.attr('data-flags') == 'true') ? true : false,
currency: scope.ngModel || 'EUR'
});
}
};
});
Here the HTML snippet
<div currencypicker id="cost-currency" data-flags="true" data-filter="true" ng-model="newEvent.cost_currency"></div>
The problem is that when I try to assign my model value to currency attribute with scope.ngModel it evaluates always to undefined and so 'EUR' value is assigned, I checked my scope with firebug, where ngModel value is "GBP".
I can't figure out why it happens.
The way you have to use your directive is the following :
app.directive('currencypicker', function () {
return {
restrict: 'A',
require: '?ngModel',
//template : you have no template nor url template?
link: function (scope, elem, attrs, ngModelCtrl) {
// You can have your "ngModelValue" into ngModelCtrl.$viewValue
// Also, you now have access to all the functions declared into ngModelController
}
};
});
And if you want more information on that topic, do not hesitate to have a look at the doc : https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Related
I have a Directive:
var ActorDisplayDirective = function() {
return {
replace : false,
restrict : 'AE',
scope : {
actor : "="
},
templateUrl: staticContext + '/angular-app/templates/actor-display-template.html',
link : function(scope, elem, attrs) {
},
}
};
This works fine in some places, but not others. Here is my code to show it where it is not working:
<p>CAP: {{can_approve_for}}</p>
<p>
Actor display template:
<span actor-display actor='can_approve_for'></span>
After template
</p>
The CAP: ... displays the data, the directive's actor value is null. Why? My controller does:
dataFactory.getCanApproveFor().then(function(data) {
$scope.can_approve_for = data;
});
So, I am able to see the value on the page, but the directive does not show it. I'm assuming it's a timing/refresh thing, but this directive works elsewhere in ng-repeat, because the ng-repeat evaluates after hte object is already set, I guess. How do I do it in this case?
You are not actually declaring ActorDisplayDirective as a directive. Its just a plain function that returns an object that sort of looks like a directive.
You have to tell angular that it is a directive like so:
angular.module('someModule', [])
.directive('actorDisplay', function () {
return {
replace: false,
restrict: 'AE',
scope: {
actor: "="
},
templateUrl: staticContext + '/angular-app/templates/actor-display-template.html',
link: function (scope, elem, attrs) {
},
}
})
I have this directive which can be required or not. It can be used in two ways (as far as I know)
<my-foo required></my-foo>
or
<my-foo ng-required="data.value > 10"></my-foo>
So, because require and ngRequire are basically the same thing you would think that the directive could do this
HTML:
<my-foo ng-require="data.isRequired"></my-foo>
JS:
...
.directive('myFoo', function () {
return {
restrict: 'E',
scope: {
required: '='
}
...
DEMO
Well, nope, this doesn't work, scope.require is undefined. You actually have to change the scope definition to
scope: {
required: '=ngRequired'
}
So the question is what is the preferred way to handle both situation such that the value gets stored in scope.required ? Should I defined both or use attrs from the link function ?
There are basically 2 approaches you can pick:
1. Custom form element supporting ng-model
If you peek at the ng-required directive source code you'll find it only deals with ng-model controller:
restrict: 'A',
require: '?ngModel',
link: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.required = true; // force truthy in case we are on non input element
ctrl.$validators.required = function(modelValue, viewValue) {
return !attr.required || !ctrl.$isEmpty(viewValue);
};
attr.$observe('required', function() {
ctrl.$validate();
});
}
Thus if you custom directive supports ng-model you already have support for ng-required i.e.:
angular.module('test', [])
.directive('myInput', function(){
return {
restrict: 'E',
require: 'ngModel',
scope: true,
template: '<div><button ng-click="changeValue()">Change Value from: {{currentValue}}</button></div>',
link: function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.push(function(val){
if(!val){
return null;
}
return parseFloat(val, 10) * 100;
});
ngModelCtrl.$render = function() {
scope.currentValue = ngModelCtrl.$viewValue || 'No value';
};
scope.changeValue = function read(){
var newValue = Math.random();
if(newValue > 0.5){
ngModelCtrl.$setViewValue(newValue + "");
} else {
ngModelCtrl.$setViewValue(null);
}
ngModelCtrl.$render();
};
}
};
});
2. Wrap existing directive and pass ng-required:
angular.module('test', [])
.directive('myFormElement', function() {
return {
restrict: 'E',
scope: {
model: '=',
required: '='
},
template: '<div>Enter number: <input type="number" ng-model="data.number" ng-required="required"></div>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-init="data={value:'Initial', required: false}">
<form>
Is required: <input type="checkbox" ng-model="data.required">
<my-form-element required="data.required" model="data"></my-form-element>
</form>
</div>
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" >
So I know that I can have the model be different than the view value by using $parsers.
But what if I want to have the model change, without using $parsers?
For example:
html
<input tel-input ng-model="data.phone">
{{data.phone}}
js
.directive('telInput', function($compile) {
return {
restrict: 'A',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs) {
elem.on('paste', function() {
scope.ngModel = 'special model value after pasting';
});
}
};
});
The problem with this is that when scope.ngModel = 'pasting not allowed occurs, it changes the value in the input.
With $parsers, it changes the value in the model, but the view value remains the same.
There isn't any $setModelValue method, which seems like it would serve this purpose.
Any ideas?
Try this,
.directive('telInput', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
elem.on('paste', function() {
ngModel.$setViewValue('special model value after pasting');
ngModel.$render();
});
}
};
});
Please, tell me how to describe a directive 'myValidation' for input-field
<input my-validation ng-model="myValue">
to make it behave as follows:
if user inputs 'A' directive must change myValue to 'B' (showing it in input field)
otherwise just remain original value
You can create a watch on myValue and perform your validation when the value is changed...
angular.module('App', [])
.directive('myValidation', function () {
return {
restrict: 'A',
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$watch(function () {
return ngModel.$modelValue;
}, function (newVal, oldVal) {
if (newVal === 'A') {
scope.myValue = 'B';
}
});
}
};
});
Fiddle: http://jsfiddle.net/fMTVm/1/