How to $watch multiples properties simultaneous and interpolate into one expression? - angularjs

Suppose two input fields - name and text. How to simultaneous watch this two fields and interpolate their value into one expression?
Thanks!
Update 9/7/2014:
I did this Plunkr with a working version of the code :)
Thanks Mohammad Sepahvand!
Code:
<!doctype html>
<html ng-app="myApp">
<head>
<title>Interpolate String Template Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.js"></script>
<script type="text/javascript">
angular.module('myApp', ['emailParser']).controller('MyController', ['$scope', 'EmailParser', function ($scope, EmailParser) {
// init
$scope.to = '';
$scope.emailBody = '';
$scope.$watchCollection('[to, emailBody]', function (newValues, oldValues) {
// do stuff here
// newValues and oldValues contain the new and respectively old value
// of the observed collection array
if (newValues[0] && newValues[1]) { // there's name and some text?
$scope.previewText = EmailParser.parse(newValues[1], {to: $scope.to});
}
});
}]);
angular.module('emailParser', []).config(['$interpolateProvider', function ($interpolateProvider) {
$interpolateProvider.startSymbol('__');
$interpolateProvider.endSymbol('__');
}]).factory('EmailParser', ['$interpolate', function ($interpolate) { // create service
return {
parse: function (text, propertiesToBeInterpolated) { // handle parsing
var template = $interpolate(text);
return template(propertiesToBeInterpolated);
}
};
}]);
</script>
</head>
<body>
<h3>Instructions in readme.md file - please read before!</h3>
<div id="emailEditor" ng-controller="MyController">
<label>*Name:</label>
<input ng-model="to" type="text" placeholder="Ex.: John"/>
<br><br>
<label>*Text:</label><br>
<textarea ng-model="emailBody" cols="25" rows="10" placeholder="Write something"></textarea>
<p style="color:red;">*required</p>
<div>
<pre>__previewText__</pre>
</div>
</div>
</body>
</html>

You can use the $watchGroup method that was added in angular 1.3:
$scope.$watchGroup(['prop1', 'prop2'], function(newValues, oldValues, scope) {
var prop1 =newValues[0];
var prop2 =newValues[1];
});
Or you could use $watchCollection which has been available since angular 1.1.4:
scope.$watchCollection('[prop1, prop2]', function(newValues, oldValues){
});

Related

Angularjs binding value from service

I wish to share a service value between one or more controllers (only one in the following example but that's not the point).
The problema is that the value hold in the service is not bound and shown in the view.
The code (derived from angularjs basic service example) is:
(function(angular) {
'use strict';
angular.
module('myServiceModule', []).
controller('MyController', ['$scope', 'notify','$log', function($scope, notify, $log) {
$scope.callNotify = function(msg) {
notify.push(msg);
};
$scope.clickCount = notify.clickCount();
$log.debug("Click count is now", $scope.clickCount);
}]).
factory('notify', ['$window','$log', function(win,$log) {
var msgs = [];
var clickCounter = 0;
return {
clickCount: function() {
clickCounter = msgs.length;
$log.debug("You are clicking, click count is now", clickCounter);
return clickCounter;
},
push: function(msg) {
msgs.push(msg);
clickCounter = msgs.length;
$log.debug("Counter is", clickCounter);
if (msgs.length === 3) {
win.alert(msgs.join('\n'));
msgs = [];
}
}
}
}]);
I wish the counter to be displayed on page:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-services-usage-production</title>
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myServiceModule">
<div id="simple" ng-controller="MyController as self">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click {{3-self.clickCount}} times more to see an alert)</p>
</div>
<div>You have clicked {{clickCount}} times</div>
</body>
</html>
See it in action on plunker
UPDATE: corrected the trivial errors is html and service code as suggested by #SehaxX
First your HTML is wrong. Your last div is not in the div of Controller, and you dont need the self.
<body ng-app="myServiceModule">
<div id="simple" ng-controller="MyController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click {{3-self.clickCount}} times more to see an alert)</p>
<div>You have clicked {{clickCount}} times</div>
</div>
</body>
Also in your service you are missing return:
clickCount: function() {
clickCounter = msgs.length;
$log.debug("You are clicking, click count is now", clickCounter);
return clickCounter;
},
And in your controller you only once call the notify.clickCount() so you need to add it to the method:
$scope.callNotify = function(msg) {
notify.push(msg);
$scope.clickCount = notify.clickCount();
$log.debug("Click count is now", $scope.clickCount);
};
Here also a working code pen with "Controller as self" if you want. But then in controller you must use this and not $scope.
Cheers,

How to update model in ui-codemirror

I have two separate controllers which shared a property. If the first controller changes the property the second controller should recognize it and should change the text in the codemirror text area. I tried to figure it out in this fiddle example but I could not find a solution.
var app = angular.module('myApp', ['ui.codemirror']);
app.service('sharedProperties', function() {
var objectValue = {
data: 'test object value'
};
return {
setText: function(value) {
objectValue.data = value;
},
getText: function() {
return objectValue;
}
}
});
app.controller('myController1', function($scope, $timeout, sharedProperties) {
$scope.setText = function(text){
sharedProperties.setText(text);
console.log(sharedProperties.getText().data);
}
});
app.controller('myController2', function($scope, sharedProperties) {
$scope.editorOptions = {
lineWrapping: true,
lineNumbers: true,
readOnly: 'nocursor',
mode: 'xml'
};
$scope.mappingFile = sharedProperties.getText();
console.log($scope.mappingFile);
});
<div ng-app="myApp">
<div ng-controller="myController1">
<input type="text" ng-model="newText"></input>
<button ng-click="setText(newText)">Set Text</button><br/>
</div>
<div ng-controller="myController2">
<ui-codemirror ui-codemirror-opts="editorOptions" ng-model="mappingFile.data" ui-refresh="true"></ui-codemirror>
</div>
</div>
At first, the way that you're doing you have 2 controllers in the same page but without any relation, I'd suggest you to make one of them as child of another.
So, to achieve what you want you need do a kind of watch on that variable from the parent controller.
Steps:
Use the $broadcast to send data to the child controller
$scope.$broadcast('newText', $scope.newText);
Use $on to receive the data from the parent controller:
$scope.$on('newText', function(event, text) {
...
});
Here's the code working based on your original code:
(function() {
'use strict';
angular
.module('myApp', ['ui.codemirror'])
.controller('myController1', myController1)
.controller('myController2', myController2);
myController1.$inject = ['$scope', '$timeout'];
function myController1($scope, $timeout) {
$scope.setText = function(text) {
console.log('Sent...', $scope.newText);
$scope.$broadcast('newText', $scope.newText);
}
}
myController2.$inject = ['$scope'];
function myController2($scope) {
$scope.editorOptions = {
lineWrapping: true,
lineNumbers: true,
readOnly: 'nocursor',
mode: 'xml'
};
$scope.$on('newText', function(event, text) {
if (!text) return;
$scope.mappingFile = text;
console.log('Received... ', $scope.mappingFile);
});
}
})();
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.17.0/codemirror.min.js"></script>
<script src="https://rawgit.com/angular-ui/ui-codemirror/master/src/ui-codemirror.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<div ng-app="myApp">
<div ng-controller="myController1">
<input type="text" ng-model="newText">
<button ng-click="setText()">Set Text</button>
<hr>
<div ng-controller="myController2">
<textarea ui-codemirror-opts="editorOptions" ng-model="mappingFile"></textarea>
</div>
</div>
</div>
</body>
</html>
Some notes:
You don't need to pass ngModel as parameter in your ngClick, you can access it directly in your controller simply calling $scope.newText (as I did);
<input> is a self-closing tag, so of course, you don't need to close it.
I hope it helps.

How can i access next sibling's proprties/attributes of element using angularjs?

I am trying to access the class of button which is placed next to paragraph. As soon as the focus gets on paragraph the class of button should change. Please see HTML the code below :
<div>
<span id="key" class="col-lg-2">email : </span>
<span ng-focus="focused($event)" id="value" contenteditable="true">abcd#abc.com</span>
<input type="submit" name="update" value="update"
class="update-hide" data-ng-click="updateValue($event)">
</div>
The angular code for controller is :
var TestParseController = function($scope, $window, $http, $routeParams, $sce,
$compile) {
$scope.focused = function(focusedValue) {
var par = focusedValue.target.parentNode;
var nodes = par.childNodes;
nodes[2].className="update-regular";
}
}
How could this be done in angular way? I know its something like $$nextSibling , but accessing the class name is problamatic. I have googled a lot and found nothing. Please help!!!
Please suggest any dynamic way i can not hardcode any id for button also.
This can be like below:
angular.module("app",[])
.controller("MainCtrl", function($scope) {
$scope.focused = function(focusedValue) {
var par = focusedValue.target.parentNode;
angular.element(par.querySelector("input[type=submit]")).addClass("update-regular");
}
});
.update-regular {
background: red;
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller="MainCtrl">
<div>
<span id="key" class="col-lg-2">email : </span>
<span ng-focus="focused($event)" id="value" contenteditable="true">abcd#abc.com</span>
<input type="submit" name="update" value="update"
class="update-hide" data-ng-click="updateValue($event)">
</div>
</body>
</html>
But mostly DOM manipulation must be done via directives. Controller must act mostly like ViewModel. So if you could create a directive and add it to the contenteditable span tag.
angular.module("app", [])
.directive("focusAdjacentButton", function () {
return {
restrict: "AEC",
link: function (scope, element, attrs) {
element.on("focus", function () {
angular.element(element[0].parentNode.querySelector("input[type=submit]")).addClass("update-regular");
});
// if you want to remove the class on blur
element.on("blur", function () {
angular.element(element[0].parentNode.querySelector("input[type=submit]")).removeClass("update-regular");
});
}
}
});
In your HTML:
<span focus-adjacent-button id="value" contenteditable="true">abcd#abc.com</span>

function not found using cache factory?

im trying to do a little example to learn on using cachefactory but i get the error that
"Argument 'CacheSampleController' is not a function"
this is my app
var eventsApp = angular.module('eventsApp', ['ngResource'])
.factory('myCache', function($cacheFactory) {
return $cacheFactory('myCache', {capacity:3});
});
this is the hmtl file:
<div ng-controller="CacheSampleController" style="padding-left:20px; padding-right:20px">
key: <input type="text" ng-model="key"/><br/>
value: <input type="text" ng-model="value"/><br/>
<button type="button" class="btn" ng-click="addToCache(key, value)">Add To Cache</button><br/>
<br/>
<br/>
<input type="text" ng-model="keyToRead"/><br/>
<h3>Value from cache: {{readFromCache(keyToRead)}}</h3>
<h3>Cache Stats: </h3>{{getCacheStats()}}
</div>
and this is the controller
eventsApp.controller('CacheSampleController',
function CacheSampleController($scope, myCache) {
$scope.addToCache = function(key, value) {
myCache.put(key, value);
};
$scope.readFromCache = function(key) {
return myCache.get(key);
};
$scope.getCacheStats = function() {
return myCache.info();
};
}
);
im not sure if it could be a syntax error or something else that im just not seeing?
thanks
Add the ng-app to the htm elemvent
<html ng-app="eventsApp">
Load your js files in this order
<script src="app.js"></script>
<script src="script.js"></script>
And your service should look like this
var eventsApp = angular.module('eventsApp', [])
.factory('myCache', function($cacheFactory) {
return $cacheFactory('myCache', {capacity:3});
});
Check this: plnkr
You must include (CacheSampleController.js) to your page like this:
<script src="/js/controllers/CacheSampleController.js"></script>
Add it after (app.js) line and I am sure it will work.

Angular - default value for model

Say I have some html as follows:
<html>
<head> angular etc. </head>
<body ng-app>
<div ng-controller="MyCtrl">
<input ng-model="weight" type="number" min="{{minWeight}}" max="{{maxWeight}}">
<p>{{weight}}</p>
</div>
</body>
</html>
and the following controller:
function MyCtrl($scope){
$scope.weight = 200;
$scope.minWeight = 100.0;
$scope.maxWeight = 300.0;
}
The "min" and "max" will show the user their input is bad, and if I hard code them to say, 100 and 300, it will make sure the value is never bound to the model at all (why isn't the behavior the same??). What I'd like to do is only actually change the "weight" if the value meets the input requirements. Any thoughts?
I don't fully understand what are you trying to do.
HTML:
<html>
<head> angular etc. </head>
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
<input ng-model="weight" type="number" min="{{minWeight}}" max="{{maxWeight}}">
<p>{{weight}}</p>
</div>
</body>
</html>
Angular: [Edit]
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope', function MyCtrl($scope){
$scope.weight = 200;
$scope.minWeight = 100.0;
$scope.maxWeight = 300.0;
$scope.$watch('weight', function (newValue, oldValue) {
if(typeof newValue === 'number') {
if(newValue > $scope.maxWeight || newValue < $scope.minWeight) {
$scope.weight = oldValue;
}
}
});
}]);
But here is an example I made in jsFiddle. I hope this was a solution you were looking for.
[Edit]
http://jsfiddle.net/migontech/jfDd2/1/
[Edit 2]
I have made a directive that does delayed validation of your input field.
And if it is incorrect then it sets it back to last correct value.
This is totally basic. You can extend it to say if it is less then allowed use Min value, if it is more then allowed use Max value it is all up to you. You can change directive as you like.
http://jsfiddle.net/migontech/jfDd2/2/
If you have any questions let me know.

Resources