问题
In the last view weeks I tried many ways to get Rivets work with Backbone.View and Backbone.Collection. All my tries where covered by examples I have found on the internet. But still, I'm not successful.
Here is the challenge I currently try to realize:
- Making Rivets recognize Backbone.Collection(s) and getting all events observed
- Use a Backbone.View as template for Rivets which holds input fields for all objects in the Backbone.Collection
- Using all for this the "."-adapter of Rivets by overriding the Rivets function like shown in a lot of examples over the net.
My adapter source code looks like this:
var dotAdapter = rivets.adapters[
'.'],
originalSubscribe =
dotAdapter.subscribe,
originalUnsubscribe =
dotAdapter.unsubscribe;
dotAdapter.subscribe = function (obj, keypath, callback) {
if (obj === undefined || obj === null) {
return;
}
// Subscribe model
if (obj instanceof Backbone.Collection) {
obj.on('add remove reset', function () {
callback(obj);
});
obj.on('change:' + keypath, function (
m, v) {
callback(v);
});
} else if (obj != null && obj instanceof Backbone.Model) {
obj.on('change:' + keypath, function (
m, v) {
callback(v);
});
obj.on('reset:' +
keypath, function (
m, v) {
callback(v);
});
} else {
originalSubscribe.apply(
this, arguments);
}
};
dotAdapter.unsubscribe =
function (obj, keypath,callback) {
if (obj === undefined || obj === null ) {
return;
}
// Unsubscribe model
if (obj instanceof Backbone.Collection) {
obj.off('add remove reset', function () {
callback(
obj);
});
obj.off('change:' + keypath, function (m, v) {
callback(v);
});
}
else if (obj instanceof Backbone.Model) {
obj.off('change:' + keypath, function (m, v) {
callback(v);
});
obj.off('reset:' + keypath, function (m, v) {
debugger;
callback(v);
});
} else {
originalUnsubscribe.apply(
this, arguments);
}
};
dotAdapter.read = function (obj,keypath) {
if (obj === null || obj === undefined ) {
return;
}
if (obj instanceof Backbone.Model) {
return obj.get(keypath);
}
else if (obj instanceof Backbone.Collection)
{
return obj.models;
} else {
return obj[keypath];
}
};
dotAdapter.publish = function (obj, keypath, value) {
if (value === "") {
value = null;
}
if (obj instanceof Backbone.Collection) {
obj.models = value;
}
else if (obj instanceof Backbone.Model) {
obj.set(keypath, value);
}
else {
obj[keypath] = value;
}
};
Can anyone point me in the right direction how I have to override the "."-adapter of rivets for making it work with Backbone.Collection object?
In my collections I will have Backbone.Model objects which should be shown on the according template.
回答1:
I strongly suggest not to override the default .
adapter.
This can cause unexpected behaviour, for example in one of the projects I have worked someone overridden the .
adapter to something like this:
get: function(obj, keypath) {
if(obj instanceof Backbone.Model)
return obj.get(keypath);
return obj[keypath];
},
this resulted in other developers not being able to access a direct property in the model (properties outside attributes
) which caused few weird bugs.
For such reasons, it's better to create separate adapters for special use cases. Also they make it easier to understand the relationship between objects by seeing the bindings which is very useful when dealing with nested structures.
For eg: users:roles.admin:priviledges.create // seeing this we figure out that users and admin are both models
Below is a simple example using the :
showcased in rivets.js documentation.
Since objects are passed via reference in javascript, updates to models will automatically reflect in collection, no need to worry about making rivets observe backbone collection events
rivets.adapters[':'] = {
observe: function(obj, keypath, callback) {
obj.on('change:' + keypath, callback)
},
unobserve: function(obj, keypath, callback) {
obj.off('change:' + keypath, callback)
},
get: function(obj, keypath) {
return obj.get(keypath)
},
set: function(obj, keypath, value) {
obj.set(keypath, value)
}
}
var data = [{
name: "john",
age: 10
}, {
name: "joseph",
age: 11
}, {
name: "joy",
age: 12
}]
var userCollection = new Backbone.Collection(data);
var View = Backbone.View.extend({
initialize: function(options) {
this.render()
},
render: function() {
rivets.bind(this.el, {
users: this.collection
});
}
});
var userView = new View({
el: '#user-list',
collection: userCollection
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rivets/0.8.1/rivets.bundled.min.js"></script>
<ul id='user-list'>
<li rv-each-user="users.models">
<input type="text" rv-value="user:age"/>
<span>{user:name}</span><span> {user:age}</span>
</li>
</ul>
回答2:
I'm new to Rivets. So i was searching for adapters and tried your code. It worked for me. So I'm posting this, so it may help someone like me..
My html page contains something like below for ':' adapter.
<ul id='item-list'>
<li rv-each-contact="contacts:">
<a rv-href='contact:url' >{contact:Name}</a></br></br>
</li>
</ul>
To make the code work for '.' adapter, i changed the line
<li rv-each-contact="contacts:">
to
<li rv-each-contact="contacts.">
来源:https://stackoverflow.com/questions/22592857/rivets-backbone-view-and-backbone-collection