I am trying to return profile.toJSON() to an object so as to use it outside the above code. I am not understanding exactly how backbone function works, so i declare a global variable obj and trying to parse data with obj = profile.toJSON(). When i use console.log(obj) it displays successfully my data. When I put console outside the above code it returns underfined.
var obj;
var ProfileView = Backbone.View.extend(
{
el: "#profiles",
template: _.template($('#profileTemplate').html()),
render: function(eventName)
{
_.each(this.model.models, function(profile)
{
var profileTemplate = this.template(profile.toJSON());
obj = profile.toJSON();
$(this.el).html(profileTemplate);
}, this);
return this;
}
});
You're taking it by the wrong end. Precreate your model and pass it to a view. Don't try to extract something from the view rendering code, it's not meant to be used this way.
var Profile = Backbone.Model.extend({});
var ProfileCollection = Backbone.Collection.extend({
model: Profile
});
var ProfileListView = Backbone.View.extend({
...
// Everything render does is rendering
render: function() {
this.collection.each(function(model) {
this.$el.append(
this.template(model.toJSON);
);
}, this);
}
...
});
// Your profile instance is defined outside the view, making
// it de facto available to outside code
var profile = new Profile({
name: 'Fere Res',
rep: 48
});
// The profile we just created gets added to a collection
var profiles = new ProfileCollection([profile]);
// We create the profile list view and pass it the collection
var view = new ProfileListView({collection: profiles});
// When we render the view, the render() code defined above is called.
// You can easily see that all the params/variables it uses are in place
view.render();
// Rendering is done, let's check our model is still available
console.log(profile.toJSON()); // :)
I ve got this code which actually fetch data from json file:
$(function() {
var Profile = Backbone.Model.extend({
defaults: {
tstamp: "",
map:"",
tagsCloud:"",
sentiment: "",
usersCloud: "",
timeline: "",
highlights: "",
signals: ""
},
initialize: function() {
}
});
var ProfileList = Backbone.Collection.extend({
model: Profile,
url: 'data.json'
});
var ProfileView = Backbone.View.extend({
el: "#profiles",
template: _.template($('#profileTemplate').html()),
render: function(eventName) {
_.each(this.model.models, function(profile){
var profileTemplate = this.template(profile.toJSON());
//obj = profile.toJSON();
//console.log(obj.tstamp);
$(this.el).html(profileTemplate);
}, this);
return this;
}
});
var profiles = new ProfileList();
var profilesView = new ProfileView({model: profiles});
setInterval(function() {
profiles.fetch({reset: true});
}, 400); // Time in milliseconds
profiles.bind('reset', function () {
profilesView.render();
});
});
I tried to add profiles to a new collection:
var profiles1 = new ProfileList([profiles]);
var view = new ProfileView({collection: profiles1});
view.render();
console.log(profile.toJSON());
I ve got console message: Cannot read property 'models' of undefined
Related
I'm making a simple list of people with option when clicking on person's name the Router will take a name as a parameter 'student/:name' and find a right person's object in a collection. I instantiate collection in a GroupView class by fetching it from the server. And that's where the Error appears: to get the access to collection (so I can find right object) in my viewStudent() method in Router class, I'm making one more instance of GroupView(), and console shows an error and that's right, 'cause there're no objects in collection.
I cannot wrap my head around this, why in GroupView() I receive data from the server and my collection just works fine, but second time I instantiate GroupView() in a Router - there's no collection? Maybe there's any other way I can get access to the collection in my Router? Any help would be greatly appreciated.
var StudentModel = Backbone.Model.extend({
defaults: {
name: 'Volodya',
lastName: 'Peterson',
age: 22,
gender: 'male'
}
});
var StudentsCollection = Backbone.Collection.extend({
model: StudentModel,
url: '/students.json'
});
var StudentView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#studentTpl').html()),
events: {
'click': function () {
eventAggregator.trigger('student:selected', this.model);
}
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var GroupView = Backbone.View.extend({
tagName: 'ul',
initialize: function () {
this.collection = new StudentsCollection();
this.collection.on('update', this.render, this);
this.collection.fetch();
},
render: function () {
var self = this;
this.collection.each(function (student) {
var studentView = new StudentView({
model: student
});
self.$el.append(studentView.render().el);
});
$('body').html(this.$el);
}
});
var RouterView = Backbone.View.extend({
tagName: 'ul',
render: function () {
var self = this;
_.each(this.model.toJSON(), function (value) {
self.$el.append('<li>' + value + '</li>');
});
return this;
}
});
var GroupController = function () {
this.start = function () {
var groupView = new GroupView();
};
};
var Router = Backbone.Router.extend({
routes: {
'': 'index',
'student/:name': 'viewStudent'
},
index: function () {
groupController.start();
},
viewStudent: function (name) {
var groupView = new GroupView();
var selectedStudent = groupView.collection.find(function (student) {
return student.get('name') === name;
});
$('body').append((new RouterView({ model : selectedStudent})).render().el);
}
});
var eventAggregator= _.extend({}, Backbone.Events),
groupController;
$(function () {
var router = new Router();
groupController = new GroupController();
Backbone.history.start();
eventAggregator.on('student:selected', function (student) {
var urlpath= 'student/'+ student.get('name');
router.navigate(urlpath, {trigger: true});
});
});
my backbone collection collection doesn't populate when i just pass it in as props to a react component. I have tried first fetching the collection using componentDidmount and componentWillMount, but that still didn't populate the collection. If I test the code by setting a window variable pointing to DecksIndex and in the console tools call getInstance() and then fetch
,the data loads fine. my code is as follows:
//router.js
var DeckComponent = require("./views/deck.jsx")
var DecksIndex = React.createFactory(require("./views/decks.jsx"))
var decksCollection = require("./component/collections/decks.js");
module.exports = Backbone.Router.extend({
initialize: function(){
this.rootEl = document.getElementById('container');
},
routes: {
"":"index",
"decks/:id":"deckShow"
},
index: function(){
var decks = new DecksIndex({decks: decksCollection.getInstance()});
this._swapView(decks)
console.log("hooray!")
},
deckShow: function(id){
//var deck = Flashcards.Collections.decks.getOrFetch(id);
var showDeck = new DeckComponent();
this._swapView(showDeck);
},
_swapView: function(view){
if (this.currentView) {
React.unmountComponentAtNode(this.rootEl);
}
this.currentView = view
React.render(view, document.getElementById('container'));
}
});
//decks.js
var deck = require('../models/deck.js')
var decks = Backbone.Collection.extend({
url: "/api/decks",
model: deck,
getOrFetch: function(id){
var model = this.get(id);
var that = this;
if (model) {
model.fetch();
}else{
model = new deck({id: id})
model.fetch({
success: function(){
that.add(model)
}
})
}
return model;
},
parse: function (data) {
debugger;
return data.objects
},
});
decks.getInstance = _.memoize(function () {
return new decks();
});
module.exports = decks;
//decks.jsx
var DecksList = React.createClass({
render: function() {
return (
<div className="deck-list">
{
this.props.decks.map(function (deck) {
var title = deck.name
debugger;
return (
<div key={deck.id} className="note-summary">
{title}
</div>
);
})
}
</div>
);
}
});
module.exports = DecksList;
this is an example of a situation where a container component that manages state makes sense. If DecksList had a container that retrieved the collection when it mounted and only rendered DecksList once the data was available it would probably solve the problem. Here's a good article on the pattern: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
app.Model.Brand = Backbone.Model.extend({});
app.Collection.BrandCollection = Backbone.Collection.extend({
model: app.Model.Brand,
parse: function (response) {
return response.brandDTOList;
}
});
var brandcollection = new app.Collection.BrandCollection();
brandcollection.url = '/brands';
brandcollection.fetch({
success: function (collection, response, options) {
app.views.brandline = new app.View.BrandPanelView({
model: brandcollection
});
$('#tab-content').empty();
$('#tab-content').append(app.views.brandline.render());
}
});
In this view a single model is passed.
app.View.BrandItemPanelView = Backbone.View.extend({
initialize: function (options) {
this.views = {};
this.model.bind('destroy', this.remove, this);
},
events: {
'click .bra-edt': 'brandEditAction',
},
brandEditAction: function () {
this.model.get('image');
}
});
When i do this.model.get('image'); i get get is not a function.
I am not sure why i am getting such a error.
I tried to deep clone it but still no success. The values are there.
var newModel = new app.Model.Brand();
newModel = $.extend(true, {}, self.model);
newModel.get('image')
Your problem is where you are passing in your BrandCollection, instead of passing it in as a collection you are passing it in as a model.
So instead of
app.views.brandline = new app.View.BrandPanelView({
model: brandcollection
});
You probably want to do
app.views.brandline = new app.View.BrandPanelView({
collection: brandcollection
});
How are you instantiating the BrandItemPanelView?
You will have to pass in the model explicitly:
var view = new app.View.BrandItemPanelView({ model: myModel });
If myModel is in fact a Backbone model, it ought to have the get function...
view.model // should return your model
view.brandEditAction() // should run the function and get the 'image' attribute from your model
So my application below is actually firing "FIRE!" in the console twice on page load. Not sure why backbone is firing the url function twice when I am only seeing the one fetch being made. Any ideas as to why this might be causing it to fire twice?
window.ScheduleApp = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template($('#' + id).html());
};
//Define the Game Model.
ScheduleApp.Game = Backbone.Model.extend({
initialize: function() {
this.gameId = this.get('Id');
this.gameTime = this.get('Time');
}
});
//Define the Games Collection that contains Game Models.
ScheduleApp.Games = Backbone.Collection.extend({
model: ScheduleApp.Game
});
//Define the Day Model.
ScheduleApp.Day = Backbone.Model.extend({
initialize: function() {
this.games = new ScheduleApp.Games(this.get('Games'));
this.games.parent = this;
this.gameDayGDT = this.get('GeneratedDateTime');
this.gameDayDate = this.get('Date');
}
});
//Define the Days Collection that contains the Day Models.
ScheduleApp.Days = Backbone.Collection.extend({
model: ScheduleApp.Day,
url: function() {
console.log('FIRE!');
return '/js/test.json'
},
parse: function(data) {
var parsedSchedule = JSON.parse('[' + data.STUFF + ']');
return parsedSchedule;
}
});
ScheduleApp.DayCollectionView = Backbone.View.extend({
el: '.container', //Container where the views get rendered to.
initialize: function() {
this.listenTo(this.collection, 'reset', this.render);
},
render: function(event) {
if (this.collection.length === 0) {
$('.container-hidden').show();
}
//Cycle through collection of each day.
this.collection.each(function(day) {
var dayView = new ScheduleApp.DayView({
model: day
});
this.$el.append(dayView.render().el);
}, this);
return this;
}
});
ScheduleApp.DayView = Backbone.View.extend({
tagName: 'div',
className: 'game-date',
template: _.template($("#gameSchedule").html(), this.model),
initialize: function() {
this.listenTo(this.model, "reset", this.render);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var daysList = new ScheduleApp.Days();
daysList.fetch({
reset: true,
update: true,
cache: false,
success: function(collection, response) {
//console.log(collection);
},
error: function(model, resp) {
// console.log('error arguments: ', arguments);
// console.log("error retrieving model");
}
});
//create new collection view.
var daysCollectionView = new ScheduleApp.DayCollectionView({
collection: daysList
});
All models belonging to a collection build their URLs based on the collection URL, as stated here. My guess would be that your collection is calling the method once, then your model / models place the second call, in order to build the model URL.
Then again, this method seems pretty harmless to me: it's just a getter. I'd rather place the console.log call in the Collection#parse or Model#initializer methods, and count how many times it gets invoked there.
I'm relatively new to Backbone and though I know the general idea of how to use it, my learning has been rapid and I'm probably missing some key elements.
So I have a collection that contains an attribute called "type" which can be article, book, video, class. I have the view rendering and everything but I need to be able to filter the collection when links are clicked.
My question is - how can I get it to filter down the collection and still be able to refilter the original collection when I click on another type?
Here's the gist of my code, I simplified it for easy reading:
var TagsView = Backbone.View.extend({
initialize: function(query) {
this.collection = new TagsCollection([], {query: self.apiQuery} );
this.collection.on('sync', function() {
self.render();
});
this.collection.on('reset', this.render, this);
},
render: function() {
//renders the template just fine
},
filter: function() {
//filtered does work correctly the first time I click on it but not the second.
var filtered = this.collection.where({'type':filter});
this.collection.reset(filtered);
}
});
update: I managed to get this working. I ended up triggering a filter event.
var TagsCollection = Backbone.Collection.extend({
initialize: function(model, options) {
this.query = options.query;
this.fetch();
},
url: function() {
return '/api/assets?tag=' + this.query;
},
filterBy: function(filter) {
filtered = this.filter(function(asset) {
return asset.get('type') == filter;
});
this.trigger('filter');
return new TagsCollection(filtered, {query: this.query});
},
model: AssetModel
});
And then in my view, I added some stuff to render my new collection.
var TagsView = Backbone.View.extend({
initialize: function(query) {
this.collection = new TagsCollection([], {query: self.apiQuery} );
this.collection.on('sync', function() {
self.render();
});
this.collection.on('filter sync', this.filterTemplate, this);
this.collection.on('reset', this.render, this);
},
render: function() {
//renders the template just fine
},
filterCollection: function(target) {
var filter = $(target).text().toLowerCase().slice(0,-1);
if (filter != 'al') {
var filtered = this.collection.filterBy(filter);
} else {
this.render();
}
},
filterTemplate: function() {
filterResults = new TagsCollection(filtered, {query: self.apiQuery});
console.log(filterResults);
$('.asset').remove();
filterResults.each(function(asset,index) {
dust.render('dust/academy-card', asset.toJSON(), function(error,output) {
self.$el.append(output);
});
});
},
});
The reason it's not working a second time is because you're deleting the models that don't match your filter when you call reset. That's normal behaviour for the reset function.
Instead of rendering with the view's main collection, try using a second collection just for rendering which represents the filtered data of the original base collection. So your view MIGHT look something like:
var TagsView = Backbone.View.extend({
filter: null,
events: {
'click .filter-button': 'filter'
},
initialize: function (query) {
this.baseCollection = new TagsCollection([], {query: self.apiQuery} );
this.baseCollection.on('reset sync', this.filterCollection, this);
this.collection = new Backbone.Collection;
this.collection.on('reset', this.render, this);
},
render: function () {
var self = this,
data = this.collection.toJSON();
// This renders all models in the one template
dust.render('some-template', data, function (error, output) {
self.$el.append(output);
});
},
filter: function (e) {
// Grab filter from data attribute or however else you prefer
this.filter = $(e.currentTarget).attr('data-filter');
this.filterCollection();
},
filterCollection: function () {
var filtered;
if (this.filter) {
filtered = this.baseCollection.where({'type': this.filter});
} else {
filtered = this.baseCollection.models;
}
this.collection.reset(filtered);
}
});
To remove any filters, set a button with class filter-button to have an empty data-filter attribute. collection will then be reset with all of baseCollection's models
Here's a better answer to this. Instead of making it so complicated, you can just use the where method. Here's my replacement solution for the question above.
filterby: function(type) {
return type === 'all' ? this : new BaseCollection(this.where({type: type});
});
You can try using comparator function of your Collection.
http://backbonejs.org/#Collection-comparator
Basically its is like sorting your collection.