我使用Backbone作为个人项目,我在其中创建了一个名为MyModel
的模型。在初始化此模型时,我想从第三方API的JSON响应中填充其属性:
app.MyModel = Backbone.Model.extend({
url: 'https://api.xxxxxx.com/v12_1/item?id=53444d0d7ba4ca15456f5690&appId=xxxx&appKey=yyyy',
defaults: {
name: 'Default Name'
}
});
此模型用于将在另一个模型中嵌入的属性中使用的集合中:
app.MyModels = Backbone.Collection.extend({
model: app.MyModel
});
app.MyModel2 = Backbone.Model.extend({
// Default attributes
defaults: {
name: 'Default Name'
},
initialize: function() {
this.myModels = new app.MyModels();
this.myModels.on('change', this.save);
}
});
在为MyModel2
创建的视图中,我向全局元素添加了一个侦听器,以便我们可以在MyModel
内初始化并将MyModels
的实例添加到MyModel2
。
app.MyModel2View = Backbone.View.extend({
initialize: function() {
// ...some code...
var self = this;
this.$(".add-myModel").click(function() {
var myModel = new app.MyModel();
myModel.fetch();
self.model.myModels.add(myModel);
});
// ...some code...
},
// ...some code...
});
这实际上是在实现预期目标,但在单击元素并添加实例时在控制台中抛出错误:
backbone.js:646 Uncaught TypeError: this.isNew is not a function
这是Backbone中从外部API填充模型实例的正确方法吗?我试图弄清楚这个错误的原因。
答案 0 :(得分:2)
很难说没有更完整的信息,但在保存MyModels
时,您似乎没有正确设置上下文:
this.myModels.on('change', this.save);
这是on()
方法的可选最后一个参数,所以可能:
this.myModels.on('change', this.save, this);
中的详细信息
答案 1 :(得分:1)
虽然Stephen is right,但他只关注最可能的错误并让你处理其他一切。我将在答案中尝试对此进行扩展。
API的网址非常复杂,每次需要时复制粘贴都很麻烦。最好将URL处理放在一个地方,一种方法是使用简单的服务。
// The API service to use everywhere you need the API specific data.
app.API = {
protocol: 'https',
domain: 'api.xxxxxx.com',
root: '/v12_1/',
params: {
appId: 'xxxx',
appKey: 'yyyy',
},
/**
* Get the full API url, with your optional path.
* @param {String} path (optional) to add to the url.
* @return {String} full API url with protocol, domain, root.
*/
url: function(path) {
path = path || '';
if (path.slice(-1) !== '/') path += '/';
return this.protocol + "://" + this.domain + this.root + path;
},
/**
* Adds the query string to the url, merged with the default API parameters.
* @param {String} url (optional) before the query string
* @param {Object} params to transform into a query string
* @return {String} e.g.: "your-url?param=value&otherparam=123"
*/
applyParams: function(url, params) {
return (url || "") + "?" + $.param(_.extend({}, this.params, params));
},
};
填写API信息。
然后,您可以创建基本模型和集合(或替换默认的Backbone行为)。
app.BaseModel = Backbone.Model.extend({
setId: function(id, options) {
return this.set(this.idAttribute, id, options);
},
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
var id = this.get(this.idAttribute);
return app.API.applyParams(base, this.isNew() || { id: encodeURIComponent(id) });
},
});
app.BaseCollection = Backbone.Collection.extend({
model: app.BaseModel,
sync: function(method, collection, options) {
var url = options.url || _.result(model, 'url') || urlError();
options.url = aop.API.applyParams(url);
return app.BaseCollection.__super__.sync.apply(this, arguments);
}
});
然后使用它就像这样简单:
app.MyModel = app.BaseModel.extend({
urlRoot: app.API.url('item'),
})
app.Collection = app.BaseCollection.extend({
model: app.MyModel,
url: app.API.url('collection-items'),
});
以下测试输出:
var app = app || {};
(function() {
app.API = {
protocol: 'https',
domain: 'api.xxxxxx.com',
root: '/v12_1/',
params: {
appId: 'xxxx',
appKey: 'yyyy',
},
/**
* Get the full API url, with your optional path.
* @param {String} path (optional) to add to the url.
* @return {String} full API url with protocol, domain, root.
*/
url: function(path) {
path = path || '';
if (path.slice(-1) !== '/') path += '/';
return this.protocol + "://" + this.domain + this.root + path;
},
/**
* Adds the query string to the url, merged with the default API parameters.
* @param {String} url (optional) before the query string
* @param {Object} params to transform into a query string
* @return {String} e.g.: "your-url?param=value&otherparam=123"
*/
applyParams: function(url, params) {
return (url || "") + "?" + $.param(_.extend({}, this.params, params));
},
};
app.BaseModel = Backbone.Model.extend({
setId: function(id, options) {
return this.set(this.idAttribute, id, options);
},
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
var id = this.get(this.idAttribute);
return app.API.applyParams(base, this.isNew() || {
id: encodeURIComponent(id)
});
},
});
app.BaseCollection = Backbone.Collection.extend({
model: app.BaseModel,
sync: function(method, collection, options) {
var url = options.url || _.result(model, 'url') || urlError();
options.url = aop.API.applyParams(url);
return app.BaseCollection.__super__.sync.apply(this, arguments);
}
});
app.MyModel = app.BaseModel.extend({
urlRoot: app.API.url('item'),
})
app.Collection = app.BaseCollection.extend({
model: app.MyModel,
url: app.API.url('collection-items'),
});
var model = new app.MyModel();
console.log("New model url:", model.url());
model.setId("53444d0d7ba4ca15456f5690");
console.log("Existing model url:", model.url());
var collection = new app.Collection();
console.log("collection url:", _.result(collection, 'url'));
var modelUrlThroughCollection = new app.BaseModel({
id: "test1234"
});
collection.add(modelUrlThroughCollection);
console.log("model via collection:", modelUrlThroughCollection.url());
})();

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.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.3.3/backbone-min.js"></script>
&#13;
New model url: https://api.xxxxxx.com/v12_1/item/?appId=xxxx&appKey=yyyy
Existing model url: https://api.xxxxxx.com/v12_1/item/?appId=xxxx&appKey=yyyy&id=53444d0d7ba4ca15456f5690
collection url: https://api.xxxxxx.com/v12_1/collection-items/
model via collection: https://api.xxxxxx.com/v12_1/collection-items/?appId=xxxx&appKey=yyyy&id=test1234
Backbone.js通过提供模型为Web应用程序提供结构 使用键值绑定和自定义事件,具有丰富API的集合 可枚举函数,具有声明性事件处理的视图,以及 通过RESTful JSON界面将其全部连接到现有API 。
如果您使用的API遵循REST原则,则可能是返回对象数组的端点。这是集合应该获取其数据的地方。
app.Collection = app.BaseCollection.extend({
model: app.MyModel,
url: app.API.url('collection-items'),
});
var collection = new app.Collection();
// GET request to
// https://api.xxxxxx.com/v12_1/collection-items/?appId=xxxx&appKey=yyyy
collection.fetch();
它应该收到类似的东西:
[
{ id: "24b6463n5", /* ... */ },
{ id: "345333bbv", /* ... */ },
{ id: "3g6g346g4", /* ... */ },
/* ... */
]
如果要将现有模型(使用ID引用)添加到集合中:
var model = new app.MyModel({
// giving an id to a model will make call to fetch possible
id: "53444d0d7ba4ca15456f5690"
});
// GET request to
// https://api.xxxxxx.com/v12_1/item/?appId=xxxx&appKey=yyyy&id=53444d0d7ba4ca15456f5690
model.fetch();
collection.add(model);
响应应该是单个对象:
{ id: "53444d0d7ba4ca15456f5690", /* ... */ }
如果您想创建新模型:
var model = new app.MyModel({ test: "data", /* notice no id passed */ });
// POST request to
// https://api.xxxxxx.com/v12_1/item/?appId=xxxx&appKey=yyyy
model.save();
// or, equivalent using a collection:
collection.create({ test: "data", /* notice no id passed */ });
.on
/ .bind
支持.listenTo
在Backbone上传递事件绑定的上下文非常重要,因为大多数部分是类而不是jQuery回调,它们通常是处理局部变量的匿名函数。除此之外,您还应使用Backbone's listenTo
代替on
。
listenTo
是更新更好的选择,因为这些听众会 在被叫stopListening
期间自动删除 删除视图时(通过remove()
)。在listenTo
之前有一个 真正阴险的问题与幽灵般的观点永远存在 (泄漏记忆并造成不良行为)......
在视图中,您应该使用events
property自动将DOM事件委托给视图的回调。它仍然是jQuery的背景,但更干净,已经集成到Backbone中,并且上下文会自动传递,因此不需要使用var self = this
技巧。
app.MyModel2View = Backbone.View.extend({
events: {
"click .add-myModel": "onAddModelClick",
},
onAddModelClick: function() {
this.model.myModels.add({});
},
// ...some code...
});
除非将id传递给模型,否则从Backbone设计创建新模型并获取它是没有意义的。只需使用空对象调用add
on the collection即可创建默认模型。