Attach $watch to input field in directive - javascript

The directive below is intended to take the value of the <input> tag and render the exact number of boxes. This directive needs to be restricted to E (bad design but is what it is), so it looks like I need to find some way of attaching a $watch to the input field.
Below you can see my best attempt, or at least a general sketch of what I'd like to accomplish, however this only triggers when the page originally loads. No change to the value in the input box is reflected by the alert statement.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body ng-app="myApp">
<draw-boxes count="3"></draw-boxes>
<script>
var app = angular.module("myApp", []);
app.directive("drawBoxes", function() {
var input = "<input type='text'></input>";
var htmlCanvas = "<canvas width='800' height='800'></canvas>";
var template = input + htmlCanvas;
var linker = function(scope, el, attrs){
scope.$watch(el.children()[0], function (v) {
alert('value changed, new value is: ' + v);
//Will do some canvas drawing here based on input
});
};
return {
restrict: "E",
template : template,
link: linker
};
});
</script>
</body>
</html>

You can use ng-change on the input. Here is an example:
var app = angular.module("myApp", []);
app.directive("drawBoxes", function() {
var linker = function(scope, el, attrs){
scope.valueChanged = '';
scope.change = function() {
scope.valueChanged = 'new value is ' + scope.value;
};
};
return {
restrict: "E",
template : "<input type='text' ng-change=\"change()\" ng-model=\"value\"></input>"+
"<span>{{valueChanged}}</span>" +
"<canvas width='800' height='800'></canvas>",
link: linker
};
Here is a working example on jsfiddle.

Personally, I would try attaching a controller to the directive.
Also, the input field will need to have a unique ng-model value attached to it.
Then your $scope.$watch can check if the value has changed for the input field whenever any $scope value changes.
Something like this:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body ng-app="myApp">
<draw-boxes count="3"></draw-boxes>
<script>
var app = angular.module("myApp", []);
app.directive("drawBoxes", function() {
var input = "<input type='text' ng-model='watchedInput'></input>";
var htmlCanvas = "<canvas width='800' height='800'></canvas>";
var template = input + htmlCanvas;
return {
restrict: "E",
template : template,
controller: function($scope) {
$scope.$watch(function() {
// when a $scope value is changed, return the
// value you want this watcher to watch
return $scope.watchedInput;
}, function(newValue) {
// if the value returned above is different from the
// previous value, this function will be invoked
// passing in the changed value as newValue
alert('value changed, new value is: ' + newValue);
}, true);
},
scope: {},
bindToController: true
};
});
</script>
</body>
</html>
FYI: I haven't tested this code but wanted to illustrate the idea.

Related

How to pass $scope variable from controller to directive using angularjs

I made a simple directive that print an object:
var app = angular.module("myApp", []);
app.controller('myCtrl', function($scope) {
$scope.users=[
{name:'davidi',age:'23'},
{name:'karen',age:'43'},
{name:'lior',age:'22'}
];
});
app.directive("appCustomer", function() {
var htmlcode="<p ng-repeat='user in customerInfo'>{{user.name}}</p>\
";
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
template : htmlcode
};
});
my html code:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<app-customer info="users"></app-customer>
</div>
</body>
</html>
<script src="angularapp.js"></script>
i want simply access the $scope.users in my directive, to print the users object without the ng-repeat,
so i made it like that:
var app = angular.module("myApp", []);
app.controller('myCtrl', function($scope) {
$scope.users=[
{name:'davidi',age:'23'},
{name:'karen',age:'43'},
{name:'lior',age:'22'}
];
});
app.directive("appCustomer", function() {
var htmlcode="";
var userslength=3;
for(var i=0;i<userslength;i++)
{
htmlcode=htmlcode+"<p>{{users["+i+"]['name']}}</p>";
}
return {
restrict: 'E',
template : htmlcode
};
});
thats works fine, but when i replace the userlength to $scope.users.length , the app fail.
so my question is how can i access a $scope from the controller in the directive?
JSFiddle
You need to specify the link function
app.directive("appCustomer", function() {
function link(scope, element, attrs) {
var htmlcode = "";
for (var i = 0; i < scope.users.length; i++) {
element.text(Put your text here);
// OR
element.html(Put your HTML here);
}
}
return {
restrict: 'E',
users: '=',
link: link
};
});
In the scope you will receive users array
The second is you can you element argument to put your data to template. Or create html template separately

How to post a scope variable right after it has changed the value?

I am learning Angularjs and want to use $http.post() method.
In HTML
In Javascript
$scope.onevariable ='value1'
$scope.doSomething = function(){
$scope.onevariable = 'value2';
$http.post('someurl',{onevariable:$scope.onevariable }).then(function(){...})}
The problem is that I want to post the changed $scope.onevariable as parameter to the server, and this variable has to be changed after I click the button. However,this $scope.onevariable can be posted before it has changed to 'value2', so how can I post it exactly after the value has been changed. 'value2' is unknown value to me,or an encrypted string, you can treat it as a random string.
Well, I don't know if I understand your question well, but here's is a snippet working:
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope) {
$scope.onevariable ='';
$scope.post = false;
var increment = 0;
$scope.changeVar = function() {
$scope.onevariable = 'value' + ++increment;
}
$scope.$watch('onevariable', function(newValue, oldValue) {
if (newValue != oldValue && newValue == 'value2') {
doPost();
}
});
function doPost () {
// post
$scope.post = true;
}
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
<button ng-click="changeVar()">Click me</button>
<hr ng-if="onevariable" />
<code ng-bind="onevariable"></code>
<hr />
<code ng-bind="'Post? ' + post"></code>
</body>
</html>
I think $watch would be an option
$scope.$watch('onevariable', function(newValue, oldValue) {
$http.post('someurl',{onevariable:newValue }).then(function(){...})}
});
More about $watch

Angular components with javascript models and ngModel

I'm trying to create an angular component, a timepicker, using plain javascript models, I want the controller of the component expose an api and also working with ngModel.
I'm pretty newbie with angular and don't know how to work with ngModel. I have two inputs inside the template with hours and minutes. My problem is that I don't know how to pass the ngmodel parameters to the controller.
I've prepared a plunker:
http://plnkr.co/edit/aal3VP?p=preview
(function() {
var app = angular.module('plunker', []);
function DemoController() {
this.tpVal = {
hours: 10,
minutes: 0
};
}
app.controller('DemoController', DemoController);
function TimePickerModel(config) {
this.show = config.show || true;
this.hours = null;
this.minutes = null;
}
function TimePickerController() {
// API for state
this.model = new TimePickerModel({});
}
TimePickerController.prototype.show = function showTimePicker() {
this.model.show = true;
};
TimePickerController.prototype.hide = function hideTimePicker() {
this.model.show = false;
};
TimePickerController.prototype.setHours = function setHoursTimePicker(hours) {
this.model.hours = hours;
};
TimePickerController.prototype.setMinutes = function setMinutesTimePicker(minutes) {
this.model.minutes = minutes;
};
TimePickerController.prototype.setValue = function setValueTimePicker(value) {
this.model.hours = value;
this.model.minutes = value;
};
app.directive('timepicker', function($compile) {
return {
restrict: 'AE',
controller: 'TimePickerController',
scope: {},
require: 'ngModel',
templateUrl: 'timepicker.html',
link: function(scope, element, attrs, ngModel) {
//console.log('Model val: ' + ngModel.$modelValue);
//console.log('View val: ' + ngModel.$viewValue);
ngModel.$render = function() {
//Do something with your model
console.log(scope.model);
var actualValue = ngModel.$modelValue;
console.log('Model val: ' + ngModel.$modelValue.hours);
console.log('View val: ' + ngModel.$viewValue.hours);
//console.log(element.find('input')[0]);
//element.find('input')[0].val(actualValue.hours);
}
}
};
});
app.controller('TimePickerController', TimePickerController);
})();
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<script data-require="angular.js#1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<script data-require="angular.js#1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-route.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="DemoController as ctrl">
{{ctrl.tpVal}}
<timepicker ng-model="ctrl.tpVal"></timepicker>
</div>
</body>
</html>
ng-model is a standard angular directive to bound inputs values to scope property, you don't need to inject it or call the directive property with the same name. If you want to inject the values from the controller into the directive, you can use scope property for that.
in directive:
scope: {
model : '=time'
},
in index.html
<timepicker time="ctrl.tpVal"></timepicker>
Check that modification: http://plnkr.co/edit/cJ0mjI?p=preview.
You also can see how changing model value inside the directive can propargate outside by adding dummy increaseHours function in directive;

Add ng-if attribute to a element from within a directive

I have a AngularJS directive which takes an ID and makes a lookup on this ID to get col-width, hide-state and order for a given flexbox element.
What I d like to do is to add a ng-if=false attribute to the element if its hide-state is true. Is there any way, how I can add ng-if attribute from within a directive to a element?
My current code:
.directive("responsiveBehaviour", ['ResponsiveService', function(ResponsiveService){
var linkFunction = function(scope, element, attributes){
var id = attributes["responsiveBehaviour"];
var updateResponsiveProperties = function(){
element.attr("column-width", ResponsiveService.getWidth(id));
if(ResponsiveService.getOrder(id)){
element.attr("order", ResponsiveService.getOrder(id));
}
if(ResponsiveService.getHidden(id) == true){
element.attr("hidden", "");
} else {
element.removeAttr("hidden");
}
};
if(id) {
scope.$watch('device', function () {
updateResponsiveProperties();
});
}
};
If I add
element.attr("ng-if", false);
instead of
element.attr("hidden", "");
it just renders out the ng-if attribute to the element but there is no action happening, so the element is still rendered and visible although ng-if is false.
Do you have any idea on how to achieve what I am looking for?
Thanks in advance.
Greets
something like below:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive( 'test', function ( $compile ) {
return {
restrict: 'E',
scope: { text: '#' },
template: '<p ng-click="add()">Jenish</p>',
controller: function ( $scope, $element ) {
$scope.add = function () {
var el = $compile( "<test text='n'></test>" )( $scope );
$element.parent().append( el );
};
}
};
});
working plunk is here
Update
Here is simple example as you requested.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.add = function () {
alert('Jenish');
$scope.cond = false;
}
$scope.cond = true;
});
app.directive( 'test', function ( $compile ) {
return {
restrict: 'E',
template: '<p ng-click="add()">Click me to hide</p>',
link: function ( $scope, $element, attr ) {
var child = $element.children().attr('ng-if', 'cond')
console.log($element)
$compile(child)($scope);
}
};
});
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.13/angular.js" data-semver="1.3.13"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<test></test>
</body>
</html>
I hope this would help you.

A validation directive that reacts to another value works in 1.0.x but not in 1.2.x

In angular js, I want to create a validator that will cause the ng-model value to become invalid when another value is specified. Now I have something that works fine for angular js 1.1.4 (which I was using because I was using an old plunkr), but when I switch to 1.1.5, it stops working.
I am sure I am doing something wrong with the scope, but I am not sure what.
Here is my code (plunkr here: http://plnkr.co/edit/Ug9oM1LNqPpTsONhRTnG?p=preview)
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.doSomething = function () {
alert('Submitted!');
}
$scope.data = {};
$scope.data.value = new String('blah');
$scope.data.value.$$error = 'My Error';
$scope.data.toggleError = function() {
if ($scope.data.value.$$error) {
$scope.data.value.$$error = null;
}
else {
$scope.data.value.$$error = "SOME ERROR";
}
};
console.log($scope.data.value instanceof String);
});
app.directive('serverError', function (){
return {
require: 'ngModel',
scope:true,
link: function(scope, elem, attr, ngModel) {
scope.$watch('attr.errorValue', function() {
console.log("The error value is " + scope.errorValue);
ngModel.$setValidity('serverError', scope.errorValue == null);
});
}
};
});
Here is my HTML:-
<!DOCTYPE html>
<html ng-app="angularjs-starter">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<form name="myForm" ng-submit="doSomething()">
<input type="text" name="fruitName" ng-model="data.value" serverError errorValue="data.value.$$error" />
<div>{{ data.value.$$error }}</div>
<span class="invalid" ng-if="myForm.fruitName.$error.serverError">
{{data.value.$$error}}
</span>
<br/>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
<input type="button" ng-click="data.toggleError()" value="Toggle Error"/>
</form>
</body>
</html>
As soon as I change from 1.1.3 to 1.2.0, my directive stops working.
I worked it out.
http://plnkr.co/edit/Ug9oM1LNqPpTsONhRTnG?p=preview
The directive is simple enough:-
app.directive('serverError', function($parse) {
return {
// restrict to an attribute type.
restrict: 'A',
// element must have ng-model attribute.
require: 'ngModel',
// scope = the parent scope
// elem = the element the directive is on
// attr = a dictionary of attributes on the element
// ctrl = the controller for ngModel.
link: function(scope, elem, attr, ctrl) {
scope.$watch(attr.serverError, function(newValue, oldValue) {
if (newValue != null) {
ctrl.$setValidity('serverError', false);
}
else {
ctrl.$setValidity('serverError', true);
}
});
}
};
});

Categories

Resources