Backbone go to same route won't refresh the page - backbone.js

I have a very simple tag which will go the current route, since I need to refresh the page to clear some messages.
<span>Click here to go get your password back</span>
<-- I am at /resetpassword route now -->
But this won't work, since I believe is Backbone route detects it is the same route, therefore just returns.
I found this question is related to mine, but there is a difference that I am not using functions, just tag.
(CMD + R) works
So is there any way I can refresh the same route page in tag?
Thanks!

In the view I would do this.
In the view
events:
"click span": "resetPage"
resetPage: ->
#trigger "reset:page"
Then in the controller
#listenTo currentView, "reset:page", (args) ->
model = args.model
App.vent.trigger "reset:page", model
Then in your router you listen for that message and call your API route again, essentially refreshing the page, but if you have something you would need to re-fetch you could just pass that along with each message and avoid having the memory/unresponsive feeling that a navigate({trigger: true}) would cause :).
#SampleApp.module "PostsApp", (PostsApp, App, Backbone, Marionette, $, _) ->
class PostsApp.Router extends Marionette.AppRouter
appRoutes:
"" : "list"
":id" : "show"
API =
list: ->
new PostsApp.List.Controller
show: (id, post) ->
new PostsApp.Show.Controller
id: id
post: post
App.vent.on "posts:list:clicked", ->
App.navigate "/"
API.list()
App.vent.on "reset:page", (post) ->
App.navigate "/" + post.id
API.show post.id, post
App.addInitializer ->
new PostsApp.Router
controller: API
To avoid re-fetching the post you'd do this in the initialize function in your controller:
#SampleApp.module "PostsApp.Show", (Show, App, Backbone, Marionette, $, _) ->
class Show.Controller extends App.Controllers.Application
initialize: (options) ->
{ post, id } = options
post or= App.request "post:entity", id
App.execute "when:fetched", post, =>
#layout = #getLayoutView()
#listenTo #layout, "show", =>
#panelRegion post
#postRegion post
#bannerRegion post
#show #layout
That will keep your app very responsive - note this is assuming you don't need to reset your models/collections. If you need to just reset the model/collection data the navigate({trigger: true}) route might be the way to go. Unless you want to keep track of say items you added and in the event do some manual resetting and then re-render the view.
I suppose you also could implement a "resetModel" function in your model that will re-fetch it and trigger a custom event which your view could listen to as a modelEvents and then re-render itself.

Related

backbone.history.naviguate trigger router staying on the same page

My problem is pretty simple.
I start on /app, when I click on some button I trigger /report with
Backbone.history.navigate('report',{trigger: true, replace: true});
Then I'm on /report page. No problem.
I have the same button on /report and I would like to trigger router route "report" when i click on it. It seems it's not working because I'm already on the asked page.
Any idea how I should proceed ?
thanks a lot
Backbone.history.navigate('report',{trigger: true, replace: true});
You should not be using this to render/reload your application views.This is not recommended.
Rather you should have a controller object (a js object) which extends Backbone.Events
Define a callback for the for the route "/report" as follows:
var MyAppBus = _.extend({},Backbone.Events);
MyAppBus.on("report",function(payLoad){
// 1.Load the data
// 2.Create an instance of the view passing the loaded data.
// (In your case, you could also pass an instance of the view in the payload
// and update the data alone in the view instance.)
// 3.Render the view.
// 4.Call the router navigate function with trigger set to false to
// update the url on a need basis.
});
Now, in the event/router callback you can do something like this:
MyAppBus.trigger("report",payLoad);
Where payLoad in the event callback could be:
var payLoad = {};
payLoad.view = viewInstance;

Find out rendered view in backbone

Is there any way I can find out the rendered view in backbone? I have 4 views
Login, Contact, Home and About
I would like to find out which view currently is rendered.
Assuming you're rendering all the views into the same element (otherwise you could know what the view is from the element id), you might add a property like 'class' to the view when you create it. Then that property can be accessed through view.options.
For example:
var LoginView = Backbone.View.extend();
var loginView = new LoginView({ model: new Backbone.Model, el: 'body', class: 'login' });
loginView.render = function() { var content = 'login'; this.$el.html(content) };
loginView.render();
console.log(loginView.options.class) // 'login'
Obviously this is an oversimplified example but the general idea should work. More details or a code sample would help if you need a more specific answer.
If you also wanted to make sure the view is actually rendered, just write a method that checks if content of the view is what you expect it to be

Backbone Marionette: CompositeView replacing items instead of appending them

I've got a Marionette CompositeView that I'm using to fill in a dropdown. The JSON response is clean when I call collection.fetch() from within the CompositeView, but instead of appending the new ItemViews, CompositeView seems to be replacing them in the DOM.
Here's my code (coffeescript):
class #PDCollectionItemView extends Backbone.Marionette.ItemView
el: 'li'
template: Handlebars.compile('{{ title }}')
class #PDCollectionsView extends Backbone.Marionette.CompositeView
id: 'pd_collections'
className: 'selection'
itemView: PDCollectionItemView
itemViewContainer: '.scroll ul'
template: HandlebarsTemplates['connections/collection_select'] #handlebars_assets gem
ui:
modalTrigger: '#pd_collection_selector'
modal : '#pd_selection_modal'
selectBtn : '#select_collection'
initialize: ->
#selectedCollection = undefined
Connectors.App.vent.on "connections:collectionStaged", #assignSelectedCollection
return #PDCollectionsView
And the parent layout where the fetch is called:
class #IndexLayout extends Backbone.Marionette.Layout
initialize: ->
#collections = new PDCollectionsCollection
#collectionsView = new PDCollectionsView
collection: #collections
onRender: ->
#collectionSelect.show #collectionsView
#collections.fetch
success: (collection, response, options) =>
Connectors.App.vent.trigger "connections:collectionsLoaded"
Connectors.App.vent.trigger "loadComplete"
error: (collection, response, options) =>
console.log response
I've tried manually appending the items with an appendHTML call, but I get the same behavior. I can log each itemView with a call to onAfterItemAdded on the #PDCollectionsView, and the item views are distinct; different cids, and the appropriate models.
I think the problem is in your use of Backbone's fetch operation. fetch "syncs" the collection with its state on the server. Without specifying any customization it will intelligently add new items, update changed items, and remove items no longer on the server. I'm guessing that if you examine the collection after you call fetch you'll see it's only got the items that are being rendered in the CompositeView.
You can modify fetch's behavior to sync to the server without removing anything by passing {remove: false}. This should yield the results you're looking for:
#collections.fetch
remove: false
success: (collection, response, options) =>
Connectors.App.vent.trigger "connections:collectionsLoaded"
Connectors.App.vent.trigger "loadComplete"
error: (collection, response, options) =>
console.log response

Setting URL in Backbone.Router for QUnit testing

I have a Backbone.Router based myRouter, that calls certain set() methods based on the URL:
var MyRouter = Backbone.Router.extend({
routes: {
"doSomething/:value": "doSetterOnModel",
},
doSetterOnModel: function(value) {
// does some set(value) calls on my Model
},
});
I would like to test now if with the model gets updated (set()) correctly using a QUnit test:
test( "update Model via URL change", function() {
var value = "newValue";
var url = "http://localhost/#/doSomething/" + value;
//does not work as it moves page away from QUnit's tests.html page
window.location = url;
// does also not work because it moves away from QUnit's tests.html page
myRouter.navigate(url);
deepEqual( myModel.get(key), value, "Value on Model was not updated by URL change");
});
How can I set the URL in Qunit in order to test if my Router performs the right action for a certain URL?
When I want to test my routers I usually bypass the document navigation altogether and simply call Backbone.history.loadUrl, which is responsible for matching URL fragments to a router methods:
Backbone.history.loadUrl("#/doSomething");
The additional benefit here is that you don't need to call Backbone.history.start in your test code. Note also the use or a relative url. Specifying the http://localhost part is not necessary.
Tiny demo here.

Trigger.io Backbone and OAuth2 using coffeescript

I have a rails app serving data on localhost:3000 using the Doorkeeper gem to provide a secure API
The client is a Trigger.io app I'm testing with my android phone, the Raffler from Railscasts Backbone episodes.
Problems>
I have a function that returns the correct oauth token & url for accessing the server. I'm confused as to where I should call that function and how I should store the return value so as it is available to the Collection class before a new collection is created.
When the client queries the server it returns 200 and seems to pass the requested object back but my view doesn't give the expected result - it returns zero for the length when it should be three.
To test this out I inputted the url in my browser, copied the returned json object and passed it directly into the function that instantiates #collection in router.coffee/initialize. This gets the desired result in the view.
I tried fetching the json object in Trigger.io's catalyst debugging console, no joy. Fetch returns an object but the length is 0
Not sure how to debug beyond what I've tried, new to coffee/Backbone. Appreciate your help, thanks!
raffler.coffee
window.Raffler ?= {
Models: {}
Collections: {}
Views: {}
Routers: {}
init: ->
new Raffler.Routers.Entries()
Backbone.history.start()
}
$(document).ready ->
Raffler.init()
entries.coffee
class Raffler.Collections.Entries extends Backbone.Collection
url: 'http://192.168.1.14:3000/api/v1/entries?access_token=022f854...
initialize: -> # this returns a valid url&token for accessing the server
entries_router.coffee
class Raffler.Routers.Entries extends Backbone.Router
routes:
'': 'index'
initialize: ->
#collection = new Raffler.Collections.Entries()
#collection.fetch()
index: ->
view = new Raffler.Views.EntriesIndex(collection: #collection)
$('#container').append(view.render().el)
entries_index.coffee
class Raffler.Views.EntriesIndex extends Backbone.View
template: _.template( $('#item-template').html() )
initialize: ->
#collection.on('fetch', #render, this)
render: ->
$(#el).html(#template(entries: #collection))
this
index.html
.....
<head>
<script id="item-template" type="text/x-underscore-template">
<h1> Raffler </h1>
<%- entries.length %>
</script>
</head> etc...
RE: Problem 1 here's what I'm currently trying:
entries_router.coffee
initialize: ->
#collection = new Raffler.Collections.Entries()
#collection.fetch()
entries.coffee
class Raffler.Collections.Entries extends Backbone.Collection
url: #url
#url = () ->
return "http://192.168.1.14:3000/api/v1/entries?access_token=#{params.access_token}"
results in 'url must be specified' error.
Here are two methods that would solve the first problem. You can set the url attribute on the Collection instance. So rather than returning your generated URL, you can do something like:
class Raffler.Collections.Entries extends Backbone.Collection
initialize: (args...) ->
#url = 'http://192.168.1.14:3000/api/v1/entries?access_token=022f854'
super(args...)
entries = new Raffler.Collections.Entries()
entries.fetch() # will use the url attribute on the collection instance
You can also specify the URL as a parameter to fetch:
entries.fetch(url: 'http://somewhereelse.com/') # will use a different URL
For the second part, I suspect you're having problems due to Same origin policy for HTTP requests from JavaScript. The usual solution when using Trigger.io Forge is to use the forge.requests module to make cross domain requests, a simple way to populate your collection would be:
entries = new Raffler.Collections.Entries()
forge.requests.ajax(
url: 'http://192.168.1.14:3000/api/v1/entries?access_token=022f854'
type: 'GET'
dataType: 'json'
success: (data) ->
entries.reset(data)
error: (e) ->
forge.logging.error("Failed to get entries: #{e.message}")
)
A more useful way might be to override Backbone.sync to back onto forge.requests.ajax. This is probably just a case of changing the last line of Backbone.sync from $.ajax as the two APIs are pretty similar.
Just in case it helps anyone - https://github.com/martindavis/trigger-backbone.sync

Resources