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.')
}
});
}
});
Related
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);
UPDATE: I've updated my views to show how I resolved this question using information from the accepted answer.
I'd like to update/increment an attribute ('video_views') of my Backbone JS model via a click event from my view. But, as a Backbone rookie, I'm not sure how to accomplish this exactly.
I'd like the 'video_views' attribute to increment by one with the playVideo event (click).
Thanks for the help!
Here is the structure of my JSON from my API:
{
"id": 8,
"name": "Bike to work day",
"slug": "bike-work-day",
"tagline": "A brief tagline about the video.",
"description": "This is a test.",
"created": "2015-02-06T15:22:26.342658Z",
"website": "http://thevariable.com/",
"logo": "http://dev.thevariable.com/media/brands/logos/test_logo.jpeg",
"video": "http://dev.thevariable.com/media/brands/videos/3D463BC3-38B8-4A6F-BE93-3F53E918EC3B-3533-00000118880074BA_1.1.mp4",
"video_thumbnail": "http://dev.thevariable.com/media/brands/video_thumbnails/3D463BC3-38B8-4A6F-BE93-3F53E918EC3B-3533-00000118880074BA_1.1.mp4.jpg",
"links": {
"self": "http://dev.thevariable.com/api/brands/bike-work-day"
},
"status_display": "published",
"video_views": 0
}
Here are my Backbone views:
var TemplateView = Backbone.View.extend({
templateName: '',
initialize: function () {
this.template = _.template($(this.templateName).html());
},
render: function () {
var context = this.getContext(), html = this.template(context);
this.$el.html(html);
},
getContext: function () {
return {};
}
});
var HomePageView = TemplateView.extend({
templateName: '#home-template',
events: {
'click video': 'updateCounter',
'click .video video': 'playVideo',
'click .sound': 'muteVideo',
'click .js-open-card': 'openCard'
},
initialize: function (options) {
var self = this;
TemplateView.prototype.initialize.apply(this, arguments);
app.collections.ready.done(function () {
app.brands.fetch({success: $.proxy(self.render, self)});
});
},
getContext: function () {
return {brands: app.brands || null};
},
updateCounter: function (e) {
var id = $(e.currentTarget).data('id');
var item = self.app.brands.get(id);
var views = item.get('video_views');
var video = this.$('.video video');
// Only update the counter if the video is in play state
if (video.prop('paused')) {
item.save({video_views: views + 1}, {patch: true});
this.render();
}
},
playVideo: function () {
var video = this.$('.video video');
if (video.prop('paused')) {
video[0].play();
} else {
video.get(0).pause();
}
},
muteVideo: function (e) {
e.preventDefault();
var video = this.$el.parent().find('video');
video.prop('muted', !video.prop('muted'));
this.$('.sound').toggleClass('is-muted');
},
openCard: function (e) {
e.preventDefault();
this.$el.toggleClass('is-open');
this.$el.closest('.card-container').toggleClass('is-open');
}
});
And my Backbone models:
var BaseModel = Backbone.Model.extend({
url: function () {
var links = this.get('links'),
url = links && links.self;
if (!url) {
url = Backbone.Model.prototype.url.call(this);
}
return url;
}
});
app.models.Brand = BaseModel.extend({
idAttributemodel: 'slug'
});
var BaseCollection = Backbone.Collection.extend({
parse: function (response) {
this._next = response.next;
this._previous = response.previous;
this._count = response.count;
return response.results || [];
},
getOrFetch: function (id) {
var result = new $.Deferred(),
model = this.get(id);
if (!model) {
model = this.push({id: id});
model.fetch({
success: function (model, response, options) {
result.resolve(model);
},
error: function (model, response, options) {
result.reject(model, response);
}
});
} else {
result.resolve(model);
}
return result;
}
});
app.collections.ready = $.getJSON(app.apiRoot);
app.collections.ready.done(function (data) {
app.collections.Brands = BaseCollection.extend({
model: app.models.Brand,
url: data.brands
});
app.brands = new app.collections.Brands();
});
Just increment that attribute on the model and save it.
var views = model.get('video_views');
model.set({video_views: views + 1});
model.save();
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've gone over the other posts on the same topic and perhaps I'm missing something in my own code but it seems to me things should be working. I've never worked with localStorage and backbone and seem to be missing something here. Any thoughts are greatly appreciated!
my instances:
var Address = {
run: function() {
this.router = new AddressRouter();
this.contactscollection = new AddressCollection();
this.addContact = new AddressAddView();
this.listView = new AddressListView();
Backbone.history.start();
}
};
my collection:
var AddressCollection = Backbone.Collection.extend({
model: AddressModel,
localstorage: new Store('backbone-addressbook')
});
my model:
var AddressModel = Backbone.Model.extend({
defaults: {
id: '',
name: '',
email: ''
}
});
and my view:
var AddressAddView = Backbone.View.extend({
el: '#content',
template: _.template($('#addContactTemplate').html()),
events: { 'submit form#addContactForm': 'createContact'},
createContact: function(){
Address.contactscollection.create(this.newAttributes());
this.save();
this.input.val('');
},
newAttributes: function() {
return {
id: $('#id').val(),
name: $('#name').val(),
email: $('#email').val()
}
},
initialize: function() {
_.bindAll(this, 'addContactPage','render');
},
addContactPage: function(id) {
var contact = {},
model = Address.contactscollection.get(id);
if (id !== undefined && model !== undefined) {
contact = model.toJSON();
}
this.$el.html(this.template({contact: contact}));
}
});
Case matters.
localstorage: new Store('backbone-addressbook')
needs to be
localStorage: new Store('backbone-addressbook')
If localStorage isn't set, your collection is assumed to persist to a RESTful API, and a url is required.