I am using Bootstrap Toggle for my AngularJs project. I created angular directive for this plugin.
myApp.directive('iosToggle', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
$(element).bootstrapToggle(scope.$eval(attrs.iosToggle));
}
};
});
and using directive in my view
<input type="checkbox" id="myInterview" data-ios-toggle="toggle" data-style="ios" data-onstyle="info" data-size="small" data-on="On" data-off="Off" ng-model="myData.myInterview">
I am getting same design and able to on or off, but When i submit the form, i am not getting check box values.
Yes, I have updated my directive with $watch on change and then updated the model. It works.
myApp.directive('iosToggle', function($timeout){
return {
restrict: 'A',
transclude: true,
replace: false,
require: 'ngModel',
link: function ($scope, $element, $attr, ngModel) {
// update model from Element
var updateModelFromElement = function() {
// If modified
var checked = $element.prop('checked');
if (checked !== ngModel.$viewValue) {
// Update ngModel
ngModel.$setViewValue(checked);
$scope.$apply();
}
};
// Update input from Model
var updateElementFromModel = function() {
$element.trigger('change');
};
// Observe: Element changes affect Model
$element.on('change', function() {
updateModelFromElement();
});
// Observe: ngModel for changes
$scope.$watch(function() {
return ngModel.$viewValue;
}, function() {
updateElementFromModel();
});
// Initialise BootstrapToggle
$timeout(function() {
$element.bootstrapToggle();
});
}
};
});
Related
I have created a directive for showing a X besides a text box for clearing the data inside the text box,
Directive JS
angular.module(appName).directive('clrTxt', function () {
return {
restrict: 'E',
replace: true,
scope: {
cntrlas: '=',
mdlval: '='
},
link: function (scope, elem, attrs, ctrl) {
scope.cleartxt = function () {
scope.cntrlas[scope.mdlval] = '';
}
},
template: '<button class="close-icon" type="reset" id="closeicon" ng-click="cleartxt()" ><img src="/resources/img/quote-tool-close.png" class="clear-icon"></button>'
};
});
HTML
<input type="text" ng-model="item.epinNumber" ng-change="numberLengthCheck(item)" >
<clr-txt cntrlas="item" mdlval="'epinNumber'"></clr-txt>
This will create a X icon at the end of the text box and will clear the data when you click on it.
The issue is, I'm triggering a function on-change , So when the X icon is clicked, the data will be cleared and so ideally. the change event should be triggered. But for some reason the change event is not triggered when the data is cleared using the X directive.
The key point is to set bindToControllerand controllerAs alias.then you can access the controller function inside link function of directive.
angular.module('plunker', []);
function MainCtrl($scope) {
$scope.name = 'Test';
$scope.numberLengthCheck = function(n){
alert('change triggered '+ n);
};
}
angular.module('plunker').directive('clrTxt', function(){
return {
restrict: 'E',
controller: 'MainCtrl',
controllerAs: 'vm',
bindToController: true,
scope: {
cntrlas: '='
},
templateUrl: 'reverse_template.html',
replace: true,
link: function(scope, elem, attr, ctrls) {
scope.cleartxt = function () {
scope.cntrlas = '';
scope.numberLengthCheck(scope.cntrlas);
}
}
};
});
In ng-change the expression is not evaluated when the value change is coming from the model. If you want to listen the model $watch is useful
And here I think no need of sending extra attributes (cntrlas,mdlval) to clrTxtdirective. You can access the parent scope in directive by making scope:false which is default.
Directive JS
app.directive('clrTxt', function () {
return {
restrict: 'E',
replace: true,
template: '<button type="reset" ng-click="cleartxt()" >X</button>',
link: function (scope, elem, attrs, ctrl) {
scope.cleartxt = function () {
scope.item.epinNumber = '';
}
}
};
});
Controller JS
app.controller('MainCtrl', function($scope) {
$scope.$watch('item.epinNumber', function(newvalue,oldvalue) {
console.log('new value is ='+newvalue+ ' and old value is ='+oldvalue);
});
});
HTML
<body ng-controller="MainCtrl">
<input type="text" ng-model="item.epinNumber">
<clr-txt ></clr-txt>
{{item.epinNumber}}
</body>
Here is the working plunker LINK
I want one of the Radio Button to be selected once the page is loaded, from another question on stackoverflow i found that Radio Button will be check if the value of the input attribute is equal to the value of model applied on the Radio Button. But i am unable to access the model($parent.selectedItem) on Radio Button in link function inside child directive. Api i used in example is a placeholder but in realtime i will have a property selected which will be true/false which I want to bind to the $parent.selectedItem
var mainApp = angular.module('mainApp', []);
mainApp.factory('myFactory', function ($http) {
var myFactory = {
myMethod: function () {
var promise = $http.get('https://jsonplaceholder.typicode.com/users').then(function (response) {
return response.data;
});
return promise;
}
};
return myFactory;
});
Controller:
mainApp.controller('myController', function ($scope, myFactory) {
myFactory.myMethod().then(function (result) {
$scope.data = result
})
});
Directives:
mainApp.directive('parent', function (myFactory) {
return {
restrict: 'E',
replace: true,
scope: true,
templateUrl: 'parent.html',
link: function (scope, element, attrs, ctrl) {
myFactory.myMethod().then(function (result) {
scope.Model = result
})
}
}
});
mainApp.directive('child', function () {
return {
restrict: 'E',
scope: {
Model: '=ngModel'
},
replace: true,
require: 'ngModel',
templateUrl: 'child.html',
link: function (scope, element, attrs, ctrl) {
// unable to access scope.selectedItem
console.log(scope.selectedItem)
}
}
});
HTML:
// mainpage.html
<body ng-app="mainApp"><parent></parent></body>
//parent.html
<div><child ng-model = "Model"></child></div>
//child.html
<div ng-repeat="item in Model"><input type="radio" name="itemSelected"
ng-value="item" ng-model="$parent.selectedItem"/>{{item.name}}</div>
when you require ngModel in the child directive, what you're basically requiring is its controller, this controller is then injected into your link function as the 4th parameter, in your case the ctrl argument.
so right now your ngModel might work, but it is not in your link function because you're expecting it to exist on the scope as selectedItem, but on your scope you have declared it as Model (not selectedItem). However, you also have access to the ngModel controller, so you could ask for its value there through its controller: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController.
ex:
ctrl.$viewValue
// or
ctrl.$modelValue
//whichever serves your purpose
I am trying to do some custom validation based on a json object a user gives me.
However the input field visually does not show the value of the ngModel property. I added a plunkr to illustrate the problem.
'use strict';
angular.module('zendantennesApp')
.directive('validation', function ($compile, $parse) {
return {
scope: {
validation: '#',
ngModel: '#'
},
require: "?ngModel",
restrict: 'A',
compile: function(el, attrs) {
el.removeAttr('validation');
el.attr('ng-blur', 'evaluateExpression()');
el.attr('ng-focus', 'assignOriginalValue()');
var fn = $compile(el);
return function(scope, element, attrs, ngModel){
ngModel.$render = function(){
$(element).val(ngModel.$viewValue);
};
fn(scope);
}
},
controller: function($scope){
$scope.originalValue = $scope.ngModel;
$scope.validationObject = JSON.parse($scope.validation.replace(/'/g, '"'));
$scope.evaluateExpression = function(){
console.log($scope.validationObject);
};
$scope.assignOriginalValue = function(){
$scope.originalValue = $scope.ngModel;
console.log($scope.originalValue);
}
}
}
});
https://plnkr.co/edit/1qYxCiSZWHgVeN9CEpxw?p=preview
validation directive will have isolated scope and hence parent scope value will not be accessible unless you explicitly mention during compile. Replace fn(scope); by fn(scope.$parent);
Updated Plunker
I have a custom directive with children directives:
<rp-nav>
<rp-nav-item cat="1"></rp-nav-item>
<rp-nav-item cat="2"></rp-nav-item>
<rp-nav-item cat="3"></rp-nav-item>
<rp-nav-item cat="4"></rp-nav-item>
<rp-flyout></rp-flyout>
</rp-nav>
Here are the modules I have defined:
var app = angular.module('app', []);
app.directive('rpNav', function() {
return {
restrict: 'E',
controller: function($scope) {
$scope.currentItem = 'none'; //initialize currentItem
this.setCurrentItem = function(itemId) {
$scope.currentItem = itemId;
}
},
};
});
app.directive('rpNavItem', function() {
return {
restrict: 'E',
template: function(el, attrs) {
return '<p>item {{currentItem}} ' + attrs.cat;
},
require: '^rpNav',
link: function(scope, el, attrs, nav) {
el.on('click', function() {
nav.setCurrentItem(attrs.cat);
});
}
};
});
app.directive('rpFlyout', function() {
return {
restrict: 'E',
template: '<p style="background-color: lightblue">{{currentItem}}</p>'
};
});
The idea is to click in any of the items and make the rp-flyout element display information about the clicked rp-nav-item. The scope variable currentItem does change on click, but the template in rp-flyout does not update. What am I missing to achieve this goal? And, is this a "best practice" way of tackling this problem.
Here's a plunker
To expand on the comment, directives are not inherently part of the digest cycle, so you need to add scope.$apply() inside your el.click handler to trigger a digest cycle and update template bindings.
el.on('click', function() {
nav.setCurrentItem(attrs.cat);
scope.$apply();
});
I am using ng-repeat and setting a model with it similar to the following
<div ng-repeat="thing in things" ng-model="thing" my-directive>
{{thing.name}}
</div>
then in my directive it looks something like this
.directive("myDirective, function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(model.name);// this gives me 'NAN'
}
}
})
My question is how can I access the values in the model? I tried model.$modelValue.name but that did not work.
If you want to bind in a scoped value then you can use the '=' in an isolated. This will appear on the scope of your directive. To read the ng-model directive, you can use =ngModel:
.directive("myDirective", function () {
return {
scope: {
model: '=ngModel'
}
link: function(scope) {
console.log(scope.model.name); // will log "thing"
}
}
});
.directive("myDirective", function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(attrs.ngModel); // will log "thing"
}
}
})
If your directive does not have isolated or child scope then you can do this:
.directive('someDirective', function() {
return {
require: ['^ngModel'],
link: function(scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0];
var someVal;
// you have to implement $render method before you can get $viewValue
ngModelCtrl.$render = function() {
someVal = ngModelCtrl.$viewValue;
};
// and to change ngModel use $setViewValue
// if doing it in event handler then scope needs to be applied
element.on('click', function() {
var val = 'something';
scope.$apply(function() {
ngModelCtrl.$setViewValue(val);
});
});
}
}
});