Here are my code:
Model:
var app = app || {};
app.Film = Backbone.Model.extend({
defaults: {
poster: 'http://placehold.it/320x150',
title: 'No name',
genre: 'Unknown',
runtime: 'Unknown',
imdbRating: 0
},
parse: function( response ) {
response.id = response._id;
return response;
}
});
Collection:
var app = app || {};
app.Films = Backbone.Collection.extend({
model: app.Film,
url: '/api/films'
});
Views:
Model:
var app = app || {};
app.FilmView = Backbone.View.extend({
tagName: 'div',
className: 'filmContainer',
events: {
},
initialize : function() {
this.template= _.template( $('#filmTemplate').html() );
},
render: function() {
this.$el.html( this.template( this.model.toJSON() ));
return this;
}
});
Collection:
ar app = app || {};
var global = 'global';
app.FilmsView = Backbone.View.extend({
el: '#films',
events:{
},
initialize: function() {
this.collection = new app.Films();
this.collection.fetch();
this.render();
this.listenTo( this.collection, 'add', this.renderFilm );
this.listenTo( this.collection, 'reset', this.render );
this.listenTo( Backbone.Events, 'findFilm', this.findFilm );
},
render: function() {
this.collection.each(function( item ) {
this.renderFilm( item );
}, this );
console.log('render');
},
renderFilm: function( item ) {
var filmView = new app.FilmView({
model: item
})
this.$el.append( filmView.render().el );
console.log('renderFilm');
},
findFilm: function () {
console.log('findFilm');
$.ajax({
type: "POST",
url: "/film/find",
data: {
name: "Gotham"
},
success: function(data){
app.FilmsView.collection = new app.Films();
app.FilmsView.collection.add (data[0]);
console.log(app.FilmsView.collection)
}
});
}
});
Idea of my problem is - I must get string from field and use it for searching of the film in DB. "/film/find" request response me with object which include fields of the model. I need some way to display this model in page. As I use fetch() method when initialize: page display all DB models. So I need clear collection and display only 1 model which I get from the server. this.collecion.reset() don't work and don't trigger render() event.
Indetesting think: app.FilmsView.collection returnt "undefined" after rendering, so I need to create new collection for AJAX response.
Try to change this string in itnitialize():
this.listenTo( Backbone.Events, 'findFilm', this.findFilm );
to
this.listenTo( Backbone.Events, 'findFilm', this.findFilm, this );
And then in findFilm:
success: function(data){
this.collection.reset();
this.collection.add (data[0]);
}.bind(this);
Related
In my Marionette app, I have a Collection view, with a childView for it's models.
The collection assigned to the CollectionView is a PageableCollection from Backbone.paginator. The mode is set to infinite.
When requesting the next page like so getNextPage(), the collection is fetching data and assigning the response to the collection, overwriting the old entries, though the full version is store in collection.fullCollection. This is where I can find all entries that the CollectionView needs to render.
Marionette is being smart about collection events and will render a new childView with it's new model when a model is being added to the collection. It will also remove a childView when it's model was removed.
However, that's not quite what I want to do in this case since the collection doesn't represent my desired rendered list, collection.fullCollection is what I want to show on page.
Is there a way for my Marionette view to consider collection.fullCollection instead of collection, or is there a more appropriate pagination framework for Marionette?
Here's a fiddle with the code
For those who don't like fiddle:
App = Mn.Application.extend({});
// APP
App = new App({
start: function() {
App.routr = new App.Routr();
Backbone.history.start();
}
});
// REGION
App.Rm = new Mn.RegionManager({
regions: {
main: 'main',
buttonRegion: '.button-region'
}
});
// MODEL
App.Model = {};
App.Model.GeneralModel = Backbone.Model.extend({});
// COLLECTION
App.Collection = {};
App.Collection.All = Backbone.PageableCollection.extend({
model: App.Model.GeneralModel,
getOpts: function() {
return {
type: 'POST',
contentType: 'appplication/json',
dataType: 'json',
data: {skip: 12},
add: true
}
},
initialize: function() {
this.listenTo(Backbone.Events, 'load', (function() {
console.log('Load more entries');
// {remove: false} doesn't seem to affect the collection with Marionette
this.getNextPage();
})).bind(this)
},
mode: "infinite",
url: "https://api.github.com/repos/jashkenas/backbone/issues?state=closed",
state: {
pageSize: 5,
firstPage: 1
},
queryParams: {
page: null,
per_page: null,
totalPages: null,
totalRecords: null,
sortKey: null,
order: null
},
/*
// Enabling this will mean parseLinks don't run.
sync: function(method, model, options) {
console.log('sync');
options.contentType = 'application/json'
options.dataType = 'json'
Backbone.sync(method, model, options);
}
*/
});
// VIEWS
App.View = {};
App.View.MyItemView = Mn.ItemView.extend({
template: '#item-view'
});
App.View.Button = Mn.ItemView.extend({
template: '#button',
events: {
'click .btn': 'loadMore'
},
loadMore: function() {
Backbone.Events.trigger('load');
}
});
App.View.MyColView = Mn.CollectionView.extend({
initialize: function() {
this.listenTo(this.collection.fullCollection, "add", this.newContent);
this.collection.getFirstPage();
},
newContent: function(model, col, req) {
console.log('FullCollection length:', this.collection.fullCollection.length, 'Collection length', this.collection.length)
},
childView: App.View.MyItemView
});
// CONTROLLER
App.Ctrl = {
index: function() {
var col = new App.Collection.All();
var btn = new App.View.Button();
var colView = new App.View.MyColView({
collection: col
});
App.Rm.get('main').show(colView);
App.Rm.get('buttonRegion').show(btn);
}
};
// ROUTER
App.Routr = Mn.AppRouter.extend({
controller: App.Ctrl,
appRoutes: {
'*path': 'index'
}
});
App.start();
You could base the CollectionView off the full collection, and pass in the paged collection as a separate option:
App.View.MyColView = Mn.CollectionView.extend({
initialize: function(options) {
this.pagedCollection = options.pagedCollection;
this.pagedCollection.getFirstPage();
this.listenTo(this.collection, "add", this.newContent);
},
// ...
}
// Create the view
var colView = new App.View.MyColView({
collection: col.fullCollection,
pagedCollection: col
});
Forked fiddle
I have this bb app that I'm trying to search and return the results of the search, then when cleared, so all results again. I was able to get everything to show before adding the search feature, but now nothing showing up. I think the collection isn't available at the time it's trying to populate, but can't seem to get it to wait. I've tried moving the fetch around to no avail. Any help would be greatly appreciate. For the sake of ease, I've put everything in a fiddle that can be found here...
//Campaign Model w defaults
app.model.Campaign = Backbone.Model.extend({
default: {
title: '',
img: '',
id: '',
slug: '',
image_types: 'small',
tagline: ''
}
});
//Campaign Collection from model
app.collection.Campaign = Backbone.Collection.extend({
//our URL we're fetching from
url: 'https://api.indiegogo.com/1/campaigns.json?api_token=e377270bf1e9121da34cb6dff0e8af52a03296766a8e955c19f62f593651b346',
parse: function(response) {
console.log('parsing...');
return response.response; //get array from obj to add to collection based on model
},
currentStatus: function(status){
return _(this.filter(function(data){
console.log('currentStats', status);
return data.get('_pending') == status;
}));
},
search: function(searchVal) {
console.log('search...');
if (searchVal == '') {
return this;
}
var pattern = new RegExp(searchVal, 'gi');
return _(this.filter(function(data) {
return pattern.test(data.get('title'));
}));
}
});
app.collection.campaigns = new app.collection.Campaign();
app.collection.campaigns.fetch({
success: function(){
console.log('Success...');
var sHeight = window.screen.availHeight - 200 + 'px';
$('#container ul').css('height', sHeight);
},
error: function() {
console.log('error ',arguments);
}
});
//List view for all the campaigns
app.view.CampaignList = Backbone.View.extend({
events: {
'keyup #searchBox': 'search'
},
render: function(data) {
console.log('campaignList',$(this.el).html(this.template));
$(this.el).html(this.template);
return this;
},
renderAll: function(campaigns) {
console.log('campList renderAll', campaigns, $('#campaignList'));
$('#campaignList').html('');
campaigns.each(function(campaign){
var view = new app.view.CampaignItem({
model: campaign,
collection: this.collection
});
console.log(view);
$('#campaignList').append(view.render().el);
});
return this;
},
initialize: function() {
console.log('init campList',app);
this.template = _.template($('#campaignList-tmp').html());
this.collection.bind('reset', this.render, this);
},
search: function(e) {
console.log('listView search');
var searchVal = $('#searchBox').val();
this.renderAll(this.collection.search(searchVal));
},
sorts: function() {
var status = $('#campSorting').find('option:selected').val();
if(status == '') {
status = false;
};
this.renderAll(this.collection.currentStatus(status));
}
});
//Item view for single campaign
app.view.CampaignItem = Backbone.View.extend({
events: {},
render: function(data){
console.log('campItem render...', data);
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function(){
console.log('campItem init');
this.template = _.template( $('#campaignItem-tmp').html());
}
});
//Router
app.router.Campaign = Backbone.Router.extend({
routes: {
'': 'campaigns'
},
campaigns: function(){
this.campListView = new app.view.CampaignList({
collection: app.collection.campaigns
});
$('#container').append(this.campListView.render().el);
this.campListView.sorts();
}
});
app.router.campaigns = new app.router.Campaign();
Backbone.history.start();
http://jsfiddle.net/skipzero/xqvrpyx8/
I have the following router:
define([
'backbone.marionette',
'app',
'views/products/list',
'views/products/browsing_filter',
'views/products/detail',
'views/dashboard/index',
'views/layout'
],
function(Marionette, App, ProductListView, BrowsingFilterView, ProductDetailView, LayoutView){
var AppRouter = Backbone.Marionette.AppRouter.extend({
routes: {
'product/:id': 'showProduct',
'products/:id': 'showProduct',
'products': 'listProducts',
'*path': 'showDashboard',
},
listProducts: function(path) {
App.contentRegion.show(new ProductListView());
product_filter_view = new BrowsingFilterView();
},
showProduct: function(id) {
App.contentRegion.show(new ProductDetailView({id: id}));
},
showDashboard: function() {
return require(['views/dashboard/index', 'collections/newsfeed_items','models/newsfeed_item'], function(DashboardView, NewsfeedItemCollection, NewsfeedItem) {
App.contentRegion.show(new DashboardView({
collection: new NewsfeedItemCollection(),
model: new NewsfeedItem()
}));
});
}
});
return AppRouter;
});
When a route is called it works fine. However, when the next route is called the container for the region App.contentRegion is emptied and no new content is rendered.
When the new route is called, the AJAX requests are done as they should, the view simply seems to either become detached or not rendered at all.
What is wrong?
Edit:
ProductDetailView:
define([
'jquery',
'backbone',
'models/product',
'models/product_property_value',
'models/product_property',
'hbs!template/product_detail/detail',
'hbs!template/product_detail/edit_string',
'collections/product_property_values',
'collections/newsfeed_items',
'hbs!template/newsfeed/feed'
],
function($, Backbone, ProductModel, ProductPropertyValueModel, ProductPropertyModel, ProductDetailTemplate, StringEditTemplate, ProductPropertyValueCollection, NewsfeedItemCollection, FeedTemplate){
ProductDetailView = Backbone.View.extend({
el: '#product_detail',
product_id: null,
events: {
'click a.show_edit': 'triggerEdit',
// 'click div.edit_container a.save': 'saveChanges',
'submit form.edit_property_value': 'saveChanges',
'click a.cancel_edit': 'cancelEdit'
},
initialize: function(param){
this.product_id = param.id;
this.product = new ProductModel({'id': this.product_id});
this.product.fetch();
this.newsfeeditems = new NewsfeedItemCollection({'product': {'id': this.product_id}});
this.listenTo(this.newsfeeditems, 'change', this.renderFeed);
this.listenTo(this.newsfeeditems, 'fetch', this.renderFeed);
this.listenTo(this.newsfeeditems, 'sync', this.renderFeed);
this.newsfeeditems.setProductId(this.product_id);
this.newsfeeditems.fetch({reset:true});
this.listenTo(this.product, 'change', this.render);
this.listenTo(this.product, 'fetch', this.render);
this.listenTo(this.product, 'sync', this.render);
},
renderFeed: function(r) {
context = this.newsfeeditems.toJSON();
this.$el.find('#product_newsfeed').html(FeedTemplate({items:context}));
},
edit_container: null,
product_property_model: null,
triggerEdit: function(r) {
r.preventDefault();
this.cancelEdit();
editable_container = $(r.target).parents('.editable').first();
product_property_value_ids = editable_container.data('property-value-id');
edit_container = $(editable_container).find('div.edit_container');
if(edit_container.length === 0) {
console.log(edit_container);
editable_container.append('<div class="edit_container"></div>');
edit_container = $(editable_container).find('div.edit_container');
}
this.edit_container = edit_container;
value_init = [];
for(var i = 0; i < product_property_value_ids.length; i++) {
value_init = {'id': product_property_value_ids[i]};
}
if(product_property_value_ids.length > 1) {
throw new Exception('Not supported');
}
this.edit_value = new ProductPropertyValueModel({'id': product_property_value_ids[0]});
this.listenTo(this.edit_value, 'change', this.renderEditField);
this.listenTo(this.edit_value, 'reset', this.renderEditField);
this.listenTo(this.edit_value, 'fetch', this.renderEditField);
this.edit_value.fetch({'reset': true});
return false;
},
cancelEdit: function() {
this.$el.find('.edit_container').remove();
},
renderEditField: function() {
edit_container.html(StringEditTemplate(this.edit_value.toJSON()));
},
saveChanges: function(r) {
r.preventDefault();
console.log('save changes');
ev = this.edit_value;
_.each($(r.target).serializeArray(), function(value, key, list) {
ev.set(value, key);
});
ev.save();
},
render: function(r) {
context = this.product.toJSON();
this.$el.html(ProductDetailTemplate(context));
$(document).foundation();
return this;
}
});
return ProductDetailView;
});
In our app we use appRoutes instead of routes as the key. I think that is the way you should do it when using Marionette.
Next you should make sure you are starting Backbone.history by using Backbone.history.start().
i have a problem with backbone.js. I'm creating a frontend for an existing api, for me unaccessable. The problem is that when I try to add a new model to a collection, i can see in my firebug that every time backbone tries to create the model it appends the attribute name to the url.
Example:
default url = /api/database
when i perform a GET = /api/database
when i perform a GET/POST with object {"name": "test"} =
/api/database/test is the result
Anyone knows how to avoid that behaviour?
Greetings Kern
My View:
window.databaseView = Backbone.View.extend({
el: '#content',
template: new EJS({url: 'js/templates/databaseView.ejs'}),
initialize: function() {
var self = this;
this.collection.fetch({
success: function() {
console.log(self.collection);
var test = self.collection.get("_system");
console.log(test);
self.collection.get("_system").destroy();
self.collection.create({name: "test"});
}
});
},
render: function(){
$(this.el).html(this.template.render({}));
return this;
}
});
Model:
window.Database = Backbone.Model.extend({
initialize: function () {
'use strict';
},
idAttribute: "name",
defaults: {
}
});
Collection:
window.ArangoDatabase = Backbone.Collection.extend({
model: window.Database,
url: function() {
return '../../_api/database/';
},
parse: function(response) {
return _.map(response.result, function(v) {
return {name:v};
});
},
initialize: function() {
this.fetch();
},
getDatabases: function() {
this.fetch();
return this.models;
},
dropDatabase: function() {
},
createDatabse: function() {
}
});
By default, Backbone create models URLs this way: {collection url}/{model id}.
It consider the collection URL to be a base URL in a RESTful way.
Here you only want to set the Model url property to the URL you whish to call. That'll overwrite the default behavior. http://backbonejs.org/#Model-url
I have Backbone Model that collect data from server:
Job.Models.Response = Backbone.Model.extend({
defaults: {
'authStatus': false,
'id': '1',
'name': 'name',
},
urlRoot: '/static/js/public/json/'
});
I have button with data-id = "id from /static/js/public/json/".
Job.Views.Response = Backbone.View.extend({
el: '.ra-response-button',
events: {
"click": "load"
},
load: function () {
var info = this.$el.data();
this.model.set({ id: info.id});
this.model.fetch();
if (this.model.attributes.authStatus === false) {
console.log('Register')
}
else {
console.log('Unregister')
}
}
});
If i console.log my model after fetch, its dont update, but data fetch success.
What kind of problem can be here?
Here i init our plugin:
var responseModel = new Job.Models.Response;
var response = new Job.Views.Response({ model: responseModel });
I resolve my problem. Finally View.
Job.Views.Response = Backbone.View.extend({
el: '.ra-response-button',
events: {
"click": "load"
},
load: function () {
var that = this;
var info = that.$el.data();
that.model.set({ id: info.id});
that.model.fetch({
success: function() {
if (that.model.attributes.authStatus === true) {
new Job.Views.ResponseForm({ model: that.model })
}
else {
new Job.Views.ResponseAuth({ model : that.model })
}
},
error: function() {
alert('Error, repeat please.')
}
});
}
});