Input does not reflect ngModel value - angularjs

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

Related

Call validation directive function in the controller

Im trying to call the validation function from directive in the controller. Is it possible?
My directive is something like this:
app.directive('evenNumber', function(){
return{
require:'ngModel',
link: function(scope, elem, attrs, ctrl){
ctrl.$parsers.unshift(checkForEven);
function checkForEven(viewValue){
if (parseInt(viewValue)%2 === 0) {
ctrl.$setValidity('evenNumber',true);
}
else{
ctrl.$setValidity('evenNumber', false);
}
return viewValue;
}
}
};
});
I want to call the function checkForEven from the controller.
$scope.copyFileContentToEditor = function(){
$scope.code = $scope.content;
// TODO call the checkForEven directive function to validate the $scope.code
}
Is it possible to do that? Any suggestions?
You may need to define a link between your controller and your directive to get them know each other.
app.controller('MainCtrl', function($scope) {
$scope.name = 'World'; // this is just used for your directive model, it is not the part of the answer
$scope.vm = {} // define this to create a shared variable to link the controller and directive together.
});
app.directive('evenNumber', function(){
return{
require:'ngModel',
scope: {'vm': '='},
restrict: 'A',
link: function(scope, elem, attrs, ctrl){
function checkForEven(){
alert('I get called.')
}
scope.vm.checkForEven = checkForEven; // once the directive's method is assigned back to "vm", so you could trigger this function from your controller by call this vm.checkForEven;
}}})
HTML
<div ng-model="name" even-number vm="vm"></div>
Plunker Example
Add the function as a property of the ngModel controller:
app.directive('evenNumber', function(){
return{
require:'ngModel',
link: function(scope, elem, attrs, ctrl){
ctrl.$parsers.unshift(checkForEven);
//ADD checkForEven to ngModel controller
ctrl.checkForEven = checkForEven;
function checkForEven(viewValue){
if (parseInt(viewValue)%2 === 0) {
ctrl.$setValidity('evenNumber',true);
}
else{
ctrl.$setValidity('evenNumber', false);
}
return viewValue;
}
}
};
});
Then name the form and input element:
<form name="form1">
<input name="input1" ng-model="vm.input1" even-number />
</form>
The controller can then reference it where it attaches to scope:
$scope.copyFileContentToEditor = function(){
$scope.code = $scope.content;
//CALL the function
$scope.form1.input1.checkForEven($scope.vm.input1);
}

AngularJs - Bootstrap Toggle not able to get check box values

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();
});
}
};
});

Why is a parent controller's $scope not accessible from a (child) directive?

From reading Scopes (Part 2 of the AngularJS - from beginner to expert in 7 steps series): A $scope can contain both data and functions available in a view. If AngularJS cannot find a function on a local $scope, the containing (parent) $scope will be checked for the property or method there.
Given my implementation of a directive's compile function (based on Angularjs: understanding a recursive directive):
compile: function(tElement, tAttrs) {
var contents = tElement.contents().remove();
console.log(contents);
var compiledContents;
// postLink function.
return {
post: function(scope, iElement, iAttrs) {
if (!compiledContents) {
// Get linking function.
compiledContents = $compile(contents);
}
// Link scope and the template together.
compiledContents(scope, function(clone) {
iElement.append(clone);
});
scope.myEvent = function() {
console.log("My Event handled!");
};
},
pre: function(scope, iElement, iAttrs) { }
}
}
In the code above, I have attached a function to the $scope of the instance element, and this is successfully called from the view. However I expected to be able to move the function definition from the instance element scope and into a parent controller's $scope:
angular.module('Myapp').controller('MyParentController', ['$scope',
function($scope) {
$scope.myEvent = function() {
console.log("My Event handled!");
};
}]);
However the parent controller's function is never called even though it is a parent of the directive for which I provided my own implementation of compile.
Updated to add code for the directive:
angular.module('Myapp').directive("my-directive", function(RecursionHelper) {
return {
restrict: "E",
scope: {
data: '=data'
},
templateUrl: 'view.html',
compile: function(tElement, tAttributes) {
return RecursionHelper.compile(tElement, tAttributes);
}
};
});
..and the RecursionHelper:
angular.module('Myapp').factory('RecursionHelper',
['$compile',
function($compile) {
var RecursionHelper = {
compile: function(tElement, tAttrs) {
var contents = tElement.contents().remove();
var compiledContents;
return {
post: function(scope, iElement, iAttrs) {
if (!compiledContents) {
compiledContents = $compile(contents);
}
compiledContents(scope, function(clone) {
iElement.append(clone);
});
},
pre: function(scope, iElement, iAttrs) { }
}
}
}
return RecursionHelper;
}]);
change your scope to
scope: {
data: '=data'
myEvent: '=myEvent'
}
and then on your directive change this
angular.module('Myapp').directive("my-directive",
to
angular.module('Myapp').directive("myDirective",
then pass the function as in
<my-directive data="scope-data" my-event="scope-event-function()"></my-directive>
because your directive has an isolated scope, you can only access data in parent scope.
scope: {
data: '=data'
},

get initial value of ngModel in directive

I have a directive that requires ngModel. The directive modifies the value stored in ngModel (it implements in-place editing of text). Inside my link function I need to get the value of ngModel before it has been changed.
I tried looking at ngModel.$viewValue, and ngModel.$modelValue. They both eventually get the model's contents, but in the beginning of the directive's life-cycle they get the raw unprocessed angular expression such as {{user.name}}. And I cannot find a way to determine
when the expression has been processed.
Any ideas?
directive('test', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
}
};
})
Use the $parse service:
app.directive('test', function($parse) {
return {
link: function (scope, element, attrs) {
var modelGetter = $parse(attrs.ngModel);
var initialValue = modelGetter(scope);
}
};
});
Or:
app.directive('test', function($parse) {
return {
compile: function compile(tElement, tAttrs) {
var modelGetter = $parse(tAttrs.ngModel);
return function postLink(scope, element) {
var initialValue = modelGetter(scope);
};
}
};
});
Demo: http://plnkr.co/edit/EfXbjBsbJbxmqrm0gSo0?p=preview

When i require ngModel controller how do I access a property of the model controller

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);
});
});
}
}
});

Resources