I am trying to understand how a collection passed in to a view is being referenced and used. There does not seem to be any reference to the collection, but it's models are being used. Also, I'm unable to get the collection items to be bound/displayed when I use my collection that is bound to my api, but it works when I use the hard coded collection. Is it because I need to fetch my collection at some point? My collection is fine and the path is fine. I use it throughout my app without any problems.
Below is the code:
module.exports = Backbone.Collection.extend({
model: Employee,
url:"api/employees"
});
MainView
module.exports = base.extend({
el: '#content',
template:template,
initialize: function () {
// var items = new EmployeeCollection([
// {id: 1, firstName: "Test1 fName", lastName: "Test1 lName"},
// {id: 2, firstName: "Test2 fName", lastName: "Test2 lName"},
// {id: 3, firstName: "Test3 fName", lastName: "Test3 lName"}
// ]);
EmployeeListCollection = new EmployeeCollection();
//this.itemView = new EmployeeListView({collection: items}); //this syntax works if I uncomment the code above to create my item list
this.itemView = new EmployeeListView({collection: EmployeeListCollection}); //do i need to fetch the collection at some point?
this.render();
},
render: function(){
this.el.innerHTML = Mustache.to_html(this.template);
this.itemView.render();
$("#empList").html(this.itemView.el);
}
});
ItemListView - where does the passed in collection get referenced? I see a model reference, but I passed in a collection.
module.exports = base.extend({
//tagName:'ul',
tagName:'select',
initialize: function(){
_.bindAll(this, "renderItem");
},
renderItem: function(model){
console.log('employeelistloop render');
this.itemView = new EmployeeListItemView({model: model});
this.itemView.render();
$(this.el).append(this.itemView.el);
},
render: function(){
this.collection.each(this.renderItem);
},
});
Actually, I think I figured what the problem was. I did indeed need to fetch the collection in my ItemListView. And I also realize now that render is being called before renderitem and that each model in the collection is being passed in to the renderitem function. I was able to get my collection to work by calling fetch in my render:
var self = this;
this.collection.fetch().done(function(){
self.collection.each(self.renderItem);
});
So it all makes sense now. But I still don't fully understand why my code runs twice. When I do a console.log in the initialize and render of MainView, I get two calls every time the first time I run it.
Related
New to VueJs. I'm wondering how/where would I make an Ajax call to pull data dynamically down to populate the following Vue table?
https://jsfiddle.net/yyx990803/xkkbfL3L/?utm_source=website&utm_medium=embed&utm_campaign=xkkbfL3L
I've (roughly) modified the example above as follows:
var demo = new Vue({
el: '#demo',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: []
},
methods: {
fetchUsers: function() {
...
// ajax call using axiom, fetches data into gridData like this:
axios.get('http://localhost/url')
.then(function(response) {
this.gridData = response.data;
})
.catch(function(error) { console.log("error"); })
...
}
},
created: function() {
this.fetchUsers();
}
})
I'm trying to incorporate the ajax pieces from here:
https://jsfiddle.net/chrisvfritz/aomd3y9n/
I've added the fetchUser method which makes the ajax call to pull the data down. I'm able to pull down my data and print it to the console using both fetch and axiom, so I know that part works.
However, my data never appears or updates. The table loads blank. I think it has something to do with me putting the method and created hook on the Vue model object (demo), rather than on the component itself. But I'm not quite sure how to modify the example to resolve it, as the example passes the data in from the parent.
Can someone give me some guidance?
You problem is right over here:
.then(function(response) {
this.gridData = response.data;
})
Within your anonymous function within your then you don't have the context you expect. The most simple solution is adding a .bind(this) to the method.
.then(function(response) {
this.gridData = response.data;
}.bind(this))
By adding it your method body will be aware of the outer context and you can access your components data.
I'm fetching unsorted data from a server and want to display it in a sorted list using Backbone. For that purpose I'm using a comparator in the collection. However, Backbone fires the add events in an inconvenient order when adding multiple models to a collection at once.
Here's an example illustrating my problem (JSFiddle: http://jsfiddle.net/5wtnjj8j/2/):
In the initialize function of the PersonCollectionView I'm adding three persons to the collection (note that they are not sorted correctly). Each time Backbone inserts one of these models into the collection it fires an add event and my personAdded function is called. This function outputs the name of the inserted person and the index at which it is inserted.
This is the output I get:
insert "Alice" at index: 0
insert "Eve" at index: 2
insert "Bob" at index: 1
Obviously, the indices are correct (i.e., sorted by name). But why does Backbone fire the add events in the order that the models are specified in, not in the order of the indices?
I think this behavior is counter-intuitive because it makes it hard to build a sorted list of views. For example, imagine that I want to build a <ul> for the models. Inserting Alice would work (because her index is 0), but when the second add event arrives, I'm about to insert Eve at index 2 without having received Bob at index 1 first.
Is there a particular reason why Backbone fires the add events in the 'wrong' order and is there a way to receive the events sorted by index?
Model
var Person = Backbone.Model.extend({
defaults: {
name: 'Unknown'
}
});
Collection
var PersonCollection = Backbone.Collection.extend({
model: Person,
comparator: 'name'
});
View
var PersonCollectionView = Backbone.View.extend({
initialize: function() {
this.collection = new PersonCollection();
this.collection.on('add', this.personAdded, this);
var models = [{name: 'Alice'}, {name: 'Eve'}, {name: 'Bob'}];
this.collection.add(models);
},
personAdded: function(model, collection, options) {
var index = collection.indexOf(model);
var message = 'insert "' + model.get('name') + '" at index: ' + index + '<br>';
$('body').append(message);
}
});
A simple solution to your problem is to sort the models list before adding it to the collection.
var models = [{name: 'Alice'}, {name: 'Eve'}, {name: 'Bob'}];
this.collection.add(_.sortBy(models, 'name'));
Here's an example http://jsfiddle.net
Backbone is adding the models in the order you gave them to it and sort them after that, and before the first event reach your personAdded function Backbone has already added all the models that's why you got index 2 for Eve and not 1
For the fetch call, try to redefine the parse function:
var PersonCollectionView = Backbone.View.extend({
...
parse: function(response) {
return _.sortBy(response, 'name')
}
I believe you can also create a new collection and instantiate the collection with the unsorted array
initialize: function() {
var models = [{name: 'Alice'}, {name: 'Eve'}, {name: 'Bob'}];
this.collection = new PersonCollection(models );
this.collection.on('add', this.personAdded, this);
},
another approach you can take is declare your collection add binding after your items have been added.
initialize: function() {
this.collection = new PersonCollection();
var models = [{name: 'Alice'}, {name: 'Eve'}, {name: 'Bob'}];
this.collection.add(models);
this.collection.on('add', this.personAdded, this);
},
Edit: alternative approach to creating a collection view. It is generally not a good idea for the view to know where/what the container is (body in this case). It's a better practice to return the content of view from the render() function. Then add the view's el to the body.
var PersonCollectionView = Backbone.View.extend({
initialize: function(opts) {
var models = [{name: 'Alice', index:1}, {name: 'Eve', index:2}, {name: 'Bob', index:3}];
this.collection = new PersonCollection(models);
this.collection.on('add', this.personAdded, this);
},
personAdded: function(model, collection, options) {
var index = collection.indexOf(model);
var message = 'insert "' + model.get('name') + '" at index: ' + model.get('index') + '<br>';
this.$el.append(message);
},
render: function(){
this.collection.each(function(model){
this.$el.append('insert "' + model.get('name') + '" at index: ' + model.get('index')+"</br>");
},this);
return this; //making it chainable (render().$el);
}
});
//from server
var personCollectionView = new PersonCollectionView();
$('body').append(personCollectionView.render().el);
How to be sure that all of the view-s will be displayed in correct order. because of use of Ajax, first one finished will be displayed first, i want to always be displayed in right order...
_.each(view.collection.models, function (category) {
var productBlockListView = new ProductBlockListView({model: category});
productBlockListView.setElement(view.el).render();
}, this);
Use a comparator option to get a sorted collection.
var items = new Backbone.Collection([{
firstName: 'Steve',
lastName: 'Jobs'
}, {
firstName: 'Bill',
lastName: 'Gates'
}], {comparator: 'firstName'});
items.each(function(item) {
console.log('%s %s', item.get('firstName'), item.get('lastName'));
});
Documentation
Demo
I'm working on my first Backbone.js app and have run into some weird behavior that I'm concerned could indicate a problem in my design. My data looks like this:
Syllabus
Dance
Figure
Figure
Figure
Dance
Figure
Figure
etc.
I have created this model to represent it:
$.syllabus.Syllabus = Backbone.RelationalModel.extend({
urlRoot: '/api/syllabus',
idAttribute: 'id',
relations: [{
type: Backbone.HasMany,
key: 'danceAssignments',
relatedModel: '$.syllabus.DanceAssignment',
collectionType: '$.syllabus.DanceAssignmentCollection',
reverseRelation: {
key: 'syllabus',
includeInJSON: 'id'
}
}]
});
$.syllabus.DanceAssignment = Backbone.RelationalModel.extend({
urlRoot: '/api/danceassignments',
idAttribute: 'id',
relations: [{
type: Backbone.HasMany,
key: 'figureAssignments',
relatedModel: '$.syllabus.FigureAssignment',
collectionType: '$.syllabus.FigureAssignmentCollection',
reverseRelation: {
key: 'danceAssignment',
includeInJSON: 'id'
}
}],
});
$.syllabus.DanceAssignmentCollection = Backbone.Collection.extend({
model: $.syllabus.DanceAssignment,
urlRoot: '/api/danceassignments',
comparator: function(danceAssignment) {
return danceAssignment.get('index');
},
});
$.syllabus.FigureAssignment = Backbone.RelationalModel.extend({
urlRoot: '/api/figureassignments',
idAttribute: 'id'
});
$.syllabus.FigureAssignmentCollection = Backbone.Collection.extend({
model: $.syllabus.FigureAssignment,
url: '/api/figureassignments',
comparator: function(figureAssignment) {
return figureAssignment.get('index');
},
});
The FigureAssignmentCollection is sorting automatically when I change the index property of one of its members, but the DanceAssignmentCollection isn't, nor does it sort when I explicitly tell it to. The comparator is called, but if I print out the contents of the collection after the sort they're in the wrong order, and the interface renders them out of order.
Any thoughts?
I found the solution. My views were rendering the danceAssignments collection as part of SyllabusView. Once I added a DanceListView with the collection as its data and had it rendering the DanceViews instead of SyllabusView doing it the dances started sorting properly, and some other very odd behaviors resolved as well.
I setup a list of objects, add them to content[] array list. So far so fine. Ember DOM in App.list show correct data. Now, when I start altering the content properties without remove/add/replaceAt() any object in App.list it seems Ember doesn't pick this up.
View:
{{#each item in App.list.content}}
{{item.title}}
{{/each}}
Code:
function myApp() {
App = Ember.Application.create({});
App.Item = Ember.Object.extend({
title: null,
parent: null
});
App.MyList = Ember.Object.extend({
title: null,
content: [],
changed: function() {
console.log('here');
}.observes('content')
});
App.list = App.MyList.create({
title: "foobar",
content: [
App.Item.create({
title: "item1"
}),
App.Item.create({
title: "item2"
})
]
});
console.log(App.list.content);
// Add 3rd object to list
App.list.get('content').pushObject(
App.Item.create({
title: "item3"
})
);
}
..and later in some random Ember ArrayController I do this:
for (var i = 0; i < 2; i++) {
App.list.content[i].title = "I CHANGED YOU";
}
Looking at my App.list the content is correct, but view is not. Am I missing something? If I have to guess ArrayHasChanged() seems to be rigged for array size changed or object being changed which I'm not doing, I'm altering property data within specific objects of the content[] array. Is it possible or do I have to removeAt()/Add/Delete objects?
You need you use get and set so the observers/bindings trigger with your changes.
// Bad
App.list.content[i].title = "blah";
// Good
App.get('list').objectAt(i).set('title', 'blah');
If this still does not work for you there might be something else missing. If you could post a jsfiddle that would help a lot!