Routing for flexible JavaScript Single-Page-App? - javascript

I'm building a single page web app (because I want flexibility and speed when moving across pages/states) but I'm struggling with routing / urls ...
In the traditional paradigm I would have urls such as:
example.com/tools/population-tool/#currentYear=1950
example.com/tools/income-tool/#country=usa
example.com/nice-story/
example.com/nice-chapter/nice-story/
Now I'd like to replace this with a Router (for example using Backbone) that loads templates and controllers for the corresponding routes.
I'm thinking about having a pages object that stores the necessary page information:
pages : {
tools : {
template : "#tools",
breadcrumb : ["Home","Tools"]
}
nice-story : {
template : "#nice-story",
breadcrumb : ["Home","Stories","Nice Story"]
}
}
With a router, I'd now like load the right content and page state, given a url like:
example.com/#!/tools/population-tool/?currentYear=1950
or like this if not using Hashbang:
example.com/tools/population-tool/?currentYear=1950
How would you organize this routing so that the url scheme makes sense while still being flexible and allow for redirects and new query string paramaters?

This is not a complete answer to your question, but a few tips on Backbone...
You may want to define a method like loadPage() on your router which can empty and replace your main page container with a view that corresponds to each "page" in your app. Each route action can call that to load up the right view.
If you will be using pseudo query strings, make sure to add a matcher for them explicitly in your Backbone routes. For example:
'/tools/population-tool/?*params'
That will call your route action with the entire params string as the first parameter. You'll need to parse that...

Related

Global Variable in Javascript for Laravel Routes - Is this a good idea?

I've created some code using a View Composer where I am passing my Route Collection through to the front end on all views, so I can access all of my laravel routes in Vuejs via the route named associated with them.
For example, to upload an image using a vue component, instead of passing my upload route into the Vue Component, it is listed as a part of a global variable:
var uploadRoute = _.find(globalRoutes, function(route) { return route.name == 'route-name.image.upload' });
$.post(uploadRoute, data) ... etc
My question is...is this sensible? I'm publically publishing my entire app's routes.
Thanks
I think your hunch about exposing your entire apps routes is legit. IMO you should explicitly pick out the routes that you need. So in thise case, you should only expose route-name.image.upload. You could create a tiny helper function to look up routes and output them along with the URL as JSON.
function json_routes(array $routes)
{
$return = [];
foreach($routes as $route)
{
$return[$route] = route($route);
}
return new \Illuminate\Support\HtmlString(json_encode($return));
}
And the, in your main view:
var routes = {{ json_routes(["route-name.image.upload"]) }};
Getting a route is simple:
routes['route-name.image.upload'];
This is the most basic exaple I can think of. You can optimize it quite a bit. Just some ideas:
Place the routes in a central place, fx. a config element: json_routes(config('app.json_routes'))
Build a command that generates a static .json file so that you don't iterate through the routes on each page load. Remember to re-generate when you add more routes.
Create a function instead of an object to get the route. That allows you to build in logic and gives a more Laravel-like feel in your js: function route(path){ return window.routes.hasOwnProperty(path) ? window.routes[path] : null ;}
(Advanced) Re-write Laravels router logic and hook into the options array, allowing you to do something like Route::get('dashboard', '...', ['as'=>'dashboard', 'expose'=>true]);, then dynamically generate the before mentioned json-file on all routes with the expose option.

Backbone get URL parameter

I have a Backbone js app that runs when I go to the URL domain.com/item/1 or domain/item/2` etc. When the app starts I create a new instance of my model and pass it an id which needs to be the last part of the URL. Is there a way to access this in Backbone?
I know it's easy to build a router that can access parameters after a hash so I am better of changing my URL to be something like domain.com/item/1#1?
I don't know you have a backbone router or not.But that's easily achievable by one of the basic use of Backbone.router.
and you do not have to use # or anything.You can access anything between slashes.
routes: {
"item/:page": function(page){
//page holds the query parameter.
}
}
The routes hash maps URLs with parameters to functions on your router (or just direct function definitions, if you prefer), similar to the View's events hash. Routes can contain parameter parts, :param, which match a single URL component between slashes; and splat parts *splat, which can match any number of URL components. Part of a route can be made optional by surrounding it in parentheses (/:optional).
Please read the section of Backbone.router in the documentation for detail.
http://backbonejs.org/#Router
FYI, passing the query parameter to your model should not be executed when a user start app but when routes is called.otherwise everytime you want to change page,You need to change url and reload the whole page.
and usually Controller makes model instances which means,You'd better create controller instance with parameters in router and then create a model in the controller.something like this
routes: {
"item/:page": function(page){
var page = new YourNameSpace.Controller.Foo({pageId : page});
page.render();
}
}
//inside of Itempage Controller
initialize : function(){
this.model = new YourNameSpace.Model.Foo({pageId : this.pageId});
}

Ember.js - Setting a model and routing dynamically via API data

So I'm working on building a dynamic model for a project that reacts to data sent from an API. The api will return, among other things, what your location should be and this in turn becomes the url. So, eg:
{
location: 'xyz'
(...)
}
So currently my router will transition to the right route dynamically. But I still have to hardcode each route ( IndexRoute, LocationXYZRoute, LocationABCRoute, etc).
My goal is to create a single route that handles things dynamically. We'll call it App.LocationRoute and my routes would look something like:
App.Router.map(function() {
this.resource(':location', function() {
this.route(':subLocation')
}
}
Now, I have two architectural questions:
1) Whats a good way to handle this sort of dynamic routing? (I've read through the guide about dynamic routing using the ':post_id' type example, but I think I need a more holistic example to really grasp it.
2) The API sends back a whole host of other data as well. I want to add this to the route's model but I also have some other static models. Doing...
this.controllerFor(location).set('content', APIdata);
... works, but it does not set for routes currently using static models. I tried something like:
this.controllerFor(location).set('apiData', APIdata);
and...
this.controllerFor(location).set('model:apiData', APIdata);
... but neither worked.
Any suggestions?
1) Yes, you should use dynamic segment
this.resource('location', { path: '/location/:location_id' }, function() {
this.resource('sublocation', { path: '/sublocation/:location_id' });
});
2) Are you using ember-data? You could check sideloaded data. Anyway, you could read the json and set the payload of each entity for each specific route.
this.controllerFor('location').set('content', APIdata.location);
this.controllerFor('user').set('content', APIdata.user);
People could help you better, if you separate your questions and create a http://emberjs.jsbin.com/ with isolated each specific case?

Change / unregister Handlebars helper (Meteor)

Once I register a helper function for Handlebars using Handlebars.registerHelper(), is it possible for me to change and/or remove the helper? Can I just use registerHelper() again to overwrite the current helper, or is there such a thing as Handlebars.unregisterHelper()? Or should I use a different approach if I need a helper to change during an application?
The use case for me is with the Iron Router plugin for Meteor. I am using a layoutTemplate as the general structure of my page. I wanted to use a helper in the layout template right before I yield the main content of the page body (via a <template>, per se) so that each individual template can define its own page title but not have to specify the location in the page every time. For example, my layout template could look like this:
{{pageTitle}}
{{yield}}
And then in the .js file for the rendered template, I would use the following to fill in the {{pageTitle}} placeholder:
Handlebars.registerHelper("pageTitle", function() {
return "My Page Title";
};
Perhaps there is an alternative way to solve this problem.
What you can do is something like this
Handlebars.registerHelper("pageTitle", function() {
return Session.get('pt');
};
function changePageTitle(str){
Session.set('pt', str);
}
Meteor, being reactive, should update the page when a session variable changes. When you switch to another page, simply run changePageTitle.

Backbone.js : Structuring Application for fixed side panels

I have an application that has a middle panel that always changes depending on what part of the application the user is looking at. These might be messages, transactions etc.
Then there are 4 'fixed' panels at the 4 corners of the application around the middle panel that are mostly fixed for the lifetime of the application, but contain dynamically updated data and therefore need to be implemented using backbone.js
How do I structure such an application in backbone.js. It seems to defeat the "Do not repeat" rule to implement the intial rendering for all the side panels within every route in the router as I would end up repeating the same rendering code in every route.
How do I structure my code in this instance so that I don't repeat code in multiple places.
JavaScript is like any other code: if you find yourself writing the same lines of code, extract them in to a function. If you find yourself needing to use the same function, extract it (and related functions and data) in to its own object.
So, your router shouldn't be calling your views and models directly. Instead, it should be delegating to other objects that can manipulate your views and objects.
Additionally, since your going to set up the same basic page layout every time the app starts up, you might not want that code in the router. The layout happens whether or not the router fires, and no matter which route is fired. Sometimes it's easier to put the layout code in another object, as well, and have the layout put in place before the router fires up.
MyApplication = {
layout: function(){
var v1 = new View1();
v1.render();
$("something").html(v1.el);
var v2 = new View2();
v2.render();
$("#another").html(v2.el);
},
doSomething: function(value){
// do someething with the value
// render another view, here
var v3 = new View3();
v3.render();
$("#whatever").html(v3.el);
}
}
MyRouter = Backbone.Router.extend({
routes: {
"some/route/:value": "someRoute"
},
someRoute: function(value){
MyApplication.doSomething(value);
}
});
// start it up
MyApplication.layout();
new MyRouter();
Backbone.history.start();
I've written a handful of articles relating to these things, which you might find useful:
http://lostechies.com/derickbailey/2012/02/06/3-stages-of-a-backbone-applications-startup/
http://lostechies.com/derickbailey/2011/08/30/dont-limit-your-backbone-apps-to-backbone-constructs/
http://lostechies.com/derickbailey/2011/12/27/the-responsibilities-of-the-various-pieces-of-backbone-js/
http://lostechies.com/derickbailey/2012/03/22/managing-layouts-and-nested-views-with-backbone-marionette/

Categories