I have an array that I am iterating through and displaying. Sometimes, however, the item it finds is an array. I'd like to do something different with this, using an ng-if. I am unsure how to do this easily. It seems like a common issue but there doesn't appear to be an easy solution.
You can use Angular.isArray() (see doc) but it does not exist as a already defined filter so you might need to define your own.
Something like that :
angular.module('...', []).filter('isArray', function() {
return function (input) {
return angular.isArray(input);
};
});
And then in your template usimply use {{ myVar | isArray }}.
My only concerne is ... is it really clean to do so ? I don't know but this will solve your problem.
By the way, was already asked on StakcOverFlow : Angular expression to check if model is array or object
Related
I've got an angular application and I need to handle collections of objects, that may have following attribute:
'field': "string string"
or
'field': ["string1", "string2", "string3"]
I need to display it: either a string or comma-separated list of strings.
What is the best angular way to handle such different objects? I know I might do if:block, if-not:block. But it would get extremely verbose. Is there a better way?
Filter is your best bet. Something like this
app.filter('separate', function () {
return function (input) {
return angular.isArray(input) ? input.join() : input;
};
});
The usage is {{ field | separate }}.
You could do the similar thing with function defined in controller's scope, but that's what filters are for.
Also, you can output arrays in expressions without any filters, e.g. if you need it for debugging, Angular parses them to strings.
I have an angular-rails resource with a property that consists of irregular data that is potentially quite complicated-- something like:
{ foo: [ { bar: 'baz', lol: [ { 'omg': ... etc
I built a directive which takes this data and drills down into it, dynamically rendering form fields for each object... I've got the data displaying perfectly, however the piece of the puzzle that's missing is, how can I take advantage of Angular's binding so that changing the value on the form input will actually update that attribute in the model?
Originally I was thinking this should be simple, as my code drills through the data structure, it can just be maintaining a path, so I'd end up with something like: 'myObject.foo.bar'
Then I could just pass that to the form input's ng-model attribute...... however, I could not get angular to recognize ng-model="path" where $scope.path = "myObject.foo.bar"... ng-model="{{path}}" did not work either.
My directive is using angular.forEach to drill down into this datastructure, and someone had mentioned to me that I should perhaps be using ng-repeat instead, but I wasn't sure if this is the correct way to go or not? I still feel like there should just be a way to do ng-model="path" and have that work...
Any guidance would be greatly appreciated.
To use dynamic property names, use array notation. I.e. myObject["foo"]["bar"]. Plunkr: http://plnkr.co/edit/W60F75?p=preview
Can you try setting an property on the scope with the value of the object itself and then refer it in the form element? Like below:
// In your script
$scope.obj = myObject;
// In your form
<input ng-model="obj.foo.bar" type="text" />
I have an array of items bound to <li> elements in a <ul> with AngularJS. I want to be able to click "remove item" next to each of them and have the item removed.
This answer on StackOverflow allows us to do exactly that, but because the name of the array which the elements are being deleted from is hardcoded it is not usable across lists.
You can see an example here on JSfiddle set up, if you try clicking "remove" next to a Game, then the student is removed, not the game.
Passing this back from the button gives me access to the Angular $scope at that point, but I don't know how to cleanly remove that item from the parent array.
I could have the button defined with ng-click="remove('games',this)" and have the function look like this:
$scope.remove = function (arrayName, scope) {
scope.$parent[arrayName].splice(scope.$index,1);
}
(Like this JSFiddle) but naming the parent array while I'm inside it seems like a very good way to break functionality when I edit my code in a year.
Any ideas?
I did not get why you were trying to pass this .. You almost never need to deal with this in angular. ( And I think that is one of its strengths! ).
Here is a fiddle that solves the problem in a slightly different way.
http://jsfiddle.net/WJ226/5/
The controller is now simplified to
function VariousThingsCtrl($scope) {
$scope.students = students;
$scope.games = games;
$scope.remove = function (arrayName,$index) {
$scope[arrayName].splice($index,1);
}
}
Instead of passing the whole scope, why not just pass the $index ? Since you are already in the scope where the arrays are located, it should be pretty easy from then.
I'm attempting to learn backbone.js and (by extension) underscore.js, and I'm having some difficulty understanding some of the conventions. While writing a simpel search filter, I thought that something like below would work:
var search_string = new RegExp(query, "i");
var results = _.filter(this, function(data){
return search_string.test(data.get("title"));
}));
But, in fact, for this to work I need to change my filter function to the following:
var search_string = new RegExp(query, "i");
var results = _(this.filter(function(data){
return search_string.test(data.get("title"));
}));
Basically, I want to understand why the second example works, while the first doesn't. Based on the documentation (http://documentcloud.github.com/underscore/#filter) I thought that the former would have worked. Or maybe this just reflects some old jQuery habits of mine... Can anyone explain this for me?
I'd guess that you're using a browser with a native Array#filter implementation. Try these in your console and see what happens:
[].filter.call({ a: 'b' }, function(x) { console.log(x) });
[].filter.call([1, 2], function(x) { console.log(x) });
The first one won't do anything, the second will produce 1 and 2 as output (http://jsfiddle.net/ambiguous/tkRQ3/). The problem isn't that data is empty, the problem is that the native Array#filter doesn't know what to do when applied to non-Array object.
All of Underscore's methods (including filter) use the native implementations if available:
Delegates to the native filter method, if it exists.
So the Array-ish Underscore methods generally won't work as _.m(collection, ...) unless you're using a browser that doesn't provide native implementations.
A Backbone collection is a wrapper for an array of models, the models array is in c.models so you'd want to:
_.filter(this.models, function(data) { ... });
Backbone collections have several Underscore methods mixed in:
Backbone proxies to Underscore.js to provide 28 iteration functions on Backbone.Collection.
and one of those is filter. These proxies apply the Underscore method to the collection's model array so c.filter(...) is the same as _.filter(c.models, ...).
This mixing-in is probably what's confusing the "should I use the native method" checks that Underscore is doing:
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
You can use _.filter on a plain old object (_.filter({a:'b'}, ...)) and get sensible results but it fails when you _.filter(backbone_collection, ...) because collections already have Underscore methods.
Here's a simple demo to hopefully clarify things: http://jsfiddle.net/ambiguous/FHd3Y/1/
For the same reason that $('#element') works and $#element doesn't. _ is the global variable for the underscore object just like $ is the global variable for the jQuery object.
_() says look in the _ object. _filter says look for a method named _filter.
I have a JST-->EJS backbone template for a 'projects' form. Ideally, I'd like to use the same template for both creating and editing models.
At the moment, I have something such as this in the template:
<input type='text' name='demo_field' value='<%= demo_field %>' />
That works fine so long as demo_field has a value, but if it doesn't, the whole form fails with an uncaught referenceError.
So, what's the most elegant way to assure variable fields return blank, instead of fail, in Backbone...or JST...or EJS?
Cheers...
I'd probably make a helper, since javascript doesn't have any nice way of doing something like
field if field? in CoffeeScript.
Throw something like this into a helper function:
function printIfExists(field) {
return (typeof field !== "undefined" && field !== null) ? field : '';
}
and call it in your templates. Easy as pie!
You might also want to use the alternate function declaration:
var printIfExists = function(field){...}
I've been using coffeescript so long I can't recall why... scoping issues with the former example, probably.