I'm new to angularJS and I'm trying to design a form including 2 input fields which are internally tied to the same scope variable: saying the first field is the speed in km/h and the second field is the speed in m/s. The behavior I'd like to obtain is: whatever field the user changes the other field would be updated.
The problem I'm running into is the following:
When the speed in m/s is changed by the user, the speed in km/h is updated which lead to update again the speed in m/s, which update the speed in km/h until no value change. The side effect is when the speed in m/s is a round number saying 1 and the user change the value to make it decimal entering the decimal dot - the update loop lead to remove the dot because from a calculus point of view 1. is 1.
What is the best way to avoid that?
Here are the fragments of code (html):
<label for="SpeedKm">Speed (Km/h):</label>
<input id="SpeedKm" name="SpeedKm" type="text"
placeholder="km/h"
ng-model="speedKmh"
ng-model-options="{ getterSetter: true}">
>
<label for="SpeedMs">Speed (m/s):</label>
<input id="SpeedMs" name="SpeedMs" type="text"
placeholder="m/s"
ng-model="speedMs"
ng-model-options="{ getterSetter: true}">
>
and the controller
angular.module('speedconverter')
.controller('MainCtrl', ['$scope', function ($scope) {
$scope.speed = 10; //km/h
$scope.speedKmh = function(newValue) {
if (angular.isDefined(newValue)) {
$scope.speed = newValue;
}
return $scope.speed;
};
$scope.speedms = function(newValue) {
if (angular.isDefined(newValue)) {
$scope.speed = newValue*3.6;
}
return $scope.speed/3.6;
};
});
Thank you
Related
I have 2 date fields: departure / arrival.
Both have validation on them using a directive.
Here is what the directive looks like for the "arrival" date range:
(function(angular) {
'use strict';
function dateRangeToValidatorDirective () {
return {
restrict: 'A',
require : 'ngModel',
link : function (scope, element, attrs, ngModelCtrl) {
function validateToDateRange (value) {
var valid = true;
if (scope.$eval(attrs.dateRangeValue) && value) {
var arrivalDate = Date.parse(value);
var departureDate = Date.parse(scope.$eval(attrs.dateRangeValue));
valid = arrivalDate >= departureDate;
if (valid) {
ngModelCtrl.$setValidity('toDateRange', true);
ngModelCtrl.$setValidity('fromDateRange', true); // Why doesn't this work?
}
else {
ngModelCtrl.$setValidity('toDateRange', false);
}
}
return value;
}
ngModelCtrl.$parsers.push(validateToDateRange);
}
}
}
angular
.module('components.shared')
.directive('dateRangeToValidator', dateRangeToValidatorDirective)
})(window.angular);
HTML usage:
<input type="text" name="arrivalDate" maxlength="10" required
date-picker date-range-to-validator
date-range-value="$ctrl.newFlight.departureDate"
placeholder='MM/DD/YYYY'
ng-model="$ctrl.newFlight.arrivalDate"
id="nf_arrivalDate" size="10" />
<div class="input-error"
ng-show="$ctrl.newFlight.departureDate.length &&
$ctrl.newFlight.arrivalDate.length &&
newFlight.arrivalDate.$dirty &&
newFlight.arrivalDate.$invalid">
Arrival Date cannot precede Departure Date!
</div>
The "departure" date range is the same with a few minor differences.
(no need to fill up the screen with the same code)
This works except in the following use case(s):
Step 1. Set departure date: 12/28/2017
Step 2. Set arrival date: 12/27/2017
===> Error is shown "Arrival Date cannot precede Departure Date!"
Step 3. Modify departure date: 12/26/2017
Actual: "Arrival Date" error message does not go away.
Expected: "Arrival Date" error message goes away.
Question: How do I get one directive that is on one element to affect the $valid state of another element that is using another directive?
Please notice my comment in the if (valid) block where I am asking why doesn't this work? If I set the validity of the model controller in one directive - why can't I get at the same model controller from another directive? I assume that this is case because that isn't working...
I ended up adding a couple of controller functions that would get invoked depending on the date field that changed using ng-change. First I needed to expose the form to my controller; hence you will notice that my form name changed. Once I could access the form from my controller, changing the validity of the element was easy.
function checkDepartureDate() {
if (ctrl.newFlightForm.arrivalDate.$valid &&
ctrl.newFlight.departureDate.length &&
ctrl.newFlightForm.departureDate.$invalid) {
ctrl.newFlightForm.departureDate.$setValidity("fromDateRange", true);
}
}
}
<input type="text" name="arrivalDate" maxlength="10" required
date-picker date-range-to-validator
date-range-value="$ctrl.newFlight.departureDate"
placeholder='MM/DD/YYYY'
ng-model="$ctrl.newFlight.arrivalDate"
ng-change="$ctrl.checkDepartureDate()"
id="nf_arrivalDate" size="10" />
Hi i have inputs like this
<input type="text" ng-model="tbl.Public">
<input type="text" ng-model="tbl.Private">
<input type="text" ng-value="tbl.Public--tbl.Private" ng-model="tbl.Total">
the above form will working fine it will sum the Public and Private value and put it in tbl.Total field. My problem is in edit form where value of tbl.Total, tbl.Public, tbl.Private are assign from database.
js
$scope.tbl.Public=10;
$scope.tbl.Private=25;
$scope.tbl.Total=35;
now after assigning a value from js when i change value of tbl.Public or tbl.Private in form it is not affecting a tbl.Total it should sum the two value and put it in tbl.Total field.
Thank you for your any help and suggestion.
ng-value is usually used on radiobuttons and option elements, it's not a good fit for your use case.
A better thing to do would be implementing an updateTotal() function combined with ng-change. I would also recommend changing your input types to number so you're not allowing users to sum text.
<input type="number" ng-model="tbl.Public" ng-change="updateTotal();">
<input type="number" ng-model="tbl.Private" ng-change="updateTotal();">
<input type="number" ng-model="tbl.Total">
In your controller:
$scope.updateTotal = function() {
$scope.tbl.Total = $scope.tbl.Public + $scope.tbl.Private;
}
It should be like this to prevent from concatenate
$scope.updateTotal = function() {
var Public = Number($scope.tbl.Public || 0);
var Private = Number($scope.tbl.Private || 0);
$scope.tbl.Total = Public + Private;
}
I have an update function and a number of input boxes. Each input has an ng-blur attached to it so that the update function is called whenever the cursor leaves the box.
$scope.update = function(data) {
console.log(data); //outputs value in the textbox
//how can I output/access the key?
}
The input for name look like this:
<input type="text" ng-model="user.name" ng-blur="update(user.name)"/>
As I need to be able to post a JSON object in the form {"name" : "bob smith"} what's a good way of generating the "key" of the object bearing in mind that it will differ depending on the input box that's being used at the time?
EDIT ↓
I have made this jsfiddle to illustrate a way to do it more cleanly & that would scale more easily: http://jsfiddle.net/kuzyn/k5bh0fq4/5/
EDIT ↑
Why not simply pass a second string argument? It's not a fancy way to do it but it would work:
<input type="text" ng-model="user.name" ng-blur="update(user.name, 'name')"/>
And
$scope.update = function(data, key) {
console.log(key, data);
}
It might be a little more work but it is much more scalable. You could make use of the ng-form and naming your form inputs. By naming your form and inputs, you are creating a form reference on your scope via $scope[form-name]. Each named input within that form then sets an input reference via $scope[form-name][input-name].
I'm coding this in coffeescript (to preserve my sanity, sorry)
form
<form name="myForm">
<input name="name" ng-model="user.name" ng-blur="update(user)"/>
<input name="email" ng-model="user.email" ng-blur="update(user)"/>
<input name="other" ng-model="user.other" ng-blur="update(user)"/>
</form>
update & save func
# key is what changed in case you need to do something special on the put call to server
$scope.save = (data, key)->
# pseudo-code
$scope.update = (data)->
for name, input of $scope.myForm
if input?.$dirty
$scope.save data, name
break
docs - https://docs.angularjs.org/guide/forms
codepen - http://codepen.io/jusopi/pen/LGpVGM?editors=101
I have a large amount of code that I inherited. It is working according to the original spec, so although it may or may not be the "right" way of doing things, it does work and I'd prefer not to mess with it. (I am VERY new to angular, and under a tight deadline - I don't have time right now to "fix" things that aren't broken, even if they're not being done the "correct" way.)
I have a checkbox and a group of address fields. When the checkbox is checked, a function runs that populates the address fields.
<input id='as_check' type="checkbox" ng-model='model.prepop_addr' ng-change="model.handlePrepopAddress()">
<input type="text" placeholder="Address 1" required ng-model='model.payment.addr1'/>
<input type="text" placeholder="Address 2" ng-model='model.payment.addr2'/>
<input type="text" placeholder="City" required ng-model='model.payment.city'/>
<input type="text" placeholder="State" required ng-model='model.payment.state'/>
<input type="text" placeholder="Zip" required ng-model='model.payment.zip'/>
In my controller, I have a function called handlePrepopAddress():
model.handlePrepopAddress = function () {
if (model.prepop_addr) {
console.log("prepop is true");
var del_addr = $window.user_state.deliveryAddress;
model.payment.addr1 = del_addr.address;
model.payment.addr2 = null;
model.payment.city = del_addr.city;
model.payment.state = del_addr.state;
model.payment.zip = del_addr.zip;
} else {
console.log("prepop is false");
model.payment.addr1 = null;
model.payment.addr2 = null;
model.payment.city = null;
model.payment.state = null;
if (!model.payment.saved) {
model.payment.zip = null;
}
}
return true;
};
When I click the checkbox, this runs correctly and updates the various fields. Requirements have changed, and the client now wants the checkbox defaulted to checked, which would then default the values of the fields. Rather than duplicating code, I thought I'd be able to just run the function at the start of the controller, like this:
place_order.controller('AsCreditCardCtrl', ['$scope', 'PaymentModel', function ($scope, model) {
$scope.model = model;
model.prepop_addr = true;
model.handlePrepopAddress();
}]);
The function is running, as the console.log statements I have in the function are running. But the view is not updating; the checkbox is visibly unchecked and the fields are empty aside from the initial placeholder values.
What am I missing? How can I make the function update the view?
You can try
var i= model.handlePrepopAddress();
It might work.
I need to output the values and perform some calculations with the values of AngularUI sliders.
I have this mark-up:
<body ng-controller="sliderDemoCtrl">
<div ui-slider="{range: 'min'}" min="0" max="50" ng-model="demoVals.slider"></div>
<p>The value so far is:
<span ng-bind="demoVals.slider"></span> // I want this to show 0 until the slider is moved
</p>
<p>The calculated value is: <input type="text" ng-model="calculated" value=" {{ calculated }}" placeholder=0 /></p>
</body>
Firstly, I can't work out how to initialise that <span ng-bind="demoVals.slider"></span> with a default value of 0 or "None". At the moment it is blank until the slider moves. How can I set this to a default value?
Secondly, I want the value of {{ calculated }} to be a number multiplied by the value of the slider. How can I pass the value of the slider to ng-model="calculated" or access the value from within my controller?
Here's my Plunkr
You can set the initial value of the ng-model in the $scope:
$scope.demoVals = {};
$scope.demoVals.slider = 0;
And to keep the value of calculated in one way sync with the demoVals.slider, you can use a $watch on the $scope:
$scope.$watch('demoVals.slider', function (newVal) {
if (typeof newVal !== 'undefined') {
$scope.calculated = newVal * 3.14159; // Use any value here.
}
});
Working demo.
The way ng-model works, it binds to the scope property you define in it's attribute value in the markup. If no such scope property exists, it gets created on initialization.
Thus, to set start value just create that model property. To adjust another scope property based on changes to the slider model you can use $watch to listen for changes to the slider model property
app.controller('sliderDemoCtrl', function($scope) {
/* ng-model bound to this object, so define it's start value*/
$scope.demoVals={slider:10}
/* watch for changes*/
$scope.$watch('demoVals.slider',function(newVal,oldVal){
if(newVal){
$scope.calculated=2*newVal;
}
})
$scope.calculated = 0;
});
DEMO