AngularJS issue showing dynamic phrases according to a scope value. - angularjs

I have an issue. When people click on a link, this variable add 5 to the original value:
$scope.sumareco = function(cantidad) { $scope.contadoreco += cantidad};
If i print the value {{sumareco}} you can see how the value changes.
The problem starts when I create a conditional on the controller.
if ($scope.contadoreco < 30 && $scope.contadoreco > 10) {
$scope.resultadoeco = "Opción uno";
} else {
$scope.resultadoeco = "";
}
If I print {{resultadoeco}} I expect that the phrase changes dinamically depending of the numeric value of $contadoreco. But it remains static and only shows the phrase assigned to the original value. ¿What can I do?
Thanks

I'm guessing your if statement is just sitting in your controller and not inside any method or statement that would run during a digest cycle.
The easiest way to update resultadoeco is to watch contadoreco. For example
$scope.$watch('contadoreco', function(val) {
$scope.resultadoeco = val > 10 && val < 30 ?
'Opción uno' : '';
});
See https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

Related

Using .Filter When the Filter Criteria are in Pre-existing String (TypeScript/Node.js)

I'm trying to process an array of JSON objects that have various common attributes, filtering each array entry on one or more of those attributes.
Normally, I'd just do it something like this:
let filteredResultsArray = originalArray.filter((obj) => {
return obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10;
});
My problem is that the filter parameters (the part after "return" in the code above) are highly variable (and unpredictable) from run to run, so I can't hard-code them in the filter. I compute them on the fly and store the whole thing in a string in my code. For example, on one run it might be:
myAttributeString = "obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10";
I've tried doing this:
let filteredResultsArray = originalArray.filter((obj) => {
return myAttributeString;
});
That's failing to filter anything. Apparently .filter() is not properly interpreting what I've stored in myAttributeString as filter criteria.
I have a sneaking suspicion that eval(myAttributeString) might be one way to pull this off, but unfortunately I'm working on a team where we've got tslint set to disallow the use of eval(), so that's not an option.
Anybody have an idea how I can get this to work?
When you "compute them on the fly", instead of creating a string, create a callback function that you can then pass to filter. For example, instead of
const myAttributeString = "obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10";
do
const filterCallback = obj => obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10
Then, later, when the appropriate time comes to .filter, simply pass that as the callback:
const filteredResultsArray = originalArray.filter(filterCallback);
If you can't pass functions around, another option would be to build an array of conditions, for example
[
{
prop: "attribute1",
constraint: "<=",
value: 3
},
{
prop: "attribute2",
constraint: ">",
value: 0
},
// ...
]
and then turn the object into the filter function needed.
****************************************UPDATE******************************
As I suspected, eval() did work, but since I can't use it in my delivered code, and thanks to CertainPerformance's suggestion (which put my thinking on the right track) as well as the Node.js documentation site (via a lucky Google search), I was able to find a workaround using the vm module:
import * as vm from "vm";
let filteredResultsArray = originalArray.filter(
vm.runInThisContext("(obj) => {
return " + myAttributeString + ";}"));
Case closed.

angularjs correct row is not being deleted from table

I have been struggling with a couple of issue so I thought I would reach out for help after doing much searching. I'm using AngularJS.
My first problem is that checkbox is not working properly when I use ng-repeat, it will only check the first box in the table code snippet below. I'm using a custom checkbox which I coded with css is the reason for the label.
My second issue is when I click on the check box it should delete any row I want but it deletes the row before it if I start from the bottom of the table up, but if I start from the top of the table down it works as expected except for the box being checked code snippet. I tried many ways to set the index but it still does not work.
$scope.removeRow = function(type) {
var index = -1;
var myArr = eval( $scope.contacts );
for( var i = 0; i < myArr.length; i++ ) {
if( myArr[i].type === type ) {
index = i;
break;
}
{
if( index === -1 ) {
alert( "Something gone wrong" );
}
$scope.contacts.splice(index, 1);
};
First off, don't use eval($scope.contacts). If that variable is a string and contains javascript it will run that javascript. if myArr isn't defined using myArr = $scope.contacts you can use myArr = JSON.parse(JSON.stringify($scope.contacts) or use angular.copy method.
I don't see your html, so I can't tell you why your checkbox doesn't work. But regarding your second problem, if it's deleting a row above when you hit splice, why not change that code to :
$scope.contacts.splice(index + 1, 1)

Using AngularJS to validate dynamically created 'input' element

I have a table that displays several entries, each has an <input>. The user can dynamically add additional inputs by clicking an "add entry" button. I need to iterate over them before saving and validate each one. I simplified my example to check that the value of each input is greater than 100 (ultimately I will use a pattern-match to validate MAC and IP addresses).
I can probably handle it if I could select all <input>s, but I would really like to select a specific <input> using an index I already have in my scope. I read that angular.element is a way, but I need to select something that was dynamically created, and thus not named something easy like id="myInput". Unless I use an id of "input" and append a unique number with Angular's $index in the id attribute?
Here is my Fiddle that shows what I'm doing. Line 44 is an if() that should check if any <input> is greater than 100. The "Save Row" button validates that the input is greater than 100, but if you edit a line, I need the "Save" button to validate any that the user has edited (by clicking Edit next to it).
tl;dr:
How can I use Angular to select an <input> that has been created dynamically?
I have updated your fiddle in a clean way so that you can maintain the validation in a generic method for both add & edit.
function validateBinding(binding) {
// Have your pattern-match validation here to validate MAC and IP addresses
return binding.ip > 100;
}
Updated fiddle:
https://jsfiddle.net/balasuar/by0tg92m/27/
Also, I have fixed the current issue with editing you have to allow multiple editing without save the first row when clicking the next edit on next row.
The validation of 'save everything' is now cleaner in angular way as below.
$scope.changeEdit = function(binding) {
binding.onEdit = true;
//$scope.editNum = newNum;
$scope.showSave = true;
};
$scope.saveEverything = function() {
var error = false;
angular.forEach($scope.macbindings, function(binding) {
if(binding.onEdit) {
if (validateBinding(binding)) {
binding.onEdit = false;
} else {
error = true;
}
}
});
if (error) {
alert("One/some of the value you are editing need to be greater than 100");
} else {
$scope.showSave = false;
}
}
You can check the updated fiddle for the same,
https://jsfiddle.net/balasuar/by0tg92m/27/
Note: As you are using angular, you can validate the model as above and no need to retrieve and loop the input elements for the validation. Also for your case, validating the model is sufficient.
If you need some advanced validation, you should create a custom
directive. Since, playing around with the elements inside the
controller is not recommended in AngularJS.
You can use a custom class for those inputs you want to validate. Then you can select all those inputs with that class and validate them. See this Fiddle https://jsfiddle.net/lealceldeiro/L38f686s/5/
$scope.saveEverything = function() {
var inputs = document.getElementsByClassName('inputCtrl'); //inputCtrl is the class you use to select those input s you want to validate
$scope.totalInputs = inputs.length;
$scope.invalidCount = 0;
for (var i = 0; i < inputs.length; i++){
if(inputs[i].value.length < 100){
$scope.invalidCount++;
}
}
//do your stuff here
}
On line 46 a get all the inputs with class "classCtrl" and then I go through the input s array in order to check their length.
There you can check if any of them is actually invalid (by length or any other restriction)

AngularJS : Why the data is not displayed in view may I use $scope.apply?

I am getting data from service and display on view using ng-repeat .Actually I am getting event when user scroll to bottom mean when user reached to bottom I will do something.When It reached to bottom I am changing the contend of my array .I am getting the correct contend in ng-repeat array (display array) but it is not reflect on view why ? May I use $scope.apply() or $scope.digest()
Here is my code
http://plnkr.co/edit/XgOxJnPXZk4DneJonlKV?p=preview
Here I am changing the contend of my display array which is not reflect on view
if (container[0].offsetHeight + container[0].scrollTop >= container[0].scrollHeight) {
if(scope.var<scope.arrays.length)
scope.display=[];
var nextvar =++counter;
var increment=counter+1
console.log("nextvar:"+nextvar+"increment:"+increment)
scope.display=scope.arrays[nextvar].concat(scope.arrays[increment]);
console.log(scope.display)
}
As #Claies mentioned you should use apply(). Though the digest() would probably have worked as well.apply() calls digest() internally. He also mentioned that your variable that seems to be storing the page number gets reset to 0 each time you scroll. You should store that in a scope variable outside that handler.
I tried to fix with minimum change
http://plnkr.co/edit/izV3Dd7raviCt4j7C8wu?p=preview
.directive("scrollable", function() {
return function(scope, element, attrs) {
var container = angular.element(element);
container.bind("scroll", function(evt) {
console.log('scroll called'+container[0].scrollTop);
var counter = scope.page;
if (container[0].scrollTop <= 0) {
if (scope.var > 0)
scope.display = scope.arrays[--scope.var].concat(scope.arrays[scope.var+1]);
}
if (container[0].offsetHeight + container[0].scrollTop >= container[0].scrollHeight) {
if (scope.var < scope.arrays.length)
scope.display = [];
var nextvar = ++counter;
var increment = counter + 1
console.log("nextvar:" + nextvar + "increment:" + increment)
scope.display = scope.arrays[nextvar].concat(scope.arrays[increment]);
console.log(scope.display)
scope.page = counter;
}
scope.$apply();
});
};
})
generally I would have implemented this differently. For example by having a spinning wheel on the bottom of the list that when displayed you get the rest of data.
It is difficult to give you a full working plunker. Probably you should have multiple JSON files in the plunker, each containing the data for one page so that we can add the data to the bottom of the display list.
After you modify display array you just have to call scope.$apply() so that it runs the $digest cycle and updates the view. Also you need the initialize scope.var either in your controller or the directive and modify it conditionally.
I dont if this is what you want. I have modified the plunker take a look.
http://plnkr.co/edit/J89VDMQGIXvFnK86JUxx?p=preview

Can't sum values in Angularjs if one value is an empty string

I am building a simple Appgyver mobile app using Angularjs and Coffeescript - I'm a beginner with both of these.
I wish to determine the total cost for a list of up to 20 items stored on the database. However, there may be less than 20 items.
I have attempted to do the calculation with ng-bind, which works perfectly as long as all strings contain values. However, if there are less than 20 pairs (values go up to q20 and p20) then the calculation returns NaN.
I would like to determine the total of all existing values for the list. I have looked at numerous examples on stackoverflow, Angularjs.org and other sites and have experimented with a myriad of alternative methods, however I think I lack the basic understanding of how to make this work. Any help would be appreciated.
This is the code I have used, shortened to 3 pairs instead of 20:
<span ng-bind="client['q1'].price * client['p1'].price + client['q2'].price
* client['p2'].price + client['q3'].price * client['p3'].price"></span>
This is the existing controller:
angular
.module('client')
.controller("ShowController", ($scope, Client, supersonic) ->
$scope.client = 0;
$scope.showSpinner = true
$scope.dataId = undefined
_refreshViewData = ->
Client.find($scope.dataId).then (client) ->
$scope.$apply ->
$scope.client = client
$scope.showSpinner = false
supersonic.ui.views.current.whenVisible ->
_refreshViewData() if $scope.dataId
supersonic.ui.views.current.params.onValue (values) ->
$scope.dataId = values.id
_refreshViewData()
$scope.remove = (id) ->
$scope.showSpinner = true
$scope.client.delete().then ->
supersonic.ui.layers.pop()
)
I think you are overloading (in the linguistic sense, not the coding sense) ng-bind. Doing all of that code in your HTML is messy and is not what it was created for. You would be better off doing the math in your controller, and then referencing it in ng-bind. You have only 3 pairs here, but you say you have 20, and could be more, so do it that way:
<span ng-bind="totalPrice"></span>
And in your controller:
var setTotalPrice = function() {
var ret = 0, i, maxClient = 6, client = $scope.client; // or however else you keep track of them
for (i=1;i<=maxClient;i++) {
if (client['q'+i] && client['q'+i].price && !isNaN(client['q'+i].price) &&
client['p'+i] && client['p'+i].price && !isNaN(client['p'+i].price)) {
ret += (client['q'+i].price * client['p'+i].price);
}
}
$scope.totalPrice = ret;
};
$scope.setTotalPrice = setTotalPrice;
setTotalPrice();
Just call setTotalPrice in your controller whenever you want, or on an ng-click.
Please don't abuse ng-bind for calculations! Instead calculate the values in your controller and bind the resulting value.
Problem with your code is -if any of the values is not a number your result becomes NaN. In the controller function you check for the presence of value and then operate. You may want to check whether the value is non-null as well as a number string and then operate on it.

Resources