Backbone collection.where error - backbone.js

I assume I am doing something silly and clearly wrong, but I am baffled. Using CoffeeScript and marionette backbone, I want to create a methods on my collection that will set most models to selected and then set all selected to unselected. I assumed
deselectAll: ->
#where({selected: true})
would get me the selected models and I could iterate over that (for model in selected) and set the models. But I cannot get #where to work and instead get:
Uncaught TypeError: _.matches is not a function
from backbones:
where: function(attrs, first) {
var matches = _.matches(attrs);
return this[first ? 'find' : 'filter'](function(model) {
return matches(model.attributes);
});
},
UPDATE
It is currently working with filter:
#filter (model) ->
model.get 'selected'
and it seems somewhat more complicated than where was, but perhaps not?

backbone <= 1.2.1 is not compatible with underscore >= 1.8.0. Upgrade your backbone to 1.2.2 or 1.2.3.
In underscore 1.8.0 function matches is deprecated and renamed to matcher. See underscore changelog

Related

Support for Dot Notation in Backbone Models

How to get support for dot notation/nested objects in backbone model. The plugins that are available are buggy and wondering if backbone would ever support
person = { name : {first: 'hon',last:'son'}}
model = new Backbone.Model(person)
model.get('name.first')
model.set('name.first','bon')
There are two plugins to get the job done:
Backbone Nested
Backbone Deep Model
Both handle getting and setting attributes and change events for dot notation.
I did that if i were you:
var nameObj = model.get('name')
nameObj.first = bon
model.set('name', nameObj)

Best Practice to add UI enhancements to multiple Backbone Marionette views

So I was what the best way for all views in an application to have actions performed on an element.
In a non single page application you would run say:
$(document).ready(function() {
$('.autosize').autosize();
});
to apply autosize function to all elements with the autosize class on every page.
Now in a Backbone Marionette app to do this you could perform that in each view with onDomRefresh or similar but for things that affect 90% of views you'd want this to run automatically somehow.
I don't think there's a way that an Application object can listen to all onDomRefresh events which would potentially solve it. I've consider overloading Marionette.MonitorDOMRefreshto add this in but it doesn't feel like a Backbone approach.
Other things I considered were sub-classing each of the marionette views to add mixins for loading different groups of UI elements.
I figured other people must have experienced this scenario so was interested what approaches have been used.
Just make a base View class and inherit from it every view class that needs the autosize enhancement.
var AutosizeBaseView = Backbone.Marionette.ItemView.extend({
onDomRefresh: function(){
this.$('.autosize').autosize();
}
});
then make your classes like this:
var SomeView = AutosizeBaseView.extend({
});
So I couldn't really find any solutions that really solved my problem, despite some helpful chats with #julio_menedez and #marionettejs on Twitter. With a really good idea being using Polymer but wasn't suitable as I need to support older IE's.
So instead I headed into the dangerous world of monkey patching to solve it (Bear in mind I might need to iron out some wrinkles with this still, just finished writing it and not fully tested it - I'll update accordingly)
In Coffeescript: (javascript version at the bottom)
# Monkey patching the Marionette View.. sorry!
# this is the only Marionette view which doesn't have it's own constructor
Marionette.ItemView = Marionette.ItemView.extend
constructor: ->
Marionette.View.prototype.constructor.apply #, Array.prototype.slice.call(arguments, 0)
original_view_constructor = Marionette.View.prototype.constructor
Marionette.View.EventAggregator = event_aggregator = _.extend {}, Backbone.Events
# all the other constructors call this so we can hijack it
Marionette.View.prototype.constructor = ->
event_aggregator.listenTo #, 'all', =>
args_array = Array.prototype.slice.call arguments, 0
event_aggregator.trigger.apply event_aggregator, [ 'view:' + args_array[0], # ].concat(args_array.slice(1))
event_aggregator.stopListening # if args_array[0] == 'close'
original_view_constructor.apply #, Array.prototype.slice.call(arguments, 0)
And then to use I just setup a listener in my application object to catch view events I need. e.g:
#listenTo Marionette.View.EventAggregator, 'view:dom:refresh', (view) ->
view.$('div').css('backgroundColor', 'red');
So in my view these are the pros and cons of this technique:
Pros:
Can listen to all view events without injecting all view classes or subclassing all view classes
Simple to use
Objects don't need to opt-in to using it at all
Cons
Uses monkey patching, dangerous to Marionette API Changes
Uses Marionette namespacing so vulnerable to a future Marionette namespace collision
Takes dealing with views out of view context
Having an event aggregator object isn't something seen elsewhere in Backbone/Marionette (afaiw) so breaks a pattern (update - something similar is seen with Backbone.history)
Anyway I'm welcome to feedback, alternatives, criticism :-) and hope maybe this helps someone else in the same situation
Javascript:
(function() {
var event_aggregator, original_view_constructor;
Marionette.ItemView = Marionette.ItemView.extend({
constructor: function() {
return Marionette.View.prototype.constructor.apply(this, Array.prototype.slice.call(arguments, 0));
}
});
original_view_constructor = Marionette.View.prototype.constructor;
Marionette.View.EventAggregator = event_aggregator = _.extend({}, Backbone.Events);
Marionette.View.prototype.constructor = function() {
var _this = this;
event_aggregator.listenTo(this, 'all', function() {
var args_array;
args_array = Array.prototype.slice.call(arguments, 0);
event_aggregator.trigger.apply(event_aggregator, ['view:' + args_array[0], _this].concat(args_array.slice(1)));
if (args_array[0] === 'close') {
return event_aggregator.stopListening(_this);
}
});
return original_view_constructor.apply(this, Array.prototype.slice.call(arguments, 0));
};
}).call(this);
In CoffeeScript I think you could also do:
extend = (obj, mixin) ->
obj[name] = method for name, method of mixin
obj
include = (klass, mixin) ->
extend klass.prototype, mixin
include Marionette.View,
onDomRefresh: () -> #$('.autosize').autosize()
Which should cover all the view types. Haven't tested this specifically, but just did something very similar to add functionality to Marionette's Layout view. Extend / include pattern at http://arcturo.github.io/library/coffeescript/03_classes.html. Of course this should all be doable in straight up JS too.
UPDATE:
Actually, since we have Underscore available to us we don't need to manually define the include and extend methods. We can just say:
_.extend Marionette.View.prototype,
onDomRefresh: () -> #$('.autosize').autosize()

Backgrid.js error, "has no method 'omit'"?

I'm working on an app in Backbone and am trying to implement Backgrid.js, but I keep getting this error:
Uncaught TypeError: Object function (a){return new m(a)} has no method 'omit'
This occurs when the Backgrid.Grid is initializing and seems to be related to the columns. I have checked my syntax and even created a simplified column array with 1 item and a simplified collection object, with a single attribute matching the column.
var columns = [{name: 'year', label: 'Year', cell: 'string'}];
var data = [{year: '1964'}, {year: '1965'}];
My grid is initialized with:
var grid = new Backgrid.Grid({ columns: columns, collection: data});
Pretty simple and straightforward, but I am still getting this error. I have also checked my versions of Backbone, Underscore and they match what is included in the Backgrid.js download.
Anybody else come across this or have any ideas what might be causing the issue?
There's only one omit call in Backgrid:
var passedThruOptions = _.omit(options, ["el", "id", "attributes",
"className", "tagName", "events"]);
and _.omit was added in Underscore 1.4.0 last September:
1.4.0 — Sept. 27, 2012
[...]
Added an omit function, for filtering an object to remove certain keys.
So presumably you're using a version of Backgrid that expects at least Underscore 1.4.0 but your actually using an older version of Underscore. Try upgrading Underscore (and Backbone while you're at it) to the latest version.

Overwrite properties in angular forEach

I imagine this is an easy thing to do, but I wasnt able to find the information I was looking for through google. I have popupProperties which is just default stuff. I then call to the service which returns specific overrides depending on the popup. How can I iterate through all of the service's overrides and apply them to the popupProperties?
var popupProperties = getDefaultPopupProperties();
var popupOverrides= popupService.getPopupOverrides(currPopupId);
angular.forEach(popupOverrides, function(popupProperty, propertyName){
//replace defaults with popupData's properties
});
You should have a look at the solution of Josh David Miller which uses the extend method of angular (documentation).
var defaults = {name:'John',age:17,weight:55};
var overrides = {name:'Jack',age:28,color:'brown'};
var props = angular.extend(defaults, overrides);
// result
props: {
name:'Jack',
age:28,
weight:55,
color:'brown'
}
The values are copied in the defaults variable. There is no need of using the return value (var props =).
I presume you mean both functions are returning objects with a number of properties (as opposed to an array).
If so, the following should work - just JavaScript, nothing AngularJS specific:
for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }
See this question for more details How can I merge properties of two JavaScript objects dynamically?

Uncaught Error: A "url" property or function must be specified for a CollectionView

I know this error has come up a few times, but I'm still not sure how to make this work appropriately..
My magic begins here :
var list_edit_member_view = new app.views.ListMemberEdit({
el: $("#enterprise_member_list_edit_container"),
list_ids: list_ids
});
list_edit_member_view.render();
And this loads this View (ListMemberEdit.js) which has this in the render() :
this.list_edit_member_view = new app.views.CollectionView({
el: $("#enterprise_member_list_edit_container"),
collection: app.peers,
list_item: app.views.ListMemberEditSelection,
list_item_options: {list_ids: this.options.list_ids}
});
Which loads a CollectionView view that renders its list_item_options as model views.. It is within this file (ListMemberEditSelection.js), that when I perform this.destroy, it will return :
Uncaught Error: A "url" property or function must be specified
So this makes me think that the Model or the Model URL is not being defined.. I'm just not sure where to put this since it works very similar to my other partials that are doing roughly the same thing..
Any thoughts? My apologies for the vagueness. Let me know if there's anything else you would like to look at!
I'm curious if its possible to see where this URL attribute would be written within the Object Model or Collection itself.
This is because destroy() function will call Backbone.sync to update the server too, not only your models in the frontend. http://backbonejs.org/#Model-destroy
So, if you're using REST to sync your data, you'll need to set a url property in your model so Backbone know where to send request:
Backbone.Model.extend({
url: "http://myapi.com/"
})
To allow more flexibility, you can also set a urlRoot: http://backbonejs.org/#Model-urlRoot
I had a similar problem, I removed the "id":"" from my models default values and the problem was solved.
I did receive similar error
Try this: I am just making an assumption what your model might look like
window.MyModel = Backbone.Model.extend({
url: function(){
return this.instanceUrl;
},
initialize: function(props){
this.instanceUrl = props.url;
}
}
Please look at this question that I had posted myself for more details: https://stackoverflow.com/a/11700275/405117
I am providing this reference as the answers here helped me better understand
Hope this helps!

Resources