I went through the Railscast tutorial and got it all working. Working on a quick prototype to see if Backbone is viable but I've messed something up and I'm not sure what I've done wrong. I'm on Backbone 1.
View
class Shsh.Views.AssetsIndex extends Backbone.View
template: JST['assets/index']
initalize: ->
#collection.on('reset', #render, this)
render: ->
$(#el).html(#template(assets: #collection))
console.log('rendered')
this
Router
class Shsh.Routers.Assets extends Backbone.Router
routes:
'': 'index'
initialize: ->
#collection = new Shsh.Collections.Assets()
#collection.fetch({reset: true})
index: ->
view = new Shsh.Views.AssetsIndex(collection: #collection)
$('#container').html(view.render().el)
The view gets rendered fine, but the length of #assets comes back as 0. I can go through the steps in the console and when I render the view again it comes back as being the correct length. What am I doing wrong?
EDIT:
I also do actually have a collection and model. The code there is all boilerplate generated by Backbone On Rails.
You are calling fetch() too early -- in router creation. Should be called in specific route code instead. The way you implemented it, fetch and reset may complete before route is triggered and therefore you'll start listening to reset after it has been fired
I'm an idiot. Initialize is spelt wrong in Shsh.Views.AssetsIndex.
Related
I am making an app using OMDB api. I have define a method 'top_movies' in 'movie_controller' which is rendering json data.
I have defined collection like this:
class Fanboy.Collections.Movies extends Backbone.Collection
url: '/movie/top_movies'
Click to see JSON response
I fetched the collection in console and getting the objects in this manner.
Click to see the Image view of console
I want to display the list on a page. But I am not able to show the list.
movies_router.js.coffee
class Fanboy.Routers.Movies extends Backbone.Router
routes:
"": "index"
initialize: ->
#collection = new Fanboy.Collections.Movies()
#collection.fetch()
index: ->
view = new Fanboy.Views.MoviesIndex(collection: #collection)
$('#movies-container').html(view.render().el)
/view/movies_index.js.coffee
class Fanboy.Views.MoviesIndex extends Backbone.View
template: JST['movies/index']
initialize: ->
#collection.on('reset', #render, this)
render: ->
$(#el).html(#template(movies: #collection))
this
/templates/movies/index.jst.eco
<h4>Top movies of all time </h4>
<ul>
<% for movie in #movies.models : %>
<li><%= movie.get('Title') %></li>
<% end %>
</ul>
Here I am able to see h4 tag but not the list of Titles. What I am doing wrong here? Please someone help.
Update:- I did some debugging. Since the url defined is coming through Omdb API and the view are loaded asynchronously in backbone. Hence the issue.
So I put setTimeout for few seconds and now I am able to display the list. But this makes the app slower. What can I do now?
Though #collection.on('reset', #render, this)should have handled the issue. But why it is not able to?
Instead of 'reset' use 'sync':
#collection.on('sync', #render, this)
"sync" (model_or_collection, response, options) — when a model or collection has been successfully synced with the server.
Backbone.js Catalog of Events
I've hit a head-scratcher with a Backbone.js. The example is on jsfiddle here. I believe the issue is here:
App.Layout = new Backbone.Layout({
// Attach the Layout to the main container.
collection: App.chapters,
el: "body",
initialize: function () {},
beforeRender: function () {
// Add a sub-view for each Chapter
this.collection.each(function (model) {
this.insertView(model.get('id'), new App.ChapterView({
"id": model.get('id')
}));
}, this);
},
views: {
// But if I set the sub-view specifically if works
// "one": new App.ChapterView({id: 'one' })
}
});
In summary, the router should simply activate or deactivate backbone.layoutmanager sub-views based on the path, e.g., /#chapter/one, /#chapter/two, etc.
If I explicitly set the sub-views in App.Layout (see line 49 in the fiddle), the routing works as expected.
However, if I try to add the views by iterating a collection of models in the beforeRender function (line 40; beforeRender is coming from backbone.layoutmanager), they don't appear to be available when the router tries to find the matching view by ID.
Once the page has render, however, the view can be activated with:
App.router.navigate('/chapter/two',{"trigger": true});
Which seems to indicate that the views are properly being added and should be findable by the router with:
App.Layout.getView(name);
No doubt I'm simply overlooking something, or am about to expose my ignorance of the Backbone library. :)
The issue is that you're navigating and rendering out-of-sync. I've updated your code here: http://jsfiddle.net/6h268r7j/55/
It works when you use the declarative approach because those are outside of the render flow, essentially statically added. As soon as you use beforeRender/render you are now in an asynchronous render flow and they won't be available in your router callbacks.
The fix was to simply render the application layout first and then trigger the routing:
App.Layout.render().then(function() {
Backbone.history.start();
});
I'm new to Backbone (and Marionette), and trying to write a pretty simple app using both. The app has a menu of "groups" on the left nav, and a list of "entries" on the main right div. Every time a Group menu item is clicked, I filter the entries with the group ID and show them, when hide all others.
Here is the Entry Item view (all scripts are in CoffeeScript btw):
class EntryItemView extends Backbone.Marionette.ItemView
tagName: 'tr'
template: _.template $('#entryItemTemplate').html()
render: ->
#$el.html #template(#model.toJSON())
show: ->
#$el.show()
hide: ->
#$el.hide()
Here is the Entry List view, extending Marionette's CollectionView:
class EntryListView extends Backbone.Marionette.CollectionView
itemView: EntryItemView
el: '#main tbody'
This is the AppRouter, pretty much straightforwad:
class AppRouter extends Backbone.Router
routes:
'group/:id' : 'showGroup'
router = new AppRouter()
router.on 'route:showGroup', (id) ->
_.each entryViews, (view) ->
if view.model.get('group_id') is parseInt(id)
view.show()
else
view.hide()
(The entryViews variable is a simple global array to store all EntryItemView instances).
With this approach, navigating the app to /group/:id indeed invokes the show() and hide() method of each EntryItemView object. The problem is, looks like the reference between this object and the actual HTML doesn't exist, so the actual element <tr> doesn't show or hide.
Can you guys point out what I'm doing wrong here? Thanks in advance.
Here are a couple pointers:
since your template is in the HTML, you just specify the jQuery selector with template: "#entryItemTemplate"
you can remove the render declaration, because Marionette does that on its own (i.e. you're implementing the default behavior)
unless you know what you're doing, you typically don't declare an el property in a collection view. Instead you declare a region (possibly within a layout), where you will call the show method to display a view instance
The reason your code probably doesn't work is that it looks like Backbone code with some Marionette stuff thrown in. Take a look at the free sample to my book on Marionette. It should get you started quickly with Marionette and will explain most of what you're trying to accomplish here.
I have a view that I need to dynamically create and insert into an Ember app ( off master - v1.0.0-rc.3-178-ge031b24 ) , and recently this view has started producing the notice:
DEPRECATION: Using the defaultContainer is no longer supported.
[defaultContainer#lookup]
I've made some attempts to modify what I'm doing to rectify my implementation, but I haven't been able to find what I need to do.
currently, I'm attaching a new ViewContainer to the controller on route setup:
App.ThingRoute = Em.Route.extend
setupDetailContainers: (controller) ->
controller.set('imageContainer', Em.ContainerView.create())
controller.get('imageContainer').appendTo(App.rootElement)
Then in the controller, when they click an image thumbnail the full size is inserted with:
showFullImage: (image) ->
image_full = image.asset_url.replace(':size', 'original')
container = #get('imageContainer')
container.pushObject App.ShowImageView.create({image: image_full})
Any guidance on the correct way to do this, in order to remove deprecation warnings would be appreciated.
I had a same problem, but before the deprecation message, so Ember was throwing an error.
Look here https://github.com/emberjs/ember.js/issues/2597
The bottom line is that you need to use createChildView inside of the view you will be creating the child in so that proper parent - children hierarchy is created.
I started running into this problem after updating to v1.0.0-rc.3-292-g39e9ef7. For me the problem arose from defining a childViewusing create() rather than extend(). For example, I had done:
App.FooView = Ember.View.extend({
templateName: 'fooTemplate'
, childViews: ['barView']
, barView: Ember.View.create({
... bindings and stuff ....
})
.... more and more ....
});
... and the notice I was receiving went away when I changed the line:
, barView: Ember.View.create({
... to:
, barView: Ember.View.extend({
Hope this helps others who might hit this notice from a similar set of circumstances.
I have a router accessing its collection. My for loop wasn't iterating through the models so I tried logging the collection to see what it returned. Turns out when I log the collection directly I see all of the models as expected. But if I try to log the models attribute of the collection I get an empty array! It doesn't make sense. These lines are directly following each other. I tried changing the order and got the same outcome.
console.log(this.collection);
=> Shots
_byCid: Object
_byId: Object
length: 15
models: Array[15]
__proto__: Shots
...
console.log(this.collection.models);
=> []
console.log(this.collection.length);
=> 0
Why would this happen?
Here is the code as it is in the router to give a better context of where this code is firing:
# Routers
class Draft.Routers.Shots extends Backbone.Router
routes:
'' : 'index'
'shots/:id' : 'show'
initialize: ->
#collection = new Draft.Collections.Shots()
#collection.fetch()
index: ->
console.log #collection
console.log #collection.models
Jim,
This doesn't fix your problem - you've worked that out. But it explains why you're seeing the console output you see.
When you run console.log(this), you output the object itself and the console links references (pointers if you like) to the inner variables.
When you're looking at it in the console, at the time the console.log(this) runs the models area is empty, but at the time you look at the logs, the collection has finished loading the models and the inner array variable is updated, AND the reference to that variable in the object log shows the current content.
Basically in console.log(this),inner models variable continues its normal life and the console shows the current status at the time you're looking at it, not at the time you called it.
With console.log(this.models), the array is dumped as is, no reference is kept and all the inner values are dumped one by one..
That behaviour is quite simple to reproduce with a short timeout, see this fiddle.. http://jsfiddle.net/bendog/XVkHW/
I found that I needed to listen for the collection to reset. So instead of passing the model into the view I created another view expecting the collection and listened for the 'reset' event to fire 'render' for the view.
# Routers
class Draft.Routers.Shots extends Backbone.Router
routes:
'' : 'index'
'shots/:id' : 'show'
initialize: ->
#collection = new Draft.Collections.Shots()
#collection.fetch()
index: ->
view = new Draft.Views.Desktop(collection: #collection)
# Views
class Draft.Views.Desktop extends Backbone.View
el: $("body")
initialize: ->
#collection.on("reset",#render,this)
render: ->
console.log #collection
console.log #collection.length
You can use a promise. (.done will do fine)
#collection.fetch().done =>
for model in #collection.models
console.log model
this will give you #collection's models fetched and ready to go.
or if you don't need to force the app to wait,
#collection.on 'sync', =>
for model in #collection.models
console.log model
Both of these will let you do what you want.