When I manually define my collection content, everything works. But after I try to fetch data from server(I get valid response), my View is not showing. Collection View (ul) is showing, but single views (li) are not ? Is there something I need to watch when fetching data to collection ?
UPDATE
main.js
require.config({
paths: {
"jquery":"vendor/jquery/jquery",
"underscore":"vendor/underscore-amd/underscore",
"backbone":"vendor/backbone-amd/backbone",
"text":"../vendor/requirejs/text",
"mustache": "../vendor/mustache/mustache"
},
shim: {
underscore: {
exports: "_"
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require([
'app',
], function(App){
// The "app" dependency is passed in as "App"
App.initialize();
});
app.js
define([
'jquery',
'underscore',
'backbone',
'collections/ObjaveCollection',
'views/ObjaveView'
], function($, _, Backbone,ObjaveCollection,ObjaveView){
var initialize = function(){
var objave = new ObjaveCollection();
objave.fetch();
var objaveView = new ObjaveView({ collection: objave});
$(document.body).append(objaveView.render().el);
}
return {
initialize : initialize
};
});
ObjavaModel.js
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var ObjavaModel = Backbone.Model.extend({
defaults:{
naslov: 'My service',
cijena: 100,
}
});
return ObjavaModel;
});
ObjaveCollection.js
define([
'jquery',
'underscore',
'backbone',
'models/ObjavaModel'
], function($, _,Backbone,ObjavaModel){
var ObjaveCollection = Backbone.Collection.extend({
model: ObjavaModel
});
return ObjaveCollection;
});
ObjavaView.js
define([
'jquery',
'underscore',
'backbone',
'text!templates/objavaTemplate.html',
'mustache',
], function($, _, Backbone,objavaTemplate,Mustache){
var ObjavaView = Backbone.View.extend({
tagName: "li",
initialize: function () {
this.template = objavaTemplate;
},
render: function() {
rendered = Mustache.to_html(this.template, this.model.toJSON());
$(this.el).html(rendered);
return this;
}
});
return ObjavaView;
});
ObjaveView.js
define([
'jquery',
'underscore',
'backbone',
'views/ObjavaView'
], function($, _, Backbone,ObjavaView){
var ObjaveView = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each(function(item) {
console.log(item);
var objavaView = new ObjavaView({ model: item });
console.log(this.$el);
this.$el.append(objavaView.render().el);
}, this);
return this;
}
});
return ObjaveView;
});
Are you rerendering your view after the data has been fetched? For example, in your view (ObjaveView.js) add:
initialize: function () {
this.listenTo(this.collection, "sync", this.render);
}
Related
I scaffolded a backbone app with Yeoman. I used the standard template .ejs. There is something strange happening. If I take an element form my HTML it will update to the standard .ejs `your content here
When I edit my template however and reload, the new content will not be shown, and the old content is showed:
<p>your content here </p>
My code:
main.js
/*global require*/
'use strict';
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
bootstrap: {
deps: ['jquery'],
exports: 'jquery'
}
},
paths: {
jquery: '../bower_components/jquery/dist/jquery',
backbone: '../bower_components/backbone/backbone',
underscore: '../bower_components/underscore/underscore',
bootstrap: '../bower_components/sass-bootstrap/dist/js/bootstrap'
}
});
require([
'backbone',
'routes/app'
], function (Backbone, AppRouter) {
var app = new AppRouter();
Backbone.history.start();
});
AppRouter (App.js);
/*global define*/
define([
'jquery',
'backbone',
'views/wine',
'views/headerView'
], function ($, Backbone, WineView, HeaderView) {
'use strict';
var AppRouter = Backbone.Router.extend({
routes: {
'' : 'doSomething'
},
doSomething: function() {
console.log('doSomething init');
var headerView = new HeaderView();
}
});
return AppRouter;
});
And the HeaderView:
/*global define*/
define([
'jquery',
'underscore',
'backbone',
'templates'
], function ($, _, Backbone, JST) {
'use strict';
var HeaderViewView = Backbone.View.extend({
template: JST['app/scripts/templates/HeaderView.ejs'],
tagName: 'div',
id: '',
className: '',
events: {
'click .new' : 'addNew'
},
initialize: function () {
this.render();
},
render: function () {
$('#header').append(this.template);
}
});
return HeaderViewView;
});
HeaderView.ejs
<h1>New Text</h1>
Output website:
your content here
I have a grid linked to a store with autoLoad: true. The problem is that the store gets loaded on application launch, even if the view is created only later when accessed through a menu.
I have referenced the store in Application.js and in the view, but I'm not instatiating explicitly neither the store nor the view.
I don't know how to achieve that the store is loaded only when needed by the view.
If I set autoLoad: true, the store gets loaded on application launch.
If I set autoLoad: false, the store doesn't get loaded at all.
I know this is pretty basic, but I'm stuck so far.
Here is all the relevant code for reference:
app/store/Owners.js
Ext.define('Mb.store.Owners', {
extend: 'Ext.data.Store',
model: 'Mb.model.Owner',
autoLoad: true,
proxy: {
...
});
Application.js
Ext.define('Mb.Application', {
name: 'Mb',
extend: 'Ext.app.Application',
models: [
'Owner'
],
stores: [
'Owners'
],
...
app/view/Owners.js
Ext.define('Mb.view.winbiz.Owners', {
extend: 'Ext.grid.Panel',
alias: 'widget.test-gridPanel',
store: 'winbiz.Owners',
columns: [{
...
The view is instantiated in the controller:
Ext.define('Mb.controller.Winbiz', {
extend: 'Ext.app.Controller',
views: [
'Owners'
],
init: function(){
this.control({
'menu #test': {click: this.onMenuTest},
})
},
onMenuTest: function(){
this.getController('Main').addToMainTab('test-gridPanel');
},
You can add render handler to view which will call store load method and disable autoLoad.
Example listener:
Ext.define('Mb.view.winbiz.Owners', {
extend: 'Ext.grid.Panel',
[...],
initComponent: function(){
this.callParent();
this.on('render', this.loadStore, this);
},
loadStore: function() {
this.getStore().load();
}
});
I would let the view's controller handle the store load.
Start by disabling autoload on the store.
Ext.define('Mb.controller.Winbiz', {
extend: 'Ext.app.Controller',
views: [
'Owners'
],
ownerStore: null,
init: function(){
this.control({
'menu #test': {click: this.onMenuTest},
});
this.ownerStore = Ext.getStore('winbiz.Owners');
},
onMenuTest: function() {
if (this.ownerStore.loaded === false) {
this.ownerStore.load(
scope: this,
callback: this.onOwnerStoreLoaded
);
}
else {
this.addToTab();
}
},
onOwnerStoreLoaded: function (store, records, successful, eOpts) {
if (successful) {
store.loaded = true;
this.addToTab();
}
},
addToTab: function () {
this.getController('Main').addToMainTab('test-gridPanel');
}
Wheter you want to change the view before or after the store is loaded is another question.
This is my final controller code:
Ext.define('Mb.controller.Winbiz', {
extend: 'Ext.app.Controller',
views: [
'Owners'
],
refs: [{ref: 'testGrid', selector: 'test-gridPanel'}],
init: function(){
this.listen({
store: {
'#Owners':{ load: this.onOwnersLoad}
}
})
this.control({
'menu #test': {click: this.onMenuTest},
'test-gridPanel': {render: this.onOwnersRender}
})
},
onMenuTest: function(){
this.getController('Main').addToMainTab('test-gridPanel');
},
onOwnersLoad: function(store){
store.loaded = true
},
onOwnersRender: function(){
var store = this.getTestGrid().getStore();
if(!store.loaded)store.load();
},
It puts all code into the controller as suggested by #pcguru and uses the render event to shorten the code as suggested by #Lolo. Thanks
requirejs configuration:
require.config({
baseUrl: '/js/',
paths: {
jquery: './libs/jquery/jquery-1.10.1.min',
underscore: './libs/underscore/underscore-min',
backbone: './libs/backbone/backbone-min',
handlebars: './libs/handlebars/handlebars',
templates: '/templates'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
}
}
});
View:
define([
'backbone',
'handlebars',
'text!templates/mytemplate.html'
], function(Backbone, Handlebars, Template){
MyView = Backbone.View.extend({
tagName: 'li',
template: Handlebars.compile(Template),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
return MyView;
});
I encountered the following error:
Uncaught TypeError: Cannot call method 'compile' of undefined.
add this to your shim configuration:
shim: {
handlebars: {
exports: 'Handlebars'
},
I wrote two grid classes.
BaseUsuarios.js:
Ext.define('CRUDManantiales.view.grid.BaseUsuarios', {
extend: 'Ext.grid.Panel',
alias: 'widget.gridBaseUsuarios',
title: 'Usuarios',
initComponent: function(){
...
this.callParent(arguments);
}
});
And FullUsuarios.js
Ext.define('CRUDManantiales.view.grid.FullUsuarios', {
extend: 'CRUDManantiales.view.grid.BaseUsuarios',
alias: 'widget.gridFullUsuarios',
title: 'Usuarios',
initComponent: function(){
...
this.callParent(arguments);
}
BaseUsuarios.js have a toolbar with 3 buttons, then FullUsuarios.js also. But i want add
some buttons in FullUsuarios.js toolbar.
How i can do ?.
Any idea ?.
Thanks !
I would do something like this:
Ext.require('*');
Ext.onReady(function() {
Ext.define('MyBase', {
extend: 'Ext.panel.Panel',
initComponent: function() {
this.tbar = this.getTbarItems();
this.callParent();
},
getTbarItems: function() {
return [{
text: 'Base'
}]
}
});
Ext.define('MySub', {
extend: 'MyBase',
getTbarItems: function() {
var items = this.callParent();
items.push({
text: 'Sub'
});
// Could also insert etc
return items;
}
});
new MySub({
renderTo: document.body
});
});
I have a RelationalModel "Todo" which "HasMany" comments (nested array). How do I remove a particular comment from this nested collection? It would be great if someone could provide also examples how to add (and update) a "comment" to this nested array. Is that or that the best way to save the entire collection in localstorage? Why is there no functionality out of the box to remove an element from a nested collection and save it?
I tried
this.model.destroy()
and
this.model.bind("destroy", this.remove)
inside my CommentView but this only removes the comment from the DOM and from the Backbone "CommentCollection", but not from the localstorage. So, somehow it doesn't sync the CommentCollection and localstorage.
Todos in localstorage look like this:
{"todo_1342290161303":{"content":"Hello Todo1","comments":[
{"id":"1","content":"Comment1","todo":"todo_1342290161303"},
{"id":"2","content":"Comment2","todo":"todo_1342290161303"}],"id":"todo_1342290161303"}
}
//-------------------- Comment MODEL ----------------
var Comment = Backbone.RelationalModel.extend({
idAttribute: "_id",
initialize: function() {
console.log("COMMENT MODEL: initialize()");
},
});
//-------------------- Comment Collection ----------------
var CommentCollection = Backbone.Collection.extend({
model: Comment,
localStorage: new Store("Todos-STORAGE-COMMENTS"),
initialize: function() {
console.log("COMMENT COLLECTION: initialize()");
//console.log(this.collection.get(1).get("content"));
}
});
//-------------------- Todo MODEL ----------------
var Todo = Backbone.RelationalModel.extend({
idAttribute: "id",
relations: [{
type: Backbone.HasMany,
key: "comments",
relatedModel: Comment,
collectionType: CommentCollection,
reverseRelation: {
key: "todo",
includeInJSON: "id",
},}],
initialize: function() {
console.log("--TODO MODEL: initialize()");
},
});
The CommentView:
var CommentView = Backbone.View.extend({
tagName: "li",
template: _.template($("#comment-template").html()),
events: {
"click span.delete-comment": "deleteComment"
},
initialize: function() {
_.bindAll(this, "render", "remove", "deleteComment");
this.model.bind("change", this.render);
this.model.bind("destroy", this.remove);
},
deleteComment: function(comment) {
this.model.destroy();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
});