I have a button that needs to add some values to an object attribute. The problem I have found is that I'm creating new objects on every click.
And what I need is to add new values to a specific attribute of a specific object.
I'm getting this
Object { id=0, title="Rolling Stones", sessionsBegin="1443564000000"}
Object { id=0, title="Rolling Stones", sessionsBegin="1443564000001"}
Object { id=0, title="Rolling Stones", sessionsBegin="1443564000002"}
What I need to generate is this
Object { id=0, title="Rolling Stones",sessionsBegin="1443564000000, 1443564000001,1443564000002"}
This on the controller part:
$scope.addItem = function(indexItem, title) {
$scope.cart = {
"id" : indexItem,
"title" : title
}
if ($scope.cart.id==indexItem){
$scope.cart.sessionsBegin=$scope.sessions[indexItem].date;
console.log($scope.cart);
}
}
This on the partial view side:
<div class="row" >
<div class="large-6 columns" >
<div class="panel">
<div ng-repeat="session in sessions">
{{event.id}} Date: {{session.date }}  
Availability: {{session.availability}}  
<a ng-click="addItem($index, session.title);" ng-show="addMore">ADD </a>
</div>
</div>
</div>
</div>
You need to concat a string to your current value, like that:
// Add a comma if needed:
$scope.cart.sessionsBegin += ($scope.cart.sessionsBegin) ? ', ' : '';
// and then add the value itself:
$scope.cart.sessionsBegin += $scope.sessions[indexItem].date;
Btw. usually you'd want a list of those sessionsBegin values to be an array - it will be much easier to work with. In that case I'd suggest:
if (!$scope.cart.sessionsBegin) {
$scope.cart.sessionsBegin = [];
}
$scope.cart.sessionsBegin.push($scope.sessions[indexItem].date);
Wouldn't changing $scope.cart.sessionsBegin=$scope.sessions[indexItem].date; to $scope.cart.sessionsBegin+=$scope.sessions[indexItem].date; do the trick?
In your code you redefine the cart object every time you press 'add' though. Hence why your console.log shows new objects every time.
$scope.cart = { ... } // this bit of code means you delete the 'old' $scope.cart and redefine it with new values
Does this work for you?
$scope.addItem = function(indexItem, title) {
$scope.cart = $scope.cart || {
"id" : indexItem,
"title" : title
}
if ($scope.cart.id==indexItem){
var sessionAsArray = $scope.cart.sessionsBegin.split(',');
sessionAsArray.push($scope.sessions[indexItem].date);
$scope.cart.sessionsBegin=sessionAsArray.join(',');
console.log($scope.cart);
}
}
Related
I'm having trouble making proper table with nested ng-repeat.
What I wanted is this https://jsbin.com/razamagabo/1/edit?output
but I'm stuck at here https://plnkr.co/edit/d5voXIpzYL81sSl9BSY2?p=preview
I don't mind my markup is not table but I'm still stuck with div
<div class="row">
<div class="col-xs-6" ng-repeat="obj in data">
{{obj.date}}
<div ng-repeat="user in obj.users">
<br>
{{user.name}}
<br>
{{user.mark}}
</div>
</div>
</div>
In order for you to be able to display your data in the desired way, it will probably be easiest if you restructure your data in the JS before trying to render it.
It will be very complicated to try and match on the user names when they are in separate objects in the data array.
I would suggest processing your scope.data in the controller. (I'm assuming that you don't have much control on how you are receiving the data).
For example after you get your data...
$scope.data = [
{
date:'1-1-2016',
users:[
{
'name':'james',
'mark':18
},
{
'name':'alice',
'mark':20
}
]
},
{
date:'2-1-2016',
users:[
{
'name':'james',
'mark':60
},
{
'name':'alice',
'mark':55
}
]
}
]
var userData = {};
var possibleDates = [];
for (dataObj of Object.entries($scope.data)) {
for (userObj of dataObj) {
if ( !userData[userObj.name] ) {
userData[userObj.name] = {};
}
userData[userObj.name][dataObj.date] = userObj.mark;
if (dates.indexOf(dataObj.date) < 0) {
dates.push(dataObj.date);
}
}
}
$scope.users = userData;
$scope.dates = possibleDates;
this will give you an object like this on your scope
$scope.users = {
'james': {
'1-1-2016': 18,
'2-1-2016': 60
},
'alice': {
'1-1-2016': 20,
'2-1-2016': 55
}
};
$scope.dates = ['1-1-2016', '2-1-2016'];
This to me seems easier to structure for your template. Though this assumes each user has an entry for each date.
<div>
<div id='header-row'>
<div id='empty-corner></div>
<div class='date-header' ng-repeat='date in $scope.dates></div>
</div>
<div class='table-row' ng-repeat='{key, value} in $scope.users'>
<div class='user-name'>{{ key }}</div>
<div class='user-data' ng-repeat='date in $scope.dates>
{{ value[date] }}
</div>
</div>
</div>
As long as you apply inline-block styles to the rows/elements this should give you what you are looking for.
Though you can also think of ways to simplify your data even further. You could instead of having each user have an object where the dates are keys, you could just push the values into an array.
With your current data structure it is not possible to display it like you want. You are trying to loop over date-users objects in data array but then you want to display user from inside users array in separate rows. With ng-repeat you can loop through rows tr but not through columns. First you would need to map your data array to group elements that are supposed to be visible in 1 row into 1 object in array. Currently you have them in 2 separate objects:
James mark: 18 and James mark: 60.
I'm using angularJS to build a SPA. I am trying to delete an object from an array in my controller. I am using ng-repeat and can't seem to get my head around this. Here is the related html:
<div class="cat-button" ng-repeat="category in cats" category="category">
<button class=" close-button" ng-click="removeCat()">
<span class="glyphicon glyphicon-remove-sign" aria-hidden=true> </span> </button>{{category.name}}
</div>
This created a div with a button for every object that gets saved to my $scope.cats array. It works fine but I cant figure out how do I use the button in each div to delete that specific object.
When I click on the button , the function on my controller gets called, but this is where I get lost, how do I delete the specific object created dynamically by the user.
This is the related code on my controller:
//Function to delete category
$scope.removeCat = function () {
//I know I have to use splice on my array but how do I Identify the object that needs to be deleted from my array?
};
You can either pass on $index like so:
<button class=" close-button" ng-click="removeCat($index)">
and in your function:
$scope.removeCat = function (index) {
$scope.cats.splice(index,1);
}
or pass the whole item and use indexOf (the saver way)
<button class=" close-button" ng-click="removeCat(category)">
$scope.removeCat = function (item) {
$scope.cats.splice(myArray.indexOf(item), 1);
}
You can pass the index of the item you want to delete in the ng-click function:
<div class="cat-button" ng-repeat="category in cats" category="category">
<button class=" close-button" ng-click="removeCat($index)">
<span class="glyphicon glyphicon-remove-sign" aria-hidden=true> </span> </button>{{category.name}}
</div>
Then you can use this in your Angular controller like this:
$scope.removeCat = function (index) {
$scope.cats.splice(index, 1);
};
Update
Incase you don't want to pass in the index, instead you can also pass in the entire object and locate the index in your controller. The code below is setup to work on all browsers. (Just haven't tested it ;) )
$scope.removeCat = function (cat) {
// Using underscore
var index = _.indexOf($scope.cats, cat);
// Or using a for loop
for(var i = 0; i < $scope.cats.length; i++) {
//Assuming your cat object has an id property
if($scope.cats.id === cat.id) {
index = i;
break;
}
}
};
Or any other way to locate the index of an object in an array.
ng-click="removeCat(category)"
$scope.removeCat = function (categoryToDelete) {
var index = $scope.cats.indexOf(categoryToDelete);
$scope.cats.splice(index, 1);
};
I am getting a server response and binding these data to view using ng-repeat. Now I want to sort these data by priceList and name. I am able to sort name using orderBy, but not with priceList. I want to sort the products based on priceList. Sorting with name will change the order of list while sorting by priceList will effect only the order of products of each category. It will effect the order of displayed category. Please help me resolve this.
My code:
<div ng-controller="Ctrl">
<pre>Sorting predicate = {{predicate}};</pre>
<hr/>
<table class="friend">
<tr>
<th>Name
</th>
<th><a href="" ng-click="predicate = 'priceList'>price</a></th>
</tr>
</table>
<div ng-repeat="data in _JSON[0].categories | orderBy:predicate">
<div ng-repeat="vals in data.itemTypeResults |orderBy:'partTerminologyName'" id="{{vals.partTerminologyName}}">
`<h4 style="background-color: gray">{{vals.partTerminologyName}} : Position :{{vals.position}}</h4>`<br>
<div ng-repeat="val in vals.products">
<b> Quantity:{{val[0].perCarQty}}</b><br>
<b> part:{{val[0].partNo}}</b><br>
<b>sku:{{val[0].sku}}</b><br>
<b> qtyInStock:{{val[0].qtyInStock}}</b><br>
<b> priceList:{{val[0].priceList}}</b><br>
<b>priceSave:{{val[0].priceSave}}</b><br>
<b> qtyDC:{{val[0].qtyDC}}</b><br>
<b> qtyNetwork:{{val[0].qtyNetwork}}</b><br>
<b> priceCore:{{val[0].priceCore}}</b><br>
</div>
</div>
</div>
JS:
$scope._JSON = [
{"categories":
[
{"id":14061,"name":"Drive Belts",
"itemTypeResults":[
{"partTerminologyName":"Serp. Belt",
"position":"Main Drive",
"products":{
"5060635":[
{"perCarQty":2,"partNo":"5060635",
"sku":"20060904","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":19.15,"priceSave":3.29,
"priceCore":10.0}
],
"635K6":[
{"perCarQty":9,"partNo":"635K6",
"sku":"10062449","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0}
]
}
}
]
},
{"id":2610,"name":"Drive Belt Tensioners, Idlers, Pulleys & Components",
"itemTypeResults":[
{"partTerminologyName":"Drive Belt Tensioner Assembly",
"position":"N/A",
"products":{
"950489A":[
{"perCarQty":4,"partNo":"950489A",
"sku":"10150833","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0
}
]
}},
{"partTerminologyName":"Drive Belt Idler Pulley","position":"N/A",
"products":{
"89161":[
{"perCarQty":1,"partNo":"89161",
"sku":"99995959","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":17.15,"priceSave":3.21,"priceCore":10.0}
],
"951373A":[
{"perCarQty":2,"partNo":"951373A","pla":"LTN",
"plaName":"Litens",
"sku":"10150926","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0}
]
}
}
]
}
]
}
];
$scope.predicate = '';
Fiddle: Fiddle
You might need to define a very good sorter function, or sort your products before they are interpreted by ng-repeat. I've created sorter function using underscore.js (or lodash).
You can checkout the demo (or the updated demo). Products are first sorted by category and then sorted by price in every category.
<!-- in html -->
<button ng-click="sortFn=sortByPrice">Sort By Price</button>
<button ng-click="sortFn=doNotSort">Do not Sort</button>
...
<div ng-repeat="val in sortFn(vals.products)">
...
// in js
$scope.sortByPrice = function(products) {
return _.sortBy(products, function(product) {
return product.length > 0 ? product[0].priceList : 0;
});
};
$scope.doNotSort = function(products) {
return products;
};
$scope.sortFn = $scope.doNotSort; // do not sort by default
BTW: You are directly calling val[0], which is very dangerous, if the product does not contain any elements, your code will break. My code won't ;-)
Update 1
The author asks me for a more pure Angular way solution.
Here is my answer: I think my solution is exactly in Angular way. Usually you can implement a filter (similar to orderBy) which wraps my sortByPrice. Why I don't do that, because you have ng-click to switch your order filter. I'd rather put control logic into a controller, not as pieces into view. This will be harder to maintain, when your project keeps growing.
Update 2
Okay, to make the +50 worthy, here is the filter version you want, (typed with my brain compiler) Please check in fiddle
You need to organize the products in other estructure. For example:
$.each($scope._JSON[0].categories , function( i , e) {
$.each(e.itemTypeResults, function(sub_i,sub_e) {
$.each(sub_e.products, function(itemTypeResults_i,product) {
console.log(product);
var aProduct = new Object();
aProduct.priceList = product[0].priceList;
aProduct.name = e.name;
$scope.products.push(aProduct);
});
} )
});
The code is not very friendly but what i do is putt all the products in one array so they can be ordered by the price. You have the products inside categories so that's why angular is ordering by the price in each category.
Fiddle:
http://jsfiddle.net/7rL8fof6/1/
Hope it helps.
Your fiddle updated: http://jsfiddle.net/k5fkocby/2/
Basically:
1. Digested the complex json object into a flat list of objects:
var productsToShow = [];
for (var i=0; i < json[0].categories.length; i++){
var category = json[0].categories[i];
for (var j=0; j<category.itemTypeResults.length;j++){
var item = category.itemTypeResults[j];
var products = item.products;
for (var productIndex in products) {
var productItems = products[productIndex];
for (var k=0; k<productItems.length;k++){
var productItem = productItems[k];
// Additions:
productItem.categoryName = category.name;
productItem.partTerminologyName = item.partTerminologyName;
productItem.position = item.position;
productsToShow.push(productItem);
}
}
}
}
Show category title only when needed by:
ng-repeat="product in (sortedProducts = (productsToShow | orderBy:predicate))"
and
ng-show="sortedProducts[$index - 1].partTerminologyName != product.partTerminologyName"
you can sort from your database and get final JSON data..
db.categories.aggregate([{$group : {category : {your condition} }, price: {$sort : { price: 1 } },}}])
I'm a rank newbie to Angular, and I'm attempting to port an old jQuery-based app to NG. The JSON I'm starting with (parsed from an XML file) looks like this:
{
"setUps":{
"cartImage":
{
"_cartIm":"addtocart.gif",
"_cartIm_o":"addtocart.gif"
}
,"_supportImsPath":"/"
},
"product":
{
"matrix":
{
"thumbnails":[
{
"_myLabel":"Antique Brass Distressed",
"_thumbImage":"3",
"_cartCode":"160"
},
{
"_myLabel":"Antique Brass Light",
"_thumbImage":"156",
"_cartCode":"156"
},
{
"_myLabel":"Old Iron",
"_thumbImage":"ap",
"_cartCode":"157"
},
{
"_myLabel":"Oil-Rubbed Bronze",
"_thumbImage":"ob",
"_cartCode":"3"
}
],
"_myLabel":"Finishes"
},
"_Title":"Flower Cabinet Knob",
"_itNum":"100407x"
}
}
What I need to do with this is output specific elements on my template - first and foremost, the matrix object with its associated thumbnails. In this example, there is only one matrix, but for many of the products there are multiples, each with their own thumbnail arrays.
This is the controller:
var XMLt = angular.module("XMLtest",[]);
XMLt.factory("Xfactory",function($http){
var factory = [];
factory.getXML = function(){
return $http.get(productXML);
}
return factory;
});
XMLt.controller("Xcontroller",function($scope,Xfactory) {
$scope.Xcontroller = [];
loadXML();
function loadXML() {
Xfactory.getXML().success(function (data) {
var x2js = new X2JS();
prodData = x2js.xml_str2json(data);
$scope.thisItem = prodData.contents;
$scope.matrices = [];
angular.forEach($scope.thisItem.matrix,function(value,key)
{
$scope.matrices.push(value,key);
});
});
}
});
And this is my view template:
<div ng-controller="Xcontroller">
<h2>Title: {{ thisItem.product._Title }}</h2>
<div ng-repeat="thisMatrix in thisItem.product" class="matrix">
{{ thisMatrix._myLabel }}
</div>
</div>
My problem is that this ng-repeat, not surprisingly, returns a div for every child element of the product that it finds, not just the matrix. So I wind up with a couple of empty divs (for _Title and _itNum) in addition to the matrix div.
I've seen quite a few examples of filtering by comparing value literals, but they don't seem to apply in this case. I also tried writing a custom filter:
$scope.isObjType = function(input) {
return angular.isObject(input);
};
<div ng-repeat="thisMatrix in thisItem.product | filter:isObjType(matrix)">
That seemed to have no effect, still returning the extraneous divs. I can't seem to wrap my head around how I'd limit the repeat to a specific object type. Am I thinking of this the completely wrong way? If so, I'd welcome any input.
Since you only have one matrix, you don't need the repeat for the matrix label. You only need it for the thumbnails.
<div ng-controller="Xcontroller">
<h2>Title: {{ thisItem.product._Title }}</h2>
<div class="matrix">
{{ thisItem.product.matrix._myLabel }}
<div ng-repeat="thisThumbnail in thisItem.product.matrix.thumbnails" class="thumbnail">
{{thisThumbnail._myLabel}}
</div>
</div>
</div>
If it were possible to have multiple matrixes, the object would need to be modified to be able to represent that (by wrapping the matrix object in an array.)
Update per comments:
If you have the possiblity of multiple matrixes, you will need to modify the object to ensure that it is consistent when there is 1 vs when there are 2+.
<div ng-controller="Xcontroller">
<h2>Title: {{ thisItem.product._Title }}</h2>
<div ng-repeat="thisMatrix in thisItem.product.matrix" class="matrix">
{{ thisMatrix._myLabel }}
<div ng-repeat="thisThumbnail in thisMatrix.thumbnails" class="thumbnail">
{{thisThumbnail._myLabel}}
</div>
</div>
</div>
and in controller:
XMLt.controller("Xcontroller",function($scope,Xfactory) {
$scope.Xcontroller = [];
loadXML();
function loadXML() {
Xfactory.getXML().success(function (data) {
var x2js = new X2JS();
prodData = x2js.xml_str2json(data);
// must always have an array of matrixes
if (!prodData.contents.product.matrix.slice) {
prodData.contents.product.matrix = [prodData.contents.product.matrix];
}
$scope.thisItem = prodData.contents;
$scope.matrices = [];
angular.forEach($scope.thisItem.matrix,function(value,key)
{
$scope.matrices.push(value,key);
});
});
}
});
So i have a html layout in which there are blocks (there are no fix number of them, because they can be created dynamically).
In these blocks there are boxes (again, they can be created dynamically)
The boxes contain *html element*s and also have different data attributes
So i need to create an object which looks like this
block1 = {
box1 : {
id : box1.data('id'),
content : box1.html()
},
box2 : {
id : box2.data('id'),
content : box2.html()
}
},
block2 = {
box3 : {
id : box3.data('id'),
content : box3.html()
}
}
Please don't write that the syntax is not correct, i know. I just tried to somehow illustrate what i want.
So my question is how do i do this with the help of jQuery?
Thank you in advanced
You can select all blocks and boxes and iterate over each of them using .each [docs]:
var blocks = {};
$('.block').each(function(index) {
var boxes = {};
$(this).find('.box').each(function(index) {
boxes['box' + index] = {
id: $(this).data('id');
content: $(this).html();
};
});
blocks['block' + index] = boxes;
});
You might not need an object of objects though, maybe an array of array suffices or would be even better, depending on what you intend to do with the data.
To learn more about how objects work, have a look at MDN - Working with Object.
Here is an idea:
1- Iterate over all of the blocks using some CSS selector.
2- Create a generic JS object and set a collection attribute called "boxes" to be an array
3- For each one, iterate over all the boxes inside it, again, using some CSS selector.
4- Create a generic JS object for each box and set the attributes as needed.
Code version
I think something like this would work (not tested):
var blocks = new Array();
$(".blocks").each(function(b) {
var my_block = {boxes: new Array()};
var $block = $(b);
$(".box", $block).each(function(box) {
var $box = $(box);
my_block.boxes.push({id: $box.attr("id"), content: $box.html()});
});
blocks.push(my_block);
});
You should take a look at Knockout.js, it's very comfortable to build an application like yours.
In detail: use Objects. Build an array for yourself, containing Objects with e.g. Block Name and all Child nodes.
<div id="lanesContainer" data-bind="foreach: blocks">
<div id="" class="dropLane laneDefault ui-widget-header ui-dialog ui-widget ui-corner-all ui-front ui-resizable">
<div class="ui-dialog-titlebar ui-helper-clearfix" data-bind="drop: {value: $data.dropTask}">
<p class="laneheader" data-bind="text: $data.title">Lane</p>
</div>
<ul data-bind="foreach: box">
<li class="ui-dialog-content laneItem" data-bind="drag: {value: $data}">
<div class="ui-widget-header laneItemHeader" data-bind="text: $data.Title"></div>
<div class="ui-widget-content laneItemBody" data-bind="text: $data.Description"></div>
<div class="ui-widget-content laneItemFooter">
<div class="ui-corner-all ui-state-default notification-important">
<span class="ui-icon ui-icon-notice" title="sometitle" data-bind="css: {'notification-important-hide': !$root.isElementImportant($data) }"></span>
</div>
</div>
</li>
</ul>
</div>
</div>
Is this useful?
Here is how to get an Object with nested Array of Childs:
function laneObject(title) {
var obj = new Object();
obj.title = title; //Identifier for Lane
obj.childs = []; //Elements of Lane to display
return obj;
}
I am not entirely sure of what your question is, but if you want to create blocks and boxes dynamically, I suggest you first of all use Arrays.
//All dynamically created blocks
blocks = [];
//Create blocks
for(var i = 1; i < 3; i++) {
var block = {
//All dynamically generated bloxes
boxes = [];
};
//Create boxes
for(var j = 1; j < 4; j++) {
block.box[j] = {
id : j,
content : '<span>html for box' + j + '</span>'
}
}
blocks[i] = block;
}