Updating a value in an array using a function - Angular JS - javascript

I'm relatively new to AngularJS and I've found myself slightly confused.
Here is my controller:
app.controller("calcController", function($scope) {
$scope.interests=[{time:'month',amount:0},
{time:'quarter',amount:0},
{time:'year',amount:0}];
$scope.rates=[{rate:0.01,lower:0,upper:1000,desc:'£0 - £1000'},
{rate:0.02,lower:1000,upper:5000,desc:'£1000 - £5000'},
{rate:0.03,lower:5000,upper:'none',desc:'£5000+'},];
$scope.balance=0;
$scope.updatedIntAmount=function(balance){
if(balance<0){
return 0;
}
else if(balance>=rates[1].lower && balance<rates[1].upper){
return balance*rates[1].rate;
}
else if(balance>=rates[2].lower && balance<rates[2].upper){
return balance*rates[2].rate;
}
else {
return balance*rates[3].rate;
}
}
});
What I want is some way to bind the value of interests.amount to updatedIntAmount.
Here's my HTML:
<div ng-controller="calcController" class="container">
<div class="form-group">
<label for="balInput">Balance:</label>
<input id="balInput" type="text" class="form-control" ng-model="balance">
</div>
<p ng-repeat="interest in interests">{{'per '+interest.time+':'+interest.amount}}</p>
</div>
So what I have is interests.amount dependent on calculateIntAmount, which in turn is dependent on both rates.rate and balance. How can I get these interests.amount values to change whenever balance is updated?
Thanks

Try adding ng-change to your input field like so <input id="balInput" type="text" class="form-control" ng-model="balance" ng-change="updatedIntAmount(balance)">. This will allow you to call the function on a change to the input field and thus do whatever you want within the function in your controller.
You can learn more at the official AngularJS documentation page

You can look into using $scope.$watch to observe any changes in your balance variable, and then update your interests.amount accordingly.
In your controller, you would have:
$scope.$watch('balance', function(newBalance) {
$scope.interests.amount = $scope.updatedIntAmount(newBalance);
});

Lot of errors are present in your code.
as first there is nothing like rates[1] instead you should change it to $scope.rates[1].
Second : your updatedIntAmount function contains rates[3] which is again wrong as you have rates[2] as max limit according to array index rule.
Your updatedIntAmount function return a single value based upon the balance is inserted. in that case after correcting the errors just add :
{{updatedIntAmount(balance)}}
in your html code. it will change as soon as change in balance.
$scope.updatedIntAmount=function(balance){
if(balance<0){
return 0;
}
else if(balance>=$scope.rates[0].lower && balance<$scope.rates[0].upper){
return balance*$scope.rates[0].rate;
}
else if(balance>=$scope.rates[1].lower && balance<$scope.rates[1].upper){
return balance*$scope.rates[1].rate;
}
else {
return balance*$scope.rates[2].rate;
}
}

Related

AngularJS - run check against elements as they come back

I just followed this
JSFiddle example to create a little search box from an array object in my javascript. Now after some tweaking and research on search.object and filter:search:strict. Now that I have it filtering correctly, I modified a checkbox in the template that is checked upon loading the document and switched on or off based on a custom data attribute in the html that is updated by the json array.
How do I run this check once someone clears the search and the old items show back up again?
In the HTML I have this template
<div ng-repeat="o in objects | filter:search:strict | orderBy:'obj_name'" class="objectContainer">
<h3>{{o.obj_name}}</h3>
<label class="userToggleSwitch" >
<input class="userToggleInput" data-isactive="{{o.obj_isactive}}" type="checkbox">
<div class="slider round"></div>
</label>
</div>
In the JS
angular.element(document).ready(function(){
angular.module('searchApp',[])
.controller('searchCtrl', ['$scope','objHolder',function ($scope,objHolder) {
$scope.search = '';
$scope.objects = [];
$scope.objects = objHolder.getobjects();
}])
// fake service, substitute with your server call ($http)
.factory('objHolder',function(){
var objects = objList;
return {
getobjects : function(){
return objects;
}
};
});
});
The JS that modifies the items on document load is using jquery like this
$(document).ready(function(){
//console.log($('.userToggleInput'));
$.each($('.userToggleInput'), function(){
if($(this).data("isactive") == 1){$(this).attr('checked', true);}
if($(this).data("isactive") == 0){$(this).attr('checked', false);}
})
$('.userToggleInput').click(function(){
if ($(this).attr('checked') == undefined) {
// THIS IS WHERE WE WILL MAKE AN AJAX CALL TO A PHP CLASS
// TO REQUEST IF THE USER CAN BE TURNED ON - DUE TO LIC RESTRICTIONS
$(this).attr('checked',true);
} else {
$(this).attr('checked',false);
}
//console.log($(this).attr('checked'));
});
});
Created JS Fiddle to assist in Helping in this manner

Angular $scope property gets overwritten when passed into function as an argument

I have an odd situation that I can't seem to figure out.
In Angular 1.4, I have a variable on the scope object used as a model to collect form data, $scope.videoModel. When a form is submitted, the model gets passed into a function, ng-submit="createVideo(videoModel), where a bunch of processing and regex happens. For example, extract a YouTube id.
Everything works as intended, but even though I am passing in the scope object as an argument (payload) to a function , certain attributes that are updated on the argument, also get updated on the $scope.
For eample, if I pass in $scope.videoModel.youtube into createVideo as payload, then extract the Youtube ID and assign it as payload.youtube = payload.youtube.match(regEx);, the $scope.videoModel.youtube property also gets updated. I can see this since the form value gets changed from a complete url to just the id.
It seems that passing in videoModel as an argument to a function does not create a copy to the variable, but instead references it. I can't seem to find anything about it. I believe I could simply create a new variable like this var tempVar = payload, but that seems wacky and I wonder if I'm doing something fundamentally incorrect.
Does videoModel get passed in a reference and not a copy?
Here is a sampling of the code clipped for brevity.
HTML
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">YouTube Url</label>
<div class="col-sm-10">
<input class="form-control" ng-class="{ 'has-error' : newvideo.youtube.$invalid && newvideo.youtube.$touched, 'is-valid': newvideo.youtube.$valid }" placeholder="Youtube Url" ng-model="videoModel.youtube" name="youtube" ng-pattern="youtubeValidateRegex" required>
<div class="help-block" ng-messages="newvideo.youtube.$error" ng-show="newvideo.youtube.$touched">
<p ng-message="required">YouTube Url is required.</p>
<p ng-message="pattern">Please input a valid YouTube Url.</p>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10" ng-if="!success">
<button class="btn btn-default" ng-click="newVideo(videoModel)" ng-disabled="newvideo.$invalid"> Publish</button>
</div>
</div>
JS
$scope.videoModel = {
youtube: 'https://www.youtube.com/watch?v=VkzVgiYUEIM',
vimeo: 'https://vimeo.com/193391290'
};
$scope.newVideo = function(payload) {
$scope.processing = true;
payload.user = $scope.user._id;
payload.filename = $scope.filename;
console.log(payload);
if(payload.type === 'vimeo') {
var matchVM = payload.vimeo.match($scope.vimeoExtractRegex);
console.log(matchVM);
if(matchVM) { //todo: helpers
payload.vimeo = matchVM[5];
} else {
return toastr.error('Unable to extract Vimeo ID');
}
}
if(payload.type === 'youtube') {
var matchYT = payload.youtube.match($scope.youtubeExtractRegex);
if (matchYT && matchYT[1].length === 11) { //todo: helpers
payload.youtube = matchYT[1];
} else {
return toastr.error('Unable to extract YouTube ID');
}
}
Videos.newVideo(payload)
.then(function(result) {
toastr.success('New video created.');
$scope.processing = false;
$scope.success = true;
$scope.result = result.data.result;
$scope.payload = result.data.payload; //I do assign part of the result to $scope.payload... but that still doesn't explain videoModel.youtube getting overwritten.
})
.catch(function(response) {
$scope.error = true;
toastr.error(response.data.message, response.data.status);
});
return $scope.success;
};
In JavaScript, object references are values.
Because of this, objects will behave like they are passed by reference:
If a function changes an object property, it changes the original value.
Changes to object properties are visible (reflected) outside the function.
http://www.w3schools.com/js/js_function_parameters.asp
I guess by now you might have understood what is pass by value and reference.
Here is a quick solution for you without changing much,you can use angular.copy() which will create new copy of your model into the payload variable and further you can do your rest of processing.
$scope.newVideo = function(videoModel) {
var payload = angular.copy(videoModel)
//do some processing here...
}
Hope this helps you !

HTML placeholder override the ng-model value if it is 0

Is there a way to tell ng-model if your value is 0 then show the placeholder value not the ng-model value.
The reason for this is I have an issue where the UX requires the ng-model to be one value while the business logic requires it to be another.
Something like this.
Controller
$scope.qty = 0;
//...some calculation that requires $scope.qty to be a number
HTML
<input ng-model="qty" placeholder="N/A">
<!-- This will show 0 not N/A as qty has a value-->
I do understand you can edit the functionality of ng-model with the following.
ng-model-options={getterSetter: true}
This does solve this issue in a single ng-model like above but what about when the qty is in a ng-repeat?
In the real world we are making a page that auto calculates the amount of items a user should order based on there inventory. If the inventory is 0 the placeholder is used if it is changed by the user then the value they entered in is used.
As there is more then one product they can order we have this in an ng-repeat.
Controller
$scope.products = [{
//...product data
inventory : 0
}]
HTML
<tr ng-repeat="product in products track by $index">
<td>
<input ng-model="product.inventory" placeholder="N/A">
</td>
</tr>
You can try the setter getter method but as the only value you get is the value entered into the input box you lose the context of which item the user is referring to unless you create a setter getter function for each product.
Try this. You can define a getter function for ng-model directive if you use ng-model-options by setting getterSetter as true. ng-model-options is used to modify the behaviour of ng-model.
<input ng-model="getterSetter" placeholder="N/A" ng-model-options={getterSetter: true}>
// Controller function
$scope.getterSetter = function(value) {
if(value == 0) {
return "N/A";
}
else {
return value;
}
}
You can ngModelOptions, Object to tell Angular exactly when to update the Model.
<input ng-model="updateModel" placeholder="some text" ng-model-options={updateModel: true}>
// Controller method
$scope.updateModel= function(value) {
if(value <= 0) {
return "some text";
}
else {
return value;
}
}

Does AngularJS store a value in the $error.maxlength object?

I've got a UI page setup through Angular, and I'm trying to take advantage of the built in ng-maxlength validator on an input element. Long story short, I know about $scope.form.$error and how that object has a maxlength property in the case that the validation fails. But I want to display an error message specific to the character length that was violated, and I don't see anywhere that the length that I specified was stored on this object. Does anyone know if it's possible to access this, so I don't have to write out a separate error message for each input that has the max length violated?
EDIT: To answer your question, yes angular does store a boolean value in the $error object that is accessible to your via the key(s) that are set in the object. In the case of the code I provided below and in th jsFiddle, we are setting the key for angular, and the value of either true or false.
Be mindful when setting the value as it is reversed. ex. $setValidity( true ), flips the $error to false.
Ok, here is what I think you were looking for...
In Angularjs v1.2.13 you will not have access to ng-message or the $validator pipeline,
which is why are are using $formatters and $parsers.
In this case, I am using named inputs, but perhaps in your case you need dynamic input names?
Plus, if you are using inputs but no form, then getting the error message to display would have to be done with a separate custom directive.
If so, then please look here for dynamically named input fields for some help.
dynamic input name in Angularjs link
Let me know if this works; I'll make changes as needed to HOOK YOU UP!
In case you don't know, you can write over Angular's maxlength for each individual input.
If you changed 'maxlength' in the updateValidity() function in the directive below, to something like 'butter', then $scope.form.inputname.$error would be something like
$scope.formname.inputname.$error { butter: true }
if you also used ng-maxlength="true", then it would be
$scope.formname.inputname.$error { butter: true, maxlength: true }
Another example if you used ng-maxlength, and capitalized the 'maxlength' in the directive to 'Maxlength'
Then you would get
$scope.formname.inputname.$error { maxlength: true(angular maxlength), Maxlength: true(your maxlength)
And of course if you name it the same, then yours writes over angulars
$scope.formname.inputname.$error { maxlength: true };
The point is YOU can add your own names to the angular $error object; you can write over Angular's; and you can just use what Angular gives you when you use Angular's directives: like ng-required="true", or ng-maxlength="true"
Link to YOUR angularjs version on jsFiddle
jsFiddle LInk
<div ng-app="myApp">
<form name="myForm">
<div ng-controller="MyCtrl">
<br>
<label>Input #1</label>
<br>
<input ng-model="field.myName" name='myName' my-custom-length="8" />
<span ng-show="myForm.myName.$error.maxlength">
Max length exceeded by {{ myForm.myName.maxlength }}
</span>
<br>
<br>
<label>Input #2</label>
<br>
<input ng-model="field.myEmail" name='myEmail' my-custom-length="3" />
<span ng-show="myForm.myEmail.$error.maxlength">
Max length exceeded by {{ myForm.myEmail.maxlength }}
</span>
</div>
</form>
</div>
var app = angular.module('myApp', []);
app.controller('MyCtrl', function ($scope) {
$scope.field = {};
});
app.directive("myCustomLength", function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
if (!ctrl) { return } // ignore if no ngModel controller
ctrl.$formatters.push(validateInput);
ctrl.$parsers.unshift(validateInput);
function validateInput(value) {
if (!value) {
updateValidity(false);
return;
}
inputLength(value);
var state = value.length > attrs.myCustomLength;
updateValidity(state);
}
function inputLength(value) {
ctrl.maxlength = null;
var length = value.length > attrs.myCustomLength;
if (length) {
ctrl.maxlength = (value.length - attrs.myCustomLength).toString();
}
}
function updateValidity(state) {
ctrl.$setValidity('maxlength', !state);
}
} // end link
} // end return
});
CSS Here if you need it.
input.ng-invalid {
border: 3px solid red !important;
}

Using Handlebars custom helper method to hide HTML

I have the following custom Handlebars helper:
Handlebars.registerHelper('IsNewUser', function (userId) {
return (userId < 1);
});
And the following HTML in my view:
{{#IsNewUser Id}}
<div>
<input name="IsActive" id="user-active" type="checkbox" checked />
Active
</div>
{{/IsNewUser}}
I can clearly see the function being hit, the userId parameter passed correctly, and the return is true of type bool but instead of showing the block it shows the text 'true'.
How can I get the HTML block to hide with Handlebars without error'ing out?
I was able to fix it after gaining some insight from this StackOverflow question. Changed my helper method to the following:
Handlebars.registerHelper('IsNewUser', function (userId, options) {
if (userId < 1)
return options.fn(this);
else
return options.inverse(this);
});

Categories

Resources