I'm working on a rather large EmberJS app and I've come to the part where I want to be able to retrieve a tracking ID in the URL from any source. I've looked into Ember.Route.serialize, and a couple of similar questions here on SO, but no reply seems to properly solve the problem (i.e., I have been unable to implement them on my site). The serialize-hook for example is only called on transitions, and to make sure the parameter is read, you'd have to expressively specifiy child-routes to pick it up.
I would like to have a route like this:
mysite.com/#!/ --> start page.
mysite.com/#!/19 --> start page with tracking ID 19.
mysite.com/#!/about --> about page.
mysite.com/#!/about/19 --> about page with tracking ID 19.
It seems that currently, to be able to fetch a parameter from any URL, you'd have to manually create a child route for every single route to retrieve it in the router:
App.Router.map({
this.resource("index", { path: "/" }, function() {
this.resource("indexTid", { path: ":tId" });
});
this.resource("about", { path: "/about" }, function() {
this.resource("aboutTid", { path: ":tId" });
});
[ ... ]
However, this seems incredibly tedious and with so many routes, having to add individual route handlers (App.AboutTidRoute = Ember.Route.extend [...])...
I'm figuring someone's had this problem before. How do you best tackle this problem?
Alternative solutions are welcome, but note that the URLs should be possible do give to partners and the tracking should follow without further work put in from their side.
Thanks for your time.
Best regards,
dimhoLt
PS. My current solution uses this URL mysite.com/?trackingId=19#!/about, which solves the problem-ish by setting a cookie on the server, but isn't very pretty. A better solution is greatly appreciated.
Beta/canary versions of Ember.js(1.4.0-beta.2) include support for query params. The guides page can be found here.
You'll have to use a beta or canary version and manually enable the feature to use it:
ENV = {FEATURES: {'query-params-new': true}};
That guide includes an example of setting/accessing a globally available query param:
App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_VIEW_LOOKUPS: true
});
App.ApplicationController = Ember.Controller.extend({
queryParams: ['iamglobal'],
iamglobal: 'foo'
});
App.IndexController = Ember.Controller.extend({
needs: 'application',
iamglobal: Ember.computed.alias(
'controllers.application.iamglobal'
)
});
jsbin example
Related
I have a client of mine who's using a single page application, built in Ember, and they are trying to use analytics to measure dropoff rates at each stage of the form.
I need to try to give them a trigger they can fire when a new stage is reached, in order to update the URL of the page, thereby allowing them to track it in analytics.
Is this even the best way to achieve this?
Hopefully this makes sense, sorry, total lack of Ember knowledge on my side.
Thanks
The code below in a route can update query params of an ember route:
routes/my-route.js
import Ember from 'ember';
export default Ember.Route.extend({
queryParams:
{
no: {replace: true}
},
model: function(dto){
this.set('no', dto.no);
return {
no: this.get('no')
};
},
actions: {
buttonClicked(){
this.incrementProperty('no');
let newNo = this.get('no');
this.controllerFor(this.get('routeName')).set('no', newNo);
this.refresh();
}
}
});
You can take a look at this twiddle for complete code.
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?
please see this functioning JSBin: http://jsbin.com/acUm/20
Here is the behavior I am working on. If I type 'Monroe' to filter the list down and then hit the browser back button, I expect Ember to process the route and fire the request for all patients. Instead it appears to do nothing. This is especially confounding since the back button seems to work in other areas.
Perhaps I have set up this transition improperly? Or is this an Ember.js bug?
When you transition to a route, it's a good idea to use the childest route in the hierarchy.
In your case you have this:
this.resource('patients', { path: '/' }, function() {
// this is created for you
// this.route('index');
this.route('filtered', { path: '/filtered/:last_name' });
});
By default is created a route index for that resource, so you have the patients.index.
But your transition goes to patients and it isn't the childest.
So to correct this, I have changed your sample to use PatientsIndex[Controller,Router etc], instead of Patients[Controller,Router etc].
Working demo http://jsbin.com/acUm/24/edit
I am taking help of the https://github.com/thinkadoo/Projects application. I have built a similar app with the help of this one. My application is using d3 charts instead of the one this uses. My app initializes the routers as
var patientStatus = new PatientStatus('#application', {'credentials':Credentials,'secret':Secret});
Now if i want to implement Router then what changes should be done?
Here is my JSFiddle with both implementations. The first one is working. But the later part where in I am initializing the Router doesnt seem to work.
http://jsfiddle.net/sweety1112/YMAjm/
Can some one help me.
Here is an updated Fiddle that shows how routing works:
var Router = can.Control({
defaults: {}
}, {
init: function() {
// this.element.html(can.view('#index', {}));
},
':type/:id route': function(data) {
console.log('Type:', data.type);
console.log('Id:', data.id);
}
});
can.route.ready(false);
new Router('#content');
can.route.ready(true);
Basically, what you do is initialize your named placeholders and tell the controller that this should be handled by the route processor. Now if you go to a URL like #!test/23 the data of the handler will contain a type and id property.
I am new to Javascript and just started fiddling around with Meteor out of curiosity. What really surprises me, is that it seems that all HTML content gets combined into a single page.
I suspect there is a way to introduce some handling of URLs directing to special pages. It seems that the "todo" example is capable of doing this via some kind of Router class. Is that the "canonical" way of URL handling?
Assuming I can handle URLs, how would I structure my HTML code to display separate pages? In my case they could each have completely separate sets of data, so no HTML code needs to be shared at all.
Jon Gold's answer used to be correct, but as of Meteor 0.5.4:
Work has now shifted to Iron Router. Please consider using IR instead of Router on new projects!
Thus, the current "canonical" way to do this is probably to use IronRouter.
As far as I am aware, there is currently no out of the box way to do this.
What I suggest to do, is to use Backbone.js smart package.
Backbone.js comes with the push-state Router, and if the user's browser doesn't support that it will fallback to hash urls.
In your meteor app directory type this meteor add backbone.
Then somewhere in your client-side code create a Backbone.js Router like so:
var Router = Backbone.Router.extend({
routes: {
"": "main", //this will be http://your_domain/
"help": "help" // http://your_domain/help
},
main: function() {
// Your homepage code
// for example: Session.set('currentPage', 'homePage');
},
help: function() {
// Help page
}
});
var app = new Router;
Meteor.startup(function () {
Backbone.history.start({pushState: true});
});
Then somewhere in your Handlebars template, you can create a helper that will render a page based on the value set in Session's "currentPage".
You can find more information about backbone.js router here: http://backbonejs.org/#Router
Also relevant information on how to create a Handlebars helper method in Metoer here: http://docs.meteor.com/#templates
Hope this helps.
Meteor-Router makes this really easy. I've been using it in some apps I've been building with Telescope as a good reference. Have a look at Telescope's router.js
To use it…
mrt add router
In client/router.js:
Meteor.Router.add({
'/news': 'news', // renders template 'news'
'/about': function() {
if (Session.get('aboutUs')) {
return 'aboutUs'; //renders template 'aboutUs'
} else {
return 'aboutThem'; //renders template 'aboutThem'
}
},
'*': 'not_found'
});
In your template…
<body>{{renderPage}}</body>
I found the same problem. When the code gets bigger it is difficult to keep the code clean.
Here goes my approach to this problem:
I separate the different html pages as I would do with another web framework. There is an index.html where I store the root html page. And then for each big functional part I create a different template and place it in one different html. Meteor then merges them all. Finally I create a session variable called operation where I define what to show at each time.
Here goes a simple example
index.html
<head>
<title>My app name</title>
</head>
<body>
{{> splash}}
{{> user}}
{{> debates}}
</body>
then in splash.html
<template name="splash">
{{#if showSplash}}
... your splash html code goes here...
{{/if}}
</template>
then in user.html
<template name="user">
{{#if showUser}}
... your user html code goes here...
{{/if}}
</template>
and so on ...
In the javascript code then I check when to print each template using the Session variable, like this:
Template.splash.showSplash = function(){
return Session.get("operation") == 'showSplash';
}
Finally the Backbone Router manages this Session variable
var DebateRouter = Backbone.Router.extend({
routes: {
"": "showSplash",
"user/:userId": "showUser",
"showDebates": "showDebates",
// ...
},
splash: function () {
Session.set('operation', 'showSplash');
this.navigate('/');
},
user: function (userId) {
Session.set('operation', 'showUser');
this.navigate('user/'+userId);
},
// etc...
});
I hope this pattern is helpful for other Meteor developers.
This is my hacky solution to routing :
https://gist.github.com/3221138
Just put the page name as the template name en navigate to /{name}