Angular : ng model array values got undefined in function on ng-click - angularjs

I am trying to allow users to add quantity into text box. I have products array and each product contains its property. My service contains array list.
products = [{
id: 1,
name: 'X Product',
face: 'img/x_p.png',
available_size: 6,
color_available: 0,
sizes: ['10"', '20"', '30"'],
properties: [{size: '10"', mrp: '10$'},
{size: '20"', mrp: '15$'},
{size: '30"', mrp: '20$'}]
},
{
id: 2,
name: 'Y Product',
face: 'img/y_p.png',
available_size: 6,
color_available: 0,
sizes: ['10"', '20"', '30"'],
properties: [{size: '10"', mrp: '10$'},
{size: '20"', mrp: '15$'},
{size: '30"', mrp: '20$'}]
}]
Iterating through the products array. When user clicks on product it will display a product show page. where I am showing all the information for the product. For properties I am showing a table where user can add their quantity just after each size.
<div style="margin-bottom: 20px;">
<h4>
Product Detail
</h4>
<table>
<tbody>
<tr class="tableHeader">
<td>Size</td>
<td>MRP</td>
<td>Quantity</td>
</tr>
<tr ng-repeat="(key, property) in product.properties">
<td>{{ property['size'] }}</td>
<td>{{ property['mrp'] }}</td>
<td>
<input type="number" placeholder="QTY" ng-model="qty">
</td>
<td>
<a href="javascript:;" class="button" ng-click="addToCart(property['size'], qty)">
Add
</a>
</td>
</tr>
</tbody>
</table>
</div>
Question: So according to this table each row contains a button with name add. So after adding quantity user clicks on add button will them allow to add item in cart. here I am getting size in addToCart function but got undefined qty as it should be an array with its respective size. Though I am getting size. But I don't know how to do that.
$scope.addToCart = function(size, qty) {
alert($scope.qty)
}
I need a help.

var app = angular.module("MyApp", []);
app.controller("MyCtrl", function() {
this.addToCart = function(property, qty) {
property["qty"] = qty;
}
this.product = {
id: 1,
name: 'X Product',
face: 'img/x_p.png',
available_size: 6,
color_available: 0,
sizes: ['10"', '20"', '30"'],
properties: [{
size: '10"',
mrp: '10$'
}, {
size: '20"',
mrp: '15$'
}, {
size: '30"',
mrp: '20$'
}]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div style="margin-bottom: 20px;" ng-app="MyApp">
<div ng-controller="MyCtrl as ctrl">
<h4>
Product Detail
</h4>
<table>
<tbody>
<tr class="tableHeader">
<td>Size</td>
<td>MRP</td>
<td>Quantity</td>
</tr>
<tr ng-repeat="(key, property) in ctrl.product.properties">
<td>{{ property['size'] }}</td>
<td>{{ property['mrp'] }}</td>
<td>
<input type="number" placeholder="QTY" ng-model="qty">
</td>
<td>
<a href="javascript:;" class="button" ng-click="ctrl.addToCart(property, qty)">
Add
</a>
</td>
</tr>
</tbody>
</table>
<hr/>
{{ctrl.product.properties}}
</div>
</div>

I have noticed many times that the textbox won't bind to a normal scope variable like
$scope.qty
and binding it in view with ng-model="qty"
Please try using something like
$scope.qty = {
text:' '
}
and bind it as ng-model="qty.text" in the view
which will make sure that your textbox is actually bound.

Simple thing to fix this issue: init qty by $scope.qty = 0;

Related

What triggers a function call in other column when ngmodel changes in first column?

Consider the snippet:
JS
var mod = angular.module('module', []);
mod.controller('controller', function($scope) {
$scope.items = [{
id: 1,
label: 'aLabel',
subItem: {
name: 'aSubItem'
}
}, {
id: 2,
label: 'bLabel',
subItem: {
name: 'bSubItem'
}
}]
$scope.getValue = function(ngmodel) {
// some code goes here...
}
});
HTML
<body ng-controller='controller'>
<div>
<table>
<tr ng-repeat='count in counter'> // 5 times
<td>
<select ng-options="item.id as item.label for item in items"
ng-model="selected[$index]">
</select>
</td>
<td>
{{getValue(1)}}
</td>
<td>
</td>
</tr>
</table>
</div>
</body>
As soon as I select some value from the dropdown (select tag) in the first column, I notice that the function in the second column is triggered? What is the reason for this? What exactly is happening behind the scenes?
The reason is you are doing it inside ng-repeat
<tr ng-repeat = 'count in counter'>
You need to pass the object on the controller, in order to do some action on the same.
{{getValue(obj)}}

Ng-repeat doesn't works with localStorage

Something weird is happening with my localStorage. Data is saved in my localStorage but is never displayed in ng-repeat. However, when the data is less than 2 items, in that case, it is displayed on the screen. When it is greater than 2 it is not shown. This is my controller code
$scope.saved = localStorage.getItem('todos');
if($scope.saved != null ) {
$scope.invoice = { items: JSON.parse($scope.saved) };
} else {
$scope.invoice = { `enter code here`
items : [{
qty: 10,
description: 'item',
cost: 9.95}]
};
}
And this is my addition code:
$scope.addItem = function(x) {
$scope.invoice.push({
qty: 1,
description: x.cakename,
cost: x.id
});
localStorage.clear();
localStorage.setItem('todos', JSON.stringify($scope.invoice));
};
This is table code:
<div>
<table class="table" style="margin-top:50px">
<tr>
<th>Name</th>
<th>Qty</th>
<th>Cost</th>
<th>Total</th>
<th></th>
</tr>
<tr ng-repeat="item in invoice">
<th>#{{ item.description }}</th>
<th>#{{ item.qty }}</th>
<th>#{{ item.cost }}</th>
<th>#{{ item.qty * item.cost }}</th>
<td>[<a href ng:click="removeItem($index)">X</a>]</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Total:</td>
<td> #{{ total() | currency }} </td>
</tr>
</table>
<a href="#/checkout" class="btn btn-danger">
Checkout <span class="glyphicon glyphicon-shopping-cart" aria-hidden="true"></span>
</a>
</div>
When there are only 2 items and I refresh, the screen shows,
Name Qty Cost Total
Black Forest 1 1 1 [X]
Berry 1 2 2 [X]
Total: $3.00
Checkout
otherwise, it shows
Name Qty Cost Total
Total: $7.00
As the total is 7.. The data is still there but not getting displayed.
You stringify the entire invoice object, but when you read it back from local storage, you parse it into an object with a different structure.
Try $scope.invoice = JSON.parse($scope.saved) instead.
I solved my question. It was because of the duplicate keys in the array. Duplicate keys are banned because AngularJS uses keys to associate DOM nodes with items. So adding this did the trick.
ng-repeat="item in invoice track by $index"
So stupid of me!

How to apply css class to selected row in ng-repeat

I have a ng-repeate table. I need to toggleClass for each selected row. Here is my code:
<tr ng-repeat="expenses in Expensereports" ng-mouseenter='showpencil=true' ng-mouseleave='showpencil=false' ng-class="{'selectedrow':selectDelete}">
<td class="text-center">
<input type="checkbox" ng-model='expenses.isDelete' class='deletebox' ng-change='selectedDeleteRow(expenses.isDelete)'/>
</td>
<td class="text-center">{{expenses.date | parseDateFormat | date}}</td>
<td class="text-center">{{expenses.type}}</td>
</tr>
My JS Code:
$scope.selectedDeleteRow = function(selected){
if(selected==true){
$scope.selectDelete=true;
}else{
$scope.selectDelete=false;
}
}
What I want is that if user check the checkbox, I need to hightlight the whole row. but leave other unchecked row alone, simply says is let user know this row is selected, if they uncheck that row, the class will be gone.
ng-class="{'selectedrow': expenses.isDelete}" should work.
var app = angular.module('myApp', []);
app.controller('test', function($scope) {
$scope.Expensereports = [
{'test': 'Row 1'},
{'test': 'Row 2'},
{'test': 'Row 3'}
];
});
tr.selectedrow {
background-color: red;
}
<div ng-app="myApp">
<table ng-controller="test">
<tr ng-repeat="expenses in Expensereports" ng-class="{'selectedrow': expenses.isDelete}">
<td>
<input type="checkbox" ng-model="expenses.isDelete" ng-change="selectedDeleteRow(expenses.isDelete)"/>
</td>
<td class="text-center">{{expenses.test}}</td>
</tr>
</table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
In Angular you can use [ngClass]:
<tr *ngFor="let expenses in Expensereports" [ngClass]="{'selectedrow': expenses.isDelete}">
...
</tr>

How to implement angular nested ng-repeat with groupBy filter and ng-model binding on input

I'm trying to create a nested ng-repeat loop. It uses the groupBy and toArray filters from angular-filter.
The issue is that I have an editable input field in the nested array. Without implementing "track by" on the ng-repeats every time the value changes when typed it looses focus as described in this issue.
If I try to put a track by on the repeats it breaks and shows many fields with blank values. Is there any way to correctly implement track by in this situation so that the repeats display correctly and I can type into the input field without it losing focus?
Here is example, if you run it you will see as soon as you edit input you loose focus:
var app = angular.module('App', ['angular.filter']);
app.controller('MainCtrl', function() {
this.Parts = [{
Id: 1,
ShortDescription: "Premium Shocks",
SupplierSku: "ZXU3322",
SellPrice: 110,
SellPriceInclGst: 130,
ProfitExclGst: 10,
SupplierName: 'Super Sports',
Quantity: 3
}, {
Id: 2,
ShortDescription: "Spanner",
SupplierSku: "4444",
SellPrice: 44,
SellPriceInclGst: 130,
ProfitExclGst: 10,
SupplierName: 'Bobs Parts',
Quantity: 1
}, {
Id: 3,
ShortDescription: "Spark Plugs",
SupplierSku: "xxxxx",
SellPrice: 10,
SellPriceInclGst: 130,
ProfitExclGst: 10,
SupplierName: 'Bobs Parts',
Quantity: 5
}]
});
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.min.js"></script>
</head>
<body ng-app="App">
<table class="table" ng-controller='MainCtrl as main'>
<thead>
<tr>
<th>Short Descriptin</th>
<th>SKU</th>
<th>Qty</th>
<th>Unit Price</th>
<th>Total</th>
<th>Total + GST</th>
</tr>
</thead>
<tbody ng-repeat="supplier in main.Parts | groupBy:'SupplierName' | toArray:true | orderBy:'$key'">
<tr class="title-display-row">
<td colspan="6">{{ supplier.$key }}</td>
</tr>
<tr ng-repeat="item in supplier track by $index">
<td>{{ item.ShortDescription }} {{ key }}</td>
<td>{{ item.SupplierSku }}</td>
<td class="field-cell">
<div class="field-wrap">
<input type="text" name="quantity[$index]" ng-model="item.Quantity" />
</div>
</td>
<td>{{ item.SellPrice | currency }}</td>
<td>Total</td>
<td>Total-gst</td>
</tr>
</tbody>
</table>
</body>
</html>
Any suggestions greatly appreciated.
In the unlikely event someone has the same problem as this... the solution was to remove the toArray|true filter.
Then instead of accessing the supplier name with {{ supplier.$key }} I'm using {{ Supplier[0].SupplierName }}. Because each supplier in the loop is an array of items with the same supplier name so I can just take the first and use it.

angularjs ng-repeat inside ng-repeat,the inside array can't update

I come from China,my English very poor,so I do a demo.how can I update array inside ng-repeat?
The HTML:
<body ng-app="main" ng-controller="DemoCtrl">
<table ng-table class="table">
<tr ng-repeat="user in users">
<td data-title="'Name'">{{user.name}}</td>
<td data-title="'Age'">{{user.age}}</td>
<td>
{{user.spms|json}}
<div ng-repeat="u in user.spms">
<span ng-bind="u"></span>
<input type="text" ng-model="u" ng-change='updateArray($parent.$index, $index, u)'>
</div>
</td>
</tr>
</table>
</body>
The JS:
var app = angular.module('main', []).
controller('DemoCtrl', function($scope) {
$scope.users = [{
name: "Moroni",
age: 50,
spms: [
6135.7678,
504.4589,
2879.164,
669.7447
]
},
{
name: "seven",
age: 30,
spms: [
34135.7678,
5034.42589,
22879.1264,
63469.72447
]
}
];
$scope.updateArray = function(parent, index, u) {
$scope.users[parent].spms[index] = u * 1; // multiply by one to keep the value a Number
}
})
There are issues here are every update is changing the scope, so you can only change one time them click then change - so I would recommend add a update values button and implementing more or less the same logic to update the array values.
DEMO

Resources