So I have a set of navigation links at the top of my page.
One of them links to a list of hardware the users owns showing name and description.
I click the nav link and my list of hardware if rendered fine.
Go into database, change the name of one of the hardware pieces.
Now i click the nav link again on the page. I would expect backbone to
render the page again. But it doesn't.
I have a message print to the console every time render is called. The second click does not run the render function on the view.
I imagine backbone prevents re rendering the view or calling events when a route that is already open is activated. Is there any way to get it to rerender the already open view when I click on nav link a second time?
You are correct that Backbone prevents a route from triggering if it's already on that route.
There are a few solutions.
You could forcefully trigger the route by hijacking the links and calling router.navigate("route/path", {trigger: true});.
Or, even better, avoid triggering the router and use an api object. It's something Derick Bailey has written about here. Every click after the first would use the api to re-run the render code, bypassing the Backbone router. You can see a more advanced use of this method in the bbclonemail app.
Related
I want to execute a piece of code every time a new the page in navigated to. I am using the <page-router-outlet>.
As mention here, while using page-router-outlet the component is cached. So when it is navigated back into view no Init lifecycle events of angular 2 are executed.
I tied using just the , which does not cache the component and as a result call the Init Lifecycle events. But using it exits the app when I press the Hardware back button.
The site you linked to also mentions this: "What you can do is inject Page inside your component and attach to page navigation events (for example navigatedFrom) and do the cleanup there." Those events are listed here, especially the "navigatedTo" event seems relevant for your usecase.
The emberjs docs say the following.
Note: A route with a dynamic segment will only have its model hook called when it is entered via the URL. If the route is entered through a transition (e.g. when using the link-to Handlebars helper), then a model context is already provided and the hook is not executed. Routes without dynamic segments will always execute the model hook.
How do I get the dynamic segment routes setupController code to execute every time. This seems to be an on going issue; see here.
The first time my code works. I press a button and everything executes as expected. But if I press the back button then press my application's button, there is information missing. Then if I refresh the page, everything is working again.
I populated an array to be displayed along side my model info. But when I navigate to that page via link, or press the back button, the array of information seems to be empty. I added some console.logs and it looks my code is getting executed but when it comes to the template displaying the contents of the array, it is empty.
The solutions that have been raised talk about afterModel functions however, the logic that I need to execute (more then the first time it is loaded) is in a setupController function. Other solutions I saw involve complex helpers that seem to be over kill for just executing code every time a user hits a specific route.
So basically, how do you force dynamic segment routes to act like it was entered through the url bar?
Any help would be greatly appreciated.
i am trying to show the user a payment popup as soon as he clicks on a payed object.
But after he pays he should directly enter the content he clicked on.
Therefore i think its a good solution to solve this with the router, because i want every link on the page that redirects to this content to show this popup.
My problem is i want to show the popup before redirecting the user.
So i tryed the onBeforeAction hook and stuff but everything working with the iron router seems to only hook in after the URL of the browser changed and the current template was unloaded.
Do you have an idea how to get this kind of behavior?
Cheers
Based on this answer, here is how you can hook the router using Router.onStop():
// onStop hook is executed whenever we LEAVE a route
Router.onStop(function(){
//check if the current route is the page from where you need to show your
//popup and show it based on, for instance, a session variable containing
//the previously clicked content id.
});
It's a common use case that I don't think is directly achievable within the iron router framework at present (although I would be delighted to be corrected!). As you've discovered, onBeforeAction is run before the page has rendered but after the new route has been run, so the old page has already disappeared.
Effectively, you're looking to queue the running of a new route until a certain action has been completed. The use case for which I've experienced this requirement is page transitions, for which the best solution appears to be to do completely the opposite of what you propose: i.e. to add the logic to an event attached to the link, and only redirect to the new route once that logic has been satisfactorily completed (i.e. the popup has been closed in your case).
I agree that doing something in the router would be a sensible way to approach this, but I'm not sure it's possible in iron router as things stand. Note that this has already been raised though!
Will this workshop?
'unload - runs just once when you leave the route for a new route.'
From
https://github.com/EventedMind/iron-router/blob/devel/DOCS.md#unload-hook
My app has a main view with some hidden elements that can be activated by the user. Like a typical sidebar on mobiles that slides in from the left. Or a cart that reveals the details when you tap on it.
The information in those initially hidden elements is always up to date since their content is mapped in the main app template. So my routes would not need to render any DOM.
The transitions are JS based. But now I want those states to be reflected in the URL, in order to get a consistent back button behavior.
How can I achieve that using the ember framework?
Update to make it more clear, what I am talking about:
To my understanding triggering routes in ember has mainly two side-effects:
Represent the new state in the URL, enabling consistent browser history support.
Render templates that manipulate the DOM based on some data
In my case, when for instance a user taps on the minimized cart I need:
Represent the new state in the URL, enabling consistent browser history support.
Execute my showCart() JS Function (no DOM changes, no template rendering)
When the user now taps on the browser back button, closeCart() should be executed (based on the fact that the state in the URL carries the information that the cart is open).
The problem is, where can they access these slide in windows? Can they only be accessed from a single location in your route map? Or can the user click on it regardless of whatever view they are in? And if they click it from different views, do you move them back to a different route just to pop out some slide in window?
If they can only click it from one place, then you could just add code in your setupController of that specific route to fire the js to slide out the window.
setupController: function(){
Ember.run.scheduleOnce('afterRender', this, function(){
//run some js to slide out the window
});
}
Honestly if they can click a button anywhere and have the slide out appear, I wouldn't try putting it into the url. Just my opinion though.
You can use the activate and deactivate methods from your show route, to know when is entered or exited from your route:
App.ShowRoute = Ember.Route.extend({
activate: function() {
alert('Entering in show');
// here would be your showCart
},
deactivate: function() {
alert('Exiting from show');
// and here the closeCart
}
});
I created a live demo here, to you see this working.
Is there a good pattern for implementing a Backbone.js Router that responds differently for these two events:
Direct navigation - A user navigates directly to page by putting the url directly into the browser or in the application via a call to Router.navigate()
Browser "back" button - The user presses the "back" button to go to a page that they were at previously.
The reason I ask is because if the router is called as a result of the user pressing the "back" button, I'd like to just put the old view back into its element via $('element_id').html(view.el) without the reloading data into the model and re-rendering the view.
If the router is called as a result of the user navigating directly to the page by entering the url into the browser, clicking on a link, or a call to Router.navigate() from within the application, I would like the router to instruct the model to re-request data and trigger a re-rendering of the view.
Any help and suggestions would be appreciated, thanks so much!
I faced a similar problem a while back. This may be more than what you want, but here's what I found works.
Direct navigation
For direct navigation, the usual flow is something like this:
Initialize the app: Most backbone.js projects will have some code that initializes the app (App.init). Although a lot of examples insert the JSON for the collection (todos, etc.) in the actual HTML, I personally like to use this code as an opportunity to fetch the collection after the page has loaded, something like this (coffeescript):
window.App =
...
init: ->
#todos = new App.Todos()
#todos.deferred = #todos.fetch()
#threads.deferred.done ->
App.appRouter = new App.AppRouter(collection: self.todos)
...
(The use of jQuery's deferred is to make sure that the collection is fetched before actually rendering the page.)
Initialize the router: Here you get a chance to assign an element to the router and assign the collection to the router (I'm using the 'SwappingRouter' from thoughtbot's backbone-support, which I highly recommend checking out):
App.Router = Support.SwappingRouter.extend
...
initialize: (options) ->
#el = $('.content')
#collection = options.collection
Execute the route handler: This is the last step, at which point the collection is already initialized and the router has a pointer to it, so we just have to create the view and render it:
show: (id) ->
view = new App.TodosView(model: #collection.get(id))
#swap(view)
(Swap renders the view and does a few other things to clean up.)
Browser back button
In this case, neither App.init nor AppRouter.initialize is called, so the collection by default won't be reloaded. Backbone will automatically call navigate on the previous route, so depending on what's in your route handler, the view may be re-rendered. In the example above, it would be (swap calls render), but you could work around this.
The key problem here and always with backbone is that you're working with a state-less (HTTP) protocol and a state-ful (rich client-side app) environment at the same time. Lining the two up so they work well together can be pretty tricky.