$scope.$eval(stringFormula) is throwing errors in AngularJS - angularjs

I have a project which is client solution based on angularJS calling an API. Formulas that should run on client solution are retrieved from the API, and parameters of each formula will be replaced by values entered by the client user.
Formulas are a bit complicated, not a simple addition or division....
Example:
"if('rented' == 'owned')
{ return 50 + 25;
} else if( 'rented' == 'rented')
{return 25;}
else
{ return '' ;
}"
In order to execute this String it was suggested to use $scope.$eval(string). I have tried it with some simple params like $scope.$eval('1 + 2') and it worked successfully. But with the above example it is throwing parsing errors like this one:
The complete angularJS function in the controller is the below:
$scope.calulateFormula = function (formula) {
formula = formula.replace(/\[P1]/g, $scope.param1.value);
formula = formula.replace(/\[P2]/g, $scope.param2.value);
return $scope.$eval(formula);
}
In HTML page:
<tr ng-repeat="formula in lstFormulas">
</td>
<td ng-bind="calulateFormula(formula)">
</td>
</tr>
lstFormulas is retrieved from the API.
So how can I fix this issue and make formulas run on my webPage?

If you can replace all variables with values from scope, you can then use plain js:
var f = new Function(formula);
console.log(f());

Related

How can I get other http.get response inside in a ng-repeat

I have this HTML http://pastebin.com/DegTb0iH
has a ng-repeat rulling in rullings within it need to do the vote count of each staff, get these values as follows: http://pastebin.com/EB4KigwS
the problem is there on the lines 17,18 and 19 HTML, totalVotes always returns 0 in JS (or only the last result), if you do the console.log in the success comes the correct result of each type of vote
Can anyone give me a help?
You're calling the function three times using a different value for voto_type (fair enough) but all three of these, on the success callback, will store the result in $scope.totalVotes which means they will always display the same and last result.
You need to store each result into a separate $scope variable.
E.g. JS:
if (angular.isUndefined($scope.totalVotes)) {
$scope.totalVotes = {};
}
if (result.total > 0) {
$scope.totalVotes[voto_type] = result.total;
} else {
$scope.totalVotes[voto_type] = 0;
}
and HTML:
<p ng-init="getTotalVotes(rulling.id, 's')">Positivos: {{totalVotes['s']}}</p>
<p ng-init="getTotalVotes(rulling.id, 'n')">Negativos: {{totalVotes['n']}}</p>
<p ng-init="getTotalVotes(rulling.id, 'a')">Abstenções: {{totalVotes['a']}}</p>

angularjs xeditable number but source is string formatted

I am using xeditable to edit data inline. Now I want to edit a numeric field, but the source data contains the number as string formatted. So I get the error ngModel:numFmt. How can I convert a string formatted number (example: 0.3) into a numeric for xeditable?
I tried with the string-to-number directive, but it seems, that xeditable did not recognize it:
<td>
<span data-editable-number="row.factor"
data-ng-model="row.factor"
data-e-name="factor"
onbeforesave="validateRow(data, row.id)"
onaftersave="save(row)"
data-e-required
string-to-number="string-to-number"
>
{{row.factor}}
</span>
</td>
You can create a filter to convert String to Integer
app.filter('num', function(){
return function(input) {
return parseInt(input, 10);
};
});
and use it like this
editable-number="row.factor | num "
Working Plunker
The previous didn't work updating the model, better way to achieve this is to transform the data beforehand.
e.g
var rows = [{factor:"1"}, {factor: "4"}, {factor: "9"}];
var tranformed_rows = rows.map(function(row) {
row.factor = parseInt(row.factor);
return row;
});
and then use tranformed_rows in your view.

order by of angularjs for sorting based on three different parameters

I am using order by of Angular for sorting but I want to sort data based on three different fields, i.e success, in-progress and failed, without using any constant and variable directly from in-built function. Is there any way?
If you want you can call a function to sort your data as you want.
you can call following custom 'orderBy'
$scope.sort = function(column,reverse) {
$scope.persons = $filter('orderBy')($scope.persons,column,reverse);
};
'$scope.persons' - is your collection of data you need to order
'column' - name of the column you want to sort data
'reverse'- bool value to reverse the order
and you can call this 'sort' function from your View(HTML) as below.
data-ng-click="sort('name',nameReverse)"
you can pass array of fields to orderBy
<div ng-repeat="row in list | orderBy:['param1','param2']">
....
</div>
EDIT
to do it in javascript
$scope.sortedList = $filter('orderBy')(list,['param1', 'param2']);
EDIT
in smart table
function customSortAlgorithm(arrayRef, sortPredicate, reverse) {
//do some stuff
return sortedArray;
}
scope.globalConfig = {
sortAlgorithm: customSortAlgorithm
};

Something like a manual refresh is needed angularjs, and a $digest() iterations error

(post edited again, new comments follow this line)
I'm changing the title of this posting since it was misleading - I was trying to fix a symptom.
I was unable to figure out why the code was breaking with a $digest() iterations error. A plunk of my code worked fine. I was totally stuck, so I decided to make my code a little more Angular-like. One anti-pattern I had implemented was to hide my model behind my controller by adding getters/setters to the controller. I tore all that out and instead put the model into the $scope since I had read that was proper Angular.
To my surprise, the $digest() iterations error went away. I do not exactly know why and I do not have the intestinal fortitude to put the old code back and figure it out. I surmise that by involving the controller in the get/put of the data I added a dependency under the hood. I do not understand it.
edit #2 ends here.
(post edited, see EDIT below)
I was working through my first Error: 10 $digest() iterations reached. Aborting! error today.
I solved it this way:
<div ng-init="lineItems = ctrl.getLineItems()">
<tr ng-repeat="r in lineItems">
<td>{{r.text}}</td>
<td>...</td>
<td>{{r.price | currency}}</td>
</tr
</div>
Now a new issue has arisen - the line items I'm producing can be modified by another control on the page. It's a text box for a promo code. The promo code adds a discount to the lineItem array. It would show up if I could ng-repeat over ctrl.getLineItems().
Since the ng-repeat is looking at a static variable, not the actual model, it doesn't see that the real line items have changed and thus the promotional discount doesn't get displayed until I refresh the browser.
Here's the HTML for the promo code:
<input type="text" name="promo" ng-model="ctrl.promoCode"/>
<button ng-click="ctrl.applyPromoCode()">apply promo code</button>
The input tag is writing the value to the model. The bg-click in the button is invoking a function that will apply the code. This could change the data behind the lineItems.
I have been advised to use $scope.apply(...). However, since this is applied as a matter of course by ng-click is isn't going to do anything. Indeed, if I add it to ctrl.applyPromoCode(), I get an error since an .apply() is already in progress.
I'm at a loss.
EDIT
The issue above is probably the result of me fixing of symptom, not a problem. Here is the original HTML that was dying with the 10 $digest() iterations error.
<table>
<tr ng-repeat="r in ctrl.getLineItems()">
<td>{{r.text}}</td>
<td>...</td>
<td>{{r.price | currency}}</td>
</tr>
</table>
The ctrl.getLineItems() function doesn't do much but invoke a model. I decided to keep the model out of the HTML as much as I could.
this.getLineItems = function() {
var total = 0;
this.lineItems = [];
this.lineItems.push({text:"Your quilt will be "+sizes[this.size].block_size+" squares", price:sizes[this.size].price});
total = sizes[this.size].price;
this.lineItems.push({text: threads[this.thread].narrative, price:threads[this.thread].price});
total = total + threads[this.thread].price;
if (this.sashing) {
this.lineItems.push({text:"Add sashing", price: this.getSashingPrice()});
total = total + sizes[this.size].sashing;
}
else {
this.lineItems.push({text:"No sashing", price:0});
}
if(isNaN(this.promo)) {
this.lineItems.push({text:"No promo code", price:0});
}
else {
this.lineItems.push({text:"Promo code", price: promos[this.promo].price});
total = total + promos[this.promo].price;
}
this.lineItems.push({text:"Shipping", price:this.shipping});
total = total + this.shipping;
this.lineItems.push({text:"Order Total", price:total});
return this.lineItems;
};
And the model code assembled an array of objects based upon the items selected. I'll abbreviate the class as it croaks as long as the array has a row.
function OrderModel() {
this.lineItems = []; // Result of the lineItems call
...
this.getLineItems = function() {
var total = 0;
this.lineItems = [];
...
this.lineItems.push({text:"Order Total", price:total});
return this.lineItems;
};
}
The problem is that with each $digest cycle, a new array is returned (even if it contains objects with equal values, new objects are created).
To circumvent this, you could associate ngRepeat with a lineItems property and call getLineItems() only when something might have changed.
A possible implementation is the following:
<!-- The VIEW -->
<table>
<tr ng-repeat="r in ctrl.lineItems">...</tr>
</table>
/* The CONTROLLER */
.controller('myCtrl', function (OrderModel) {
this.orderModel = OrderModel;
this.lineItems = this.orderModel.lineItems;
this.reloadItems = this.orderModel.getLineItems;
// Initialization
this.reloadItems();
});
/* The SERVICE */
app.service('OrderModel', function () {
this.lineItems = [];
this.getLineItems = function () {
var total = 0;
this.lineItems.splice(0, this.lineItems.length);
...
for (var i = 0; i < 10; i++) {
total++;
this.lineItems.push({text: 'Order Total', price: total});
}
};
});
See, also, this short demo.

Angularjs ng-repeat sort table with special character ":" in value

I'm using angularjs ng-repeat to sort my records by clicking on table headers, I've found an example who works perfectly, the code are like this:
<th ng-repeat="header in headers">
<a ng-click="toggleSort($index)">{{ headers[$index] }}</a>
</th>
Here is a fiddle of this example.
But, my problem is there are index with ":" in my object, and it cause an error like "TypeError: Cannot read property 'col:A' of undefined"
Here is a demo with ":"
So, how can I make it work with ":" ?
This is a bug which happens when a special character, such as '%', is used in the OrderBy filter to sort the object by the key.
You can find more details about this issue here:
https://github.com/angular/angular.js/issues/6143
It seems to have been corrected in Angular 1.3.0-beta3.
A workaround is to write your own compare function and to use the sort() method of Array.
I've updated your fiddle to give you an idea of an compare function and how to use it:
function compare(a,b,property) {
if (a[property] < b[property])
return -1;
if (a[property] > b[property])
return 1;
return 0;
}
You should change the way you call the parameters in the array:
$scope.headers = ['sortColumn', 'colB'];
$scope.records = [{sortColumn: 'a1', colB: 'd1'}, {sortColumn: 'c2', colB: 'b2'}, {sortColumn: 'b3', colB: 'c3'}, {sortColumn: 'd4', colB: 'a4'}];
$scope.sortColumn = 'colA';
Fiddle

Resources