I'm aware that Angular filters can only be applied to arrays, not objects
I'm attempting to include templates added dynamically using the following code. All seems to work well until you see the order
What I would like to have this order:
Create
Book
Address
here is the Plunker
seems like angular will get ng-include by ordering the names according to their name,
so when you use
$scope.templates =
{
_address : 'address.html',
_create : 'create.html',
_book : 'book.html'
};
ordering template according to their names, then _address comes first _book comes second _create comes third
simple approach to solve
$scope.templates =
{
_a_create : 'create.html',
_b_address : 'address.html',
_c_book : 'book.html'
};
here is the Plunker
Instead of using a key-value object, why not use an array? ng-repeat orders the iteration by the index of the iterated object/array.
FORKED DEMO
$scope.templates = [
'create.html',
'book.html',
'address.html'
];
Related
I have a series of arrays, filled with objects - in JSON format. They look something like this:
Group 1
[
{ "name" : "John",
"age" : "31"
},
{ "name" : "Bob",
"age" : "33"
}
]
Group 2
[
{ "name" : "Jim",
"age" : "46"
},
{ "name" : "Harry",
"age" : "23"
}
] // ... and so on ...
In Angular, how can I join the two arrays to form an array of arrays? I'm guessing it's group1.concat(group2), or something like that? I'm not sure where to do it though, would I do this in the controller?
Currently I have a $scope property assigned to each variable, would I make a new $scope property that was a concatenated array of each of these?
And would that be something like:
$scope.allGroups = []
$scope.allGroups = $scope.group1.concat($scope.group2)
// since 'allGroups', 'group1', and 'group2' have all been defined can I do...
allGroups = group1.concat(group2) // ...or does $scope need to be used each time?
My intention is (with the necessary filters) to be able to do an ng-repeat through all groups as they will now all be linked to one $scope variable.
I'm pretty sure that's laiden with errors, but I thought it better to provide some bad code than nothing at all, just so it was more evident what I was trying to do. If there are better approaches (which I'm sure there are), I'm all ears.
Thanks in advance
You're right, array1.concat(array2) is the good method to use.
Now the question is, do you need group1 and group2 to be on your $scope ? Do you need to display them ?
If the answer is no, then you could simply do as follow:
Recover the two arrays and store them in 2 "private" variables
Concat them into a variable set into your $scope
You dont have to set variable into your $scope if you dont display them. It will then look like this:
$scope.allGroups = group1.concat(group2)
Otherwise, no other choice than do like you said:
$scope.allGroups = $scope.group1.concat($scope.group2)
EDIT
If you want an array containing the group1 and group2 arrays, and not only their content, you can simply use the push() method as follow:
$scope.allGroups = [];
$scope.allGroups.push(group1, group2);
If you want to be able to access the concatenated array from your views you have to attach the concatenated array in the $scope object, so you will have to use
$scope.allGroups = $scope.group1.concat($scope.group2)
In the case that you leave var allGroups not attached to the $scope object allGroups will be a local variable to the controller function and will be available only through a closure
You can use concat() to join one array with another.
concat() function returns an array.
Here is the code:
$scope.a = [1,2];
$scope.b = [3,4];
$scope.c = $scope.a.concat($scope.b);
I am trying to apply Angular filter to the following array after following the instructions here http://docs.angularjs.org/api/ng.filter:filter:
[{"id":"compute-1.amazonaws.com_Delivery","id":"compute-1.amazonaws.com_TaskJob","id":"UpdateFiles","id":"TaskRequest"}]
Say the filter values are:
filter = "TaskRequest";
filter = "Taskjob";
filter = "UpdateFiles";
filter = "Delivery";
How can I apply Angular filter:
I have tried:
conData = JSON.stringify(data);
results = $filter('filter')(conData, filter, false);
I have also tried :
results = $filter('filter')(conData, filter, true);
results = $filter('filter')(conData, filter);
When I examine the results of the filter nothing changes and I get back the same array.
So if the filter applied was "Delivery"
The results array would be :
[{"id":"compute-1.amazonaws.com_Delivery"}]
What am I doing wrong here?
Not sure if it is a copy/paste mistake but the array you provide in your first source extract is wrong (or at least won't do what you expect). If you look closely the key id is repeated inside the same JSON object. Try with the following array :
[
{"id":"compute-1.amazonaws.com_Delivery"},
{"id":"compute-1.amazonaws.com_TaskJob"},
{"id":"UpdateFiles"},
{"id":"TaskRequest"}
];
BTW I assembled a lighter version of your code accessible here where the filtering is working.
I want to start with a base JSON, ie [], and provide a tree like structure with options to edit node (change or extend existing keys/values), add sibling (create new entry), and add child (extend the json to become a map, ie add { "field1" : "value1", "field2" : "value2"} to "data".
Seems like the best way to do this is to bind a json scope value to a tree structure. I am about to build one myself, but thought I would check to see if it's been done already....
This sort of feature would allow someone closer to the business to define and refine the data model, as well as make simple edits. Think the php myadmin interface, or the django admin page.
This fiddle will give you a headstart. It was actually referenced from this question. It does not deal with object parameters, just nodes in the tree, but adding these should be pretty straightforward from a controller standpoint. The real challenge is in developing a good-looking view. Here is the controller part (just to make SO happy):
angular.module("myApp", []).controller("TreeController", ['$scope', function($scope) {
$scope.delete = function(data) {
data.nodes = [];
};
$scope.add = function(data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
data.nodes.push({name: newName,nodes: []});
};
$scope.tree = [{name: "Node", nodes: []}];
}]);
Check it out json-tree angularjs directive.
This looks like a good implementation of what you're looking for.
I'm trying display some data loaded from a datastore and it's not reflecting changes on the UI. I created an example to show a general idea of what I'm trying to achieve.
http://plnkr.co/edit/MBHo88
Here is the link to angularjs example where they show when on click then dropdowns are clear out. If you replace the expression with one of the colors of the list dropdowns are well selected. Does this type of selection only work on user events?
http://docs.angularjs.org/api/ng.directive:select
Help is appreciated!!!
Actually the problem is that ngSelect compares objects using simple comparition operator ('=='), so two objects with same fields and values are considered as different objects.
So you better use strings and numbers as values ('select' parameter in expression of ngSelect directive).
Here is kind of solution for your plunker.
Aslo there are some discussion about this topic on GitHub:
https://github.com/angular/angular.js/issues/1302
https://github.com/angular/angular.js/issues/1032
Also as I headred there is some work in progress about adding custom comparor/hashing for ngSelect to be able to use ngSelect more easier on objects.
One mistake in the initialization of your controller. You have to refer to the objects in your palette, since these are watched on the view:
$scope.selectedColors.push({col: $scope.palette[2]});
$scope.selectedColors.push({col: $scope.palette[1]});
Same with your result:
$scope.result = { color: $scope.obj.codes[2] };
Then you need to watch the result. In the below example, select 1 receives the value from the initiating select, the second receives the value below in the palette. I don't know if that's what you wanted, but you can easily change it:
$scope.$watch('result', function(value) {
if(value) {
var index = value.color.code -1;
$scope.selectedColors[0] = {col: $scope.palette[index] };
$scope.selectedColors[1] = {col: $scope.palette[Math.max(index-1, 0)] };
}
}, true);
See plunkr.
Ok, I think I figured this out but thanks to #ValentynShybanov and #asgoth.
According to angularjs example ngModel is initialized with one of the objects from the array utilized in the dropdown population. So having an array as:
$scope.locations = [{ state: 'FL', city: 'Tampa'}, {state: 'FL', city: 'Sarasota'} ....];
And the dropdown is defined as:
<select ng-options="l.state group by l.city for l in locations" ng-model="city" required></select>
Then $scope.city is initialized as:
$scope.city = $scope.locations[0];
So far so good, right?!!!.. But I have multiple locations therefore multiple dropdowns. Also users can add/remove more. Like creating a table dynamically. And also, I needed to load data from the datastore.
Although I was building and assigning a similar value (e.g: Values from data store --> State = FL, City = Tampa; Therefore --> { state : 'FL', city : 'Tampa' }), angularjs wasn't able to match the value. I tried diff ways, like just assigning { city : 'Tampa' } or 'Tampa' or and or and or...
So what I did.. and I know is sort of nasty but works so far.. is to write a lookup function to return the value from $scope.locations. Thus I have:
$scope.lookupLocation = function(state, city){
for(var k = 0; k < $scope.locations.length; k++){
if($scope.locations[k].state == state && $scope.locations[k].city == city)
return $scope.locations[k];
}
return $scope.locations[0]; //-- default value if none matched
}
so, when I load the data from the datastore (data in json format) I call the lookupLocation function like:
$scope.city = $scope.lookupLocation(results[indexFromSomeLoop].location.state, results[indexFromSomeLoop].location.city);
And that preselects my values when loading data. This is what worked for me.
Thanks
In my Backbone app, on my collection I have numerous sorting methods, when rendering the views based on the collection I am currently using a global var set via the route (I do it with a global as other actions add to the collection and I want the last ordering to be used). For example
routes : {
"" : "index",
'/ordering/:order' : 'ordering'
},
ordering : function(theorder) {
ordering = theorder;
listView.render();
},
then in my view
if (typeof ordering === 'undefined') {
d = this.collection.ordered();
}
else if(ordering == 'owners') {
d = this.collection.owners();
}
_.each(d, function(model){
model.set({request : self.model.toJSON()});
var view = new TB_BB.OfferItemView({model : model});
els.push(view.render().el);
});
Where ordered and owners are the 2 ordering methods.
So my first question is, based on routes could someone advice a better way of implementing above? This view gets rendered in multiple places hence me using a global rather than passing a ordered var to the method?
Second question is - I would like to also add some filtering, so lets say I want to sort by 'price' but also do some filtering (lets say by multiple categories id). How could I add a flexible 'route' to deal with filtering.
I guess I could do
routes : {
"" : "index",
'/ordering/:order/:filter1/:filter2' : 'ordering'
},
So the filter1 and filter2 would be the subsequent filtering, but if the filters could be 0 or 100 this will not work. Could anyone offer a solution?
Well, first you should be using Backbone's built-in ability to auto-sort collections. You can take advantage of this by defining a comparator function on your collection. This gives you all kinds of wins right out of the box — for example, the collection will re-sort itself every time you add or remove something from it, based on your comparator. If you want to define multiple sort functions, just define them all as functions and then update comparator when you need to. Then you can ditch that ugly global var.
For your second question, I'm not totally sure what you mean by "if the filters could be 0 or 100 this will not work." If you mean that you'll run into trouble if you don't specifiy all of the filters, then that's true. But you can use a wildcard to fix that. Here's what that might look like:
// your routes look like this:
routes : {
'/ordering/:order/filters/*filters' : 'ordering' // your routes will look like: /ordering/price/filters/filter_one/filter_two/filter_three
},
ordering: function (order, filters) {
filters = filters.split('/'); // creates an array of filters: ['filter_one', 'filter_two', 'filter_three']
listView.render(filters); // pass your filters to the view
}
// listView.render() looks like this:
render: function(filters) {
collection = this.collection;
_.each(filters, function (filter) {
collection = collection.filter(function () {
// your actual filtering code based on what the filter is
});
});
}