I'm picking up backbone for the first time and I'm having some trouble getting my view to render my collection.
main.js
/*global require*/
'use strict';
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
},
paths: {
app: 'app',
jquery: '../components/jquery/jquery',
backbone: '../components/backbone-amd/backbone',
underscore: '../components/underscore-amd/underscore',
competitions: 'collections/competition-collection',
competitionModel: 'models/Competition-model',
templates: 'templates'
}
});
require([
'backbone',
'app',
'competitions',
'competitionModel',
'views/competition-view',
'templates'
], function (
Backbone,
App,
Competitions,
CompetitionModel,
CompetitionsView
) {
window._app = new App(
Competitions,
CompetitionModel,
CompetitionsView
);
window._app.demoData();
window._app.start();
});
app.js
define([], function() {
var App = function(Competitions,CompetitionModel,CompetitionsView) {
// Our models will be instantiated later as needed later.
this.Models.CompetitionModel = CompetitionModel;
this.Collections.Competitions = Competitions;
this.Collections.competitions = new Competitions();
this.Views.competitionsView = new CompetitionsView();
//console.log(this.Views.competitionsView)
};
App.prototype = {
Views: {},
Models: {},
Collections: {},
start: function() {
this.Views.competitionsView.render();
Backbone.history.start();
},
// TODO: We'll get rid of this or move later ... just "spiking" ;)
demoData: function() {
var me = new this.Collections.Competitions(
[
{
name: 'Some Name',
},
{
name: 'Other Name',
}
]
);
console.log("***** Demo Competitions Created *****");
}
};
return App;
});
Competition-model.js
define([
'underscore',
'backbone',
], function (_, Backbone) {
'use strict';
var CompetitionModel = Backbone.Model.extend({
defaults: {
},
initialize: function(){
console.log(this.attributes);
}
});
this.listenTo(Competitions, 'add', function(){
console.log("bla")
});
return CompetitionModel;
});
competition-collection.js
define([
'underscore',
'backbone',
'models/competition-model'
], function (_, Backbone, CompetitionModel) {
'use strict';
var CompetitionCollection = Backbone.Collection.extend({
model: CompetitionModel
});
return CompetitionCollection;
});
competition-view.js
define([
'jquery',
'underscore',
'backbone',
'templates',
'competitions',
], function ($, _, Backbone, JST, Competitions) {
'use strict';
var CompetitionView = Backbone.View.extend({
template: JST['app/scripts/templates/competition.ejs'],
render: function() {
console.log(this.model);
}
});
console.log("yo")
return CompetitionView;
});
I know that the models are loaded correctly but I can't seem to figure out how to pass the model collection to the view and render all the objects.
Can anyone help?
Thanks
You have created the view, collection and models properly but haven't created a link between collection and view. You need to pass the collection to the view and use that collection in the view to render all models.
In your app.js replace:
this.Views.competitionsView = new CompetitionsView();
With:
this.Views.competitionsView = new CompetitionsView({collection: this.Collections.competitions});
Now you have a reference of collections object in your view. Now inside competition-view.js replace:
console.log(this.model);
With:
this.collection.each(function (model) {
console.log(model);
});
Also in your app.js, inside start function, you are calling Backbone.history.start() without creating a Backbone router, which is also giving a console error.
Related
I'm having troubles handling events between my views and collections. In the below example you can find a short version of what does my webapp look like and how are the events being handled now.
What happens here is that when switching from menu1 to menu2 or even when going backwards, it causes that the "APP:change_city" event listener is stacked up. So when I then trigger this event , it calls the method OnCityChange() as many times as I switched between the menus.
I'm now not really sure whether I'm using the event aggregator (eMgr) correctly.
Can anyone please assist?
eMgr.js
define(['backbone.wreqr'],function(Wreqr){
"use strict";
return new Wreqr.EventAggregator();
})
AppRouter.js
define(['marionette'], function (Marionette) {
"use strict";
var AppRouter = Marionette.AppRouter.extend({
appRoutes: {
'menu1' : 'showMenu1',
'menu1' : 'showMenu2'
}
});
return AppRouter;
});
AppControler.js
define(['underscore', 'backbone', 'marionette', '../eMgr'], function (_, Backbone, Marionette, eMgr) {
"use strict";
var Controller = Marionette.Controller.extend({
initialize: function(){
console.log("AppRouter - Init")
},
showMenu1: function (city) {
console.log(" [Info] [AppControler] opening Menu1");
eMgr.trigger("APP:open_menu", { menu: "Menu1", city: city});
},
showMenu2: function (city) {
console.log(" [Info] [AppControler] opening Menu2");
eMgr.trigger("APP:open_menu", { menu: "Menu2", city: city});
}
});
return Controller;
});
App.js
define([ 'backbone', 'underscore', 'marionette', 'eMgr',
'layouts/MainMenu/layoutview.menu1',
'layouts/MainMenu/layoutview.menu2',
'controllers/AppController', 'routers/AppRouter'],
function (Backbone, _, Marionette, eMgr,
lv_mainmenu1, lv_mainmenu2,
AppController, AppRouter) {
"use strict";
var MyApp = new Marionette.Application();
var controller = new AppController();
MyApp.addRegions({
.....
mainmenu: '#main_menu',
.....
});
MyApp.listenTo(eMgr, "menu_changed",function(eData){
switch(eData.menu){
case "Menu1":
MyApp.mainmenu.show(new lv_mainmenu1(eData));
break;
case "Menu2":
MyApp.mainmenu.show(new lv_mainmenu2(eData));
break;
}
});
MyApp.addInitializer(function(options) {
var router = new AppRouter({
controller : controller
});
});
MyApp.on("start", function(){
if (Backbone.history){
Backbone.history.start();
}
});
$(document).ready(function() {
MyApp.start();
});
});
layoutview.menu1.js
define([ 'backbone', 'underscore', 'marionette',
'templates/template.mainmenu',
'layouts/MainMenu/collectionview.categories'],
function (Backbone, _, Marionette, t_Menus, c_Categories, cv_Categories) {
"use strict";
var Menu1LayoutView = Marionette.LayoutView.extend({
template: t_Menus['menu1'],
regions: {
menu : '#menu'
},
initialize: function(options){
this.city = options.city
},
onRender: function(){
},
onShow: function(){
this.menu.show(new cv_Categories({city:this.city}));
}
});
return Menu1LayoutView;
});
collectionview.categories.js
define([ 'backbone', 'underscore', 'marionette',
'layouts/MainMenu/compositeview.subcategories',
'collections/MainMenu/MM.collection.categories'],
function (Backbone, _, Marionette, cv_Subcategories, c_Categories) {
"use strict";
var CategoriesCollectionView = Marionette.CollectionView.extend({
initialize: function(options){
this.collection = new c_Categories([], {city: options.city});
},
getChildView: function(model){
return cv_Subcategories;
},
onRender: function(){
},
onShow: function(){
}
});
return CategoriesCollectionView;
});
This is where all the categorie's data are fetched from , it also re-fetches the data once the APP:change_city event is being triggered.
MM.collection.categories.js
define([ 'underscore', 'backbone', 'eMgr','models/MainMenu/MM.model.category'], function(_, Backbone, eMgr, m_Category){
var CategoriesCollection = Backbone.Collection.extend({
model: m_Category,
initialize: function(attr, opts) {
this.city = opts.city;
this.fetch();
eMgr.once("APP:change_city", this.OnCityChange, this)
},
url: function(){
return 'AF_GetCategories?city='+this.city;
},
OnCityChange: function(eData){
/// this is the part which is being called multiple times !!!!! ////
/// when checking eMgr's events , it shows that the events are stacking up ..
this.url= 'AF_GetCategories?city='+eData.city;
this.fetch();
}
});
return CategoriesCollection;
});
compositeview.subcategories.js
define([ 'backbone', 'underscore', 'marionette',
'templates/template.mainmenu',
'layouts/MainMenu/itemview.subcategory'],
function (Backbone, _, Marionette, t_MainMenu, iv_Subcategory) {
"use strict";
var SubcategoriesCompositeView = Marionette.CompositeView.extend({
template: t_Menus['subcategorieslayout'],
childViewContainer: "#category-wrapper",
getChildView: function(model){
return iv_Subcategory;
},
initialize: function(){
this.collection = this.model.get("subcategories");
},
onRender: function(){
},
onShow: function(){
this.$el.find("#loading").fadeOut(150);
}
});
return SubcategoriesCompositeView;
});
I have come to a conclusion that keeping the event listeners this.listenTo("custom_event", this.do_something) in either model or collection isn't a good idea. The events weren't cleaned up properly while switching between menu1 and menu2. It only worked when I manually called eMgr.stopListening() before loading any views.
So I tried moving all the event listeners from models/collections to their views and Voila!...it all worked! Events are no longer being triggered multiple times as before.
I try to make my router decide to show a login only home view, otherwise the login view or registerview.
my app is at initially localhost:8080/indexapp.
I load it at the data-main file. but the route tied to action that makes this decision is not triggered I think.
Is it possible to have a router with a route to be triggered on the page loaded.
Also, I have a commented one line that says this.loginModel.fetch(); which causes undefined is not a function error.
define([
'jquery',
'ratchet',
'underscore',
'backbone',
'login/loginview',
'login/loginmodel',
'register/registerview',
'home/homeview',
],
function($, Ratchet, _, Backbone, LoginModel, LoginView, RegisterView, HomeView){
var MainRouter = Backbone.Router.extend({
routes: {
"": "showHome",
//"/": "showHome",
"login": "showLogin",
"register": "showRegister"
},
initialize: function(){
Backbone.history.navigate("home", {trigger:true})
},
showHome: function(){
//var self = this, loginModel = new LoginModel();
this.loginModel = new LoginModel();
//this.loginModel.fetch();
if(this.loginModel.get('loginp') == true ){
this.homeView = new HomeView();
this.homeView.render();
}
else {
Backbone.history.navigate("/login", {trigger:true})
}
/*
this.loginModel.fetch({
success: function(model){
if( model.get('loginp') == true ){ //show you app view for logged in users!
this.homeView = new HomeView();
this.homeView.render();
}
else { //not logged in!
Backbone.history.navigate("/login", {trigger:true})
}
},
});
*/
},
showLogin: function(){ //display your login view
this.loginView = new LoginView;
//this.loginView.fetch();
this.loginView.render();
},
showRegister: function(){ //display your register view
this.registerView = new RegisterView;
//this.registerView.fetch();
this.registerView.render();
},
});
return MainRouter;
});
loginmodel:
define([
'underscore',
'backbone',
],
function(_, Backbone) {
var LoginModel = Backbone.Model.extend({
urlRoot: '/login',
initialize: function(){
this.fetch();
},
defaults: {
username: null,
},
});
return LoginModel;
});
loginview:
define([
'jquery',
'ratchet',
'underscore',
'backbone',
'login/loginmodel',
'text!login/logintemplate.html',
],
function($, Ratchet, _, Backbone, LoginModel, LoginTemplate){
var LoginView = Backbone.View.extend({
el: $('body'),
model: new LoginModel,
/*
initialize: function(){
this.model = new LoginModel;
},
*/
template: _.template( LoginTemplate ),
render: function(){ //display your login view
this.$el.html( template( this.model.attributes ) );
},
});
return LoginView;
});
Glad to see you're following my idea! ;)
The only thing you're missing is to start up routing using Backbone.history.start() on $(document).ready.
http://backbonejs.org/#Router
It works this way: you need to instantiate your router(s) before calling Backbone.history.start().
So in your main app file, require your MainRouter, create one instance and start history.
$(document).ready(function(){
new MainRouter();
Backbone.history.start();
});
So I'm building a mobile website and I have a directory called 'api' with various php files hat echo JSON formatted data from a remote API. I did this to avoid the cross-domain issue.
But one of the php files needs a GET parameter (i.e. id) so that I can echo the JSON data for a specific object based on it's id.
My collection will need to do this (assuming this will work):
define([
'backbone',
'models/tournaments/single'
], function(Backbone, singleModel) {
var TournamentCollection = Backbone.Collection.extend({
model: singleModel,
url: '/api/tournament.php?id=' + id,
parse: function(response) {
return response;
}
});
return TournamentCollection;
});
I have this in my router, but how do I pass the 'id' value to the view or collection:
define([
'jquery',
'underscore',
'backbone',
'views/home',
'views/tournament'
], function($, _, Backbone, HomeView, TournamentView) {
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'tournament/:id': 'tournament'
}
});
var initialize = function() {
var app_router = new AppRouter;
app_router.on('route:home', function() {
var homeView = new HomeView();
});
app_router.on('route:tournament', function(id) {
var tournamentView = new TournamentView({id: id});
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
Couple of things:
1) Your definition of the url property of the collection will not work as id is likely not defined when defining the TournamentCollection class. You can use a function rather than a property. TournamentCollection will become something like this:
define([
'backbone',
'models/tournaments/single'
], function(Backbone, singleModel) {
var TournamentCollection = Backbone.Collection.extend({
model: singleModel,
initialize: function (options) {
this.id = options.id;
},
url: function () {
return '/api/tournament.php?id=' + this.id
},
parse: function(response) {
return response;
}
});
return TournamentCollection;
});
This way you can initialize the object with an id, and later, when the url is fetched it will include the correct id.
2) I would probably initialize and fetch the collection from the router. Then from the initialize of the view, listen for that fetch to complete and ultimately re-render the view. Something like this:
define([
'jquery',
'underscore',
'backbone',
'views/home',
'views/tournament'
], function($, _, Backbone, HomeView, TournamentView) {
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'tournament/:id': 'tournament'
}
});
var initialize = function() {
var app_router = new AppRouter;
app_router.on('route:home', function() {
var homeView = new HomeView();
});
app_router.on('route:tournament', function(id) {
var tournaments = new TournamentCollection({ id: id });
tournaments.fetch();
var tournamentView = new TournamentView({ collection: tournaments });
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
// Tournament View define stuff
var TournamentView = Backbone.View.extend({
initialize: function () {
this.listenTo(this.collection, 'sync', this.render);
},
render: function () {
//...
}
});
return TournamentView
hope that helps. :)
I am using jquery, backbonejs, underscorejs and bootstrap 3 for my project (enter link description here). This is my source code https://github.com/datomnurdin/izify-template.
My question is, how to combine 3 models into 1 collections in OrderCollections.
CategoryModel.js
define([
'underscore',
'backbone',
], function(_, Backbone) {
var CategoryModel = Backbone.Model.extend({});
return CategoryModel;
});
MerchantModel.js
define([
'underscore',
'backbone',
], function(_, Backbone) {
var MerchantModel = Backbone.Model.extend({});
return MerchantModel;
});
ProductModel.js
define([
'underscore',
'backbone',
], function(_, Backbone) {
var ProductModel = Backbone.Model.extend({});
return ProductModel;
});
OrderCollection.js
define([
'underscore',
'backbone',
'models/category/CategoryModel',
'models/merchant/MerchantModel',
'models/product/ProductModel'
], function(_, Backbone, CategoryModel, MerchantModel, ProductModel){
//stuck here
var OrderCollection = Backbone.Collection.extend({
model: CategoryModel,
url:"https://izify.com/api/izify-api/user/get_all_categories.php",
parse: function(data) {
return data.tbl_categories;
},
});
return OrderCollection;
});
And then, how to retrieve all data into one html page?
OrderView.js
define(['jquery', 'underscore', 'backbone', 'models/global/GlobalModel', 'collections/order/OrderCollection', 'views/header/HeaderView', 'views/sidebar/SidebarView', 'views/footer/FooterView', 'text!templates/category/orderTemplate.html'], function($, _, Backbone, GlobalModel, OrderCollection,HeaderView,SidebarView,FooterView, orderTemplate) {
var OrderView = Backbone.View.extend({
el: $("#page"),
initialize: function() {
this.$el.off();
},
render: function(productId) {
var that = this;
var global = new GlobalModel();
this.collection = new OrderCollection();
var formValues = {
merchantId: global.merchantId,
productId: productId
};
this.collection.fetch({
data: formValues,
success: function(collection, response) {
var template = _.template(orderTemplate, {
orders: that.collection.models
});
that.$el.html(template);
var sidebarView = new SidebarView();
sidebarView.render();
},
error: function(collection, response) {
console.log("error");
}
});
},
});
return OrderView;
});
Demo: http://staging.revivalx.com/izify-template/
Resource: https://github.com/datomnurdin/izify-template
Thanks a lot in advance.
The magic of creating a collection of different models is by implementing a model function.
here is the examples from http://backbonejs.org/#Collection-model
var Library = Backbone.Collection.extend({
model: function(attrs, options) {
if (condition) {
return new PublicDocument(attrs, options);
} else {
return new PrivateDocument(attrs, options);
}
}
});
I'm using backbone.marionette for view control.
My issue is "How do you pass a parameter to a model?"
This is what I have tried:
define([
'jquery',
'underscore',
'backbone',
'models/CampaginModel',
'collections/CampaignCollection',
'text!templates/includes/_campaign.html'
], function ($, _, Backbone, CampaginModel, CampaignCollection, campaignTemplate) {
var campaginView = Backbone.Marionette.ItemView.extend({
template: campaignTemplate,
initialize: function (options) {
this.campaign_id = options.id;
},
model: CampaginModel({id: this.campaign_id}),
onRender: function () {
}
}); // end campagin view
return campaginView;
});
I have noticed that my parameter get passed to the view init function I'm kinda stuck after this point. In standard backbone I just created a new model in the render function and passed the parameter to the model that way. However Marionette views have a 'model' attribute which I think should allow me to pass in it there, but it does not!
Model:
define([
'underscore',
'backbone',
'jquery'
], function (_, Backbone, jquery) {
var CampaginModel = Backbone.Model.extend({
urlRoot: '/api/v1/campaign/',
// Model Constructor
initialize: function () {
},
});
return CampaginModel;
});
I don't know what your file structure looks like.
But it should be like something like this.
define([
'jquery',
'underscore',
'backbone',
'models/CampaginModel',
'collections/CampaignCollection',
'text!templates/includes/_campaign.html'
], function ($, _, Backbone, CampaginModel, CampaignCollection, campaignTemplate) {
var campaginView = Backbone.Marionette.ItemView.extend({
template: campaignTemplate,
initialize: function (options) {
this.campaign_id = options.id;
this.model.set({id: this.campaign_id});
},
model: CampaginModel,
onRender: function () {
}
}); // end campagin view
return campaginView;
});
I haven't test the code yet.
If you need to set your parameters to model, you have to use backbone's model.set() function