I'm working on a Backbone.js web application with a few different views. Let's say we have the AudioPlayer view at the top of the page that should be persistent and play audio while the rest of the page content changes. The rest of the page content should be able to be switched on demand (with the Router updating the url via navigate).
I'm looking for the correct way to hide/remove FirstView, insert a SecondView, and then hide/remove SecondView and insert/show FirstView again when the user clicks the "back" button.
I've been told that views should be removed when they are not shown to avoid memory leaks. If this is true, what's the proper way to re-create the view again, as its associated view.el has been destroyed during the remove process? Or is there a more logical way to do this?
This is the way I do it:
extend Backbone.View with a method called open that will append the view to the DOM as well as set a flag on the view that is now open, and a similar close property
make all views properties on a common views property of your app
create a method called clearViews on the app that will close all open views except the view names passed in
Here is a gist (in coffeescript) of what I've been using. Feel free to copy.
https://gist.github.com/4597528
So after extending Backbone in this way, lets suppose you want to create and open a new view in a Backbone route after closing all open views except the top nav bar, which app.views.topNav points to. You can say:
app.clearViews('topNav');
app.views.myNewView = new MyView;
app.render().open('body'); // or some other container
There are some great view and layout managers out there for larger projects like Marionette by Derick Baily and LayoutManager by Tim Branyen, but they seemed like overkill for my smaller projects.
I currently do this in several of my apps, and it is accomplished with a tabbed interface. You can see an example of those interface here:
Twitter Bootstrap
Zurb Foundation
jQueryUI
I use a backbone router to watch the url. This let's me keep deep-linking/bookmarking for users, but I usually have the view events trigger tab changes.
For my purpose I have a mult-tabbed app which has a chat window, image gallery window, and collaborative editing windows.
Related
I'm planning on an academics organizer application where you can add tabs much like you can add tabs in a browser, but it's in the context of the application. Each tab has its own history, route, navigation, etc.
I thought this would be trivial to implement with vue-router but thinking about it a little more I have no idea how to map router links to tabs that can change as routes are usually just set in stone.
I also thought about creating a separate router for each tab, but that also seems a bit funny. Can someone help me out?
EDIT: It's important to note that the application is not actually a web browser. I would just like browser-like tabs, but in the context of an academics-organizer-application-thing.
You probably don't need a router to do that you can simply use v-if and v-else on a specific property that sets the active tab
First idea.
It may be better to use some existed layout framework for tabs logic. For example Golden Layout
And when tab opens you can bootstrap into this tab vue.js app with vue-router in "abstract" mode.
For state managment you can use event bus or vuex. But it has to be the only one across the whole app.
Second idea.
If bootstrapping of several vue.js apps looks like overengineering, then you could create component which implements basic functions for history / navigation. This component have to wrap the content of each your tab.
I'm making a game using JavaScript, currently I'm using window.location = "somepage.html" to perform navigation but I'm not sure if that is the correct way to do it. As I said in the title I've choosed Blank App Template so I do not have any navigator.js or something like.
Can you guys tell me the best way to do it?
Although you can use window.location to perform navigation, I'm sure you've already noticed a few of the downsides:
The transition between pages goes through a black screen, which is an artifact of how the underlying HTML rendering engine works.
You lose your script context between pages, e.g. you don't have any shared variables or namespaces, unless you use HTML5 session storage (or WinRT app data).
It's hard to wire up back buttons, e.g. you have to make sure each destination page knows what page navigated to it, and then maintain a back stack in session storage.
It's for these reasons that WinJS + navigator.js created a way to do "pages" via DOM replacement, which is the same strategy used by "single page web apps." That is, you have a div in default.html within which you load an unload DOM fragments to give the appearance of page navigation, while you don't actually ever leave the original script context of default.html. As a result, all of your in-memory variables persist across all page navigations.
The mechanics work like this: WinJS.Navigation provides an API to manage navigation and a backstack. By itself, however, all it really does is manage a backstack array and fire navigation-related events. To do the DOM replacement, something has to be listening to those events.
Those listeners are what navigator.js implements, so that's a piece of code that you can pull into any project for this purpose. Navigator.js also implements a custom control called the PageControlNavigator (usually Application.PageControlNavigator) is what implements the listeners.
That leave the mechanics of how you define your "pages." This is what the WinJS.UI.Pages API is for, and navigator.js assumes that you've defined your pages in this way. (Technically speaking, you can define your own page mechanisms for this, perhaps using the low-level WinJS.UI.Fragments API or even implementing your own from scratch. But WinJS.UI.Pages came about because everyone who approached this problem basically came up with the same solution, so the WinJS team provided one implementation that everyone can use.)
Put together then:
You define each page as an instance of WinJS.UI.Pages.PageControl, where each page is identified by its HTML file (which can load its own JS and CSS files). The JS file contains implementations of a page's methods like ready, in which you can do initialization work. You can then build out any other object structure you want.
In default.html, define a single div for the "host container" for the page rendering. This is an instance of the PageControlNavigator class that's defined in navigator.js. In its data-win-options you specify "{home: }" for the initial page that's loaded.
Whenever you want to switch to another page, call WinJS.Navigation.navigate with the identifier for the target page (namely the path to its .html file). In response, it will fire some navigating events.
In response, the PageControlNavigator's handlers for those events will load the target page's HTML into the DOM, within its div in default.html. It will then unload the previous page's DOM. When all of this gets rendered, you see a page transition--and a smooth one because we can animate the content in and out rather than go through a black screen.
In this process, the previous page control's unload method is called, and the init/load/processed/ready methods of the new page control are called.
It's not too hard to convert a blank app template into a nav template project--move your default.html/.css/.js content into a page control structure, add navigator.js to default.html (and your project), and put a PageControlNavigator into default.html. I suggest that you create a project from the nav app template for reference. Given the explanation above, you should be able to understand the structure.
For more details, refer to Chapter 3 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, where I talk about app anatomy and page navigation with plenty of code examples.
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.
In some cases I need to reinitialize my Sencha Touch app. Till now I just used location.reload() to do this. However this is really bad for the user experience as the whole HTML page, JS and CSS will be reloaded, too.
So I'm looking for an easy and clean way to reset and reinitialize my app without reloading the whole page.
I tried to do something like Ext.Viewport.removeAll(true, true) to remove all view components from the viewport. But this results in some errors, when you masked the viewport before or if you showed some dialogs before. Sencha keeps some references to objects created while masking the view or showing a dialog, that won't be present any more after removing all components from the viewport:
Ext.Msg.alert()
Ext.Viewport.removeAll(true, true)
Ext.Msg.alert() // => will raise `TypeError: Cannot read property 'dom' of null`
How do you reinitialize your app without reloading the whole page? Is there any way to do it "the nice" way?
You can relaunch the app by calling the launch function of you application.
Suppose you application name is my_app, you can relaunch the app using:-
my_app.app.launch()
Assume I have a lot of views in my single page app. It means, I put some view inside a div and show this or that view depending on user's actions. So, I usually show on view at a time. For example, I have these views - dashboard, settings, entries, entry details.
Depending on current state of the app one of these views is visible and others aren't. When I go this way dashboard > entries > entry details, then edit some entry details and click save or back button, I want the app to go back to entries. But if I have a link on dashboard that, for example goes to the latest edited entry, I can go this way dashboard > entry details, and in this case I want to get back to the dashboard by clicking save or back button.
This case can become even more complex with deeper views paths. Right now I manage it like this - When a button responsible for views switching is clicked, the variable state is being changed to something like from_dashboard_to_entries. I listen for that variable to change, then do all the view switching from another function depending on the state variable value. This way I have to manually define all possible scenarios and test all the possible combinations of cases. Right now this approach works quite well, but I am worried about deeper, or longer views sequences.
My idea is to somehow create a history of views in an array or something but I am not clearly understand how to do it better.
Please share your thoughts how you would organize this.
Simply don't worry about the source view - you don't have to know it to change to the destination view. If you want a history, instead of manually storing it, consider using the HTML5 History API, preferably with a wrapper library (e.g. History.js).
Rather, We should be setting the view while changing the state of application. This may optimize the initial app launch time by not loading all the views at launch and will give a way for on-demand loading.