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.
Related
Recently I am learning Angularjs, my code seems not work as expected:
this is my div:
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="data.name" value=""/>
{{data.count}}
</div>
</div>
my controller is:
<script>
var app = angular.module('myApp',[]);
app.controller('myController', function($scope) {
$scope.data = {
name:"tom",
count = 0
}
$scope.$watch('data', function(oldValue,newValue) {
++$scope.data.count;
},true);
})
</script>
what I expect is when I type something in the <input> box, the {{data.count}} will increase by 1 each time. However the code is initially 11 and each time I make changes in the input field, the count is increased by 11, can someone help me find where have I done wrong? Thanks a lot in advance.
Why this happen?
Watcher calls multiple times because you created watcher for full object data. Flag true will create sub-watcher for every value in object.
Its a proper behavior. I believe you want something like:
$scope.$watch('data', function(oldValue,newValue) {
if(oldValue.name != newValue.name){
++$scope.data.count;
}
},true);
Demo Fiddle
The second solution is to watch on name only:
$scope.$watch(function(){
return $scope.data.name
}, function(oldValue,newValue) {
++$scope.data.count;
});
Here is a another way to do it. Use the ng-keydown directive and update the count only when a key is pressed inside the input element.
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
$scope.data = {
name: "tom",
count: 0
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app='myApp'>
<input type="text" ng-model="data.name" value="" ng-keydown="data.count = data.count+1" /> {{data.count}}
</div>
I am trying to do a function inside of a controller that uses an if else to change the return. And it works until I change the drop down, and then stops working completely. It does not seem to be working, not sure if I am doing something wrong, or you just cannot do this type of function. Any help would be appreciated.
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-sanitize.js"></script>
<script>
var app = angular.module('newApp', ['ngSanitize']);
app.controller('player1ctrl', function ($scope) {
$scope.playerName = "Hero";
$scope.playerCard = "A<br />♠";
$scope.color = function () {
//return $scope.playerName;
var arr = $scope.playerCard.split("<br />");
var rtn = "";
if (arr[1] == "♠" || arr[1] == "♣") {
return "Black";
}
else {
return "Red";
}
};
});
</script>
<title>test ngbindhtml</title>
<meta charset="utf-8" />
</head>
<body ng-app="newApp">
<div ng-controller="player1ctrl">
Card selected: <p ng-bind-html="playerCard"></p>
Suit Color: {{color()}}
<select name="playerCard" ng-model="playerCard">
<option value="2<br /> ♠">2 ♠</option>
<option value="3<br /> ♠">3 ♠</option>
<option value="4<br /> ♥">4 ♥</option>
</select>
</div>
</body>
</html>
First of all - You have space in ♠:
if(arr[1] == " ♠" || arr[1] == " ♣")
Second - You need use:
ng-bind-html-unsafe="playerCard"
in Your template or use trustAsHtml in controller;
Plunker
You cannot bind an HTML string without "trusting" it in Angular. Your "playerCard" variable, with the <br /> is causing the issue. You need to inject $sce into your controller and then call the trustAsHtml function:
...
app.controller('player1ctrl', function ($scope, $sce) {
...
$scope.playerCard = $sce.trustAHtml("A<br />♠");
Doing this should allow you to still use [the correct] ng-bind-html like you currently are. You can also turn this into a directive quite easily.
I'm in a project that uses angularjs and rails. So, i'm using this library too:
https://github.com/FineLinePrototyping/angularjs-rails-resource
Well, when i'm using the controller as syntax from angularjs, some strange behaviour is happening. You can see that in this plunker example:
http://plnkr.co/edit/i4Ohhh8llS7WN68sLX5q?p=preview
The promise object returned by the remote call in first controller using the angularjs-rails-resource library in some way is setting the instance variable that belongs to the second controller. I don't know if it is a bug in the library, or an angular behaviour that i should know. Anyway, is clearly an undesirable behaviour.
Here is the same plunker code (index.html):
<!doctype html>
<html ng-app="Demo">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.js"></script>
<script src="https://rawgit.com/FineLinePrototyping/dist-angularjs-rails-resource/master/angularjs-rails-resource.min.js"></script>
<script src="example.js"></script>
</head>
<body>
<div ng-controller="Controller1 as ctrl1">
<form>
<label>should appear first remote</label>
<input type="text" ng-model="ctrl1.remote.name"/><br>
<label>should appear first local</label>
<input type="text" ng-model="ctrl1.local.name"/>
</form>
</div>
<br>
<div ng-controller="Controller2 as ctrl2">
<form>
<label>should appear second local</label>
<input type="text" ng-model="ctrl2.remote.name"/><br>
<label>should appear second local</label>
<input type="text" ng-model="ctrl2.local.name"/>
</form>
</div>
</body>
</html>
My angularjs code (example.js):
angular.module('Demo', ['rails']);
angular.module('Demo').controller('Controller1', ['$scope', 'Remote', function($scope, Remote) {
ctrl = this;
ctrl.remote = {};
Remote.get().then(function(remote) {
ctrl.remote = remote;
});
ctrl.local = {};
ctrl.local.name = "first local";
}]);
angular.module('Demo').controller('Controller2', ['$scope', function($scope) {
ctrl = this;
// SAME VARIABLE NAME
// WILL RECEIVE VALUE FROM REMOTE CALL ON FIRST CONTROLLER!!!
ctrl.remote = {};
ctrl.remote.name = "second local";
// SAME VARIABLE NAME
ctrl.local = {};
ctrl.local.name = "second local";
}])
angular.module('Demo').factory('Remote', [
'railsResourceFactory',
'railsSerializer',
function (railsResourceFactory, railsSerializer) {
return railsResourceFactory({
url:'clients.json',
name: 'remote',
})
}]
);
clients.json
{
"name":"first remote"
}
Any ideias how fix this without having to change variable names to avoid conflict? Because that way we will just mask the problem.
I report the problem to angularjs-rails-resource library but no answer until now.
You need to use var when declaring your variables, otherwise they're global.
Use
var ctrl = this; instead of just ctrl = this;
Also, 'use strict' is a nice thing to use(and it helps in these situations)
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){
});
<input ng-model='yourName'>
<p>{{yourName}}</p>
when I type some words in the input yourName, the <p> will display what I typed immediately.
===
If I need do some sync in different model,
eg.
<input ng-model='start'>
<input ng-model='end'>
<input ng-model='step'>,default 10.
when I changed the model start to 1, it automatically update the model end to 11,vice versa.
what should I do?
this problem is solved.just add type="number", thanks
<!DOCTYPE html>
<html ng-app>
<head>
<title></title>
<script src="lib/angular/angular.js"></script>
</head>
<body ng-controller='MyController'>
<input type="number" ng-model="start">
<input type="number" ng-model="end">
<input type="number" ng-model="step">
<script>
function MyController($scope){
$scope.$watch('start',function(newStart){
$scope.end = newStart + $scope.step;
console.log(1);
}) ;
$scope.$watch('end',function(newEnd){
$scope.start = newEnd - $scope.step;
console.log(2);
}) ;
$scope.step = 10;
}
</script>
</body>
</html>
In your controller, use a $scope.$watch() to perform behaviour when each property changes.
e.g.
$scope.$watch('dateStart', function (newDateStart) {
if (!newDateStart) return;
$scope.dateEnd = newDateStart;
});
$scope.$watch('dateEnd', function (newDateEnd) {
if (!newDateEnd) return;
$scope.dateStart = newDateEnd;
});
Note that I would recommend that you first check if there is already a value for the other field, or if the user has manually modified it. If so, don't automatically change the other field. Otherwise, you will be forever overwriting what the user chose for the other field.