I'm doing something wrong while setting Session variables and handling the Router
main.js:
Template.global.onCreated(function(){
Session.setDefault("musicFilteredCategory", "latest")
});
router.js:
Router.route("/music/:category?", {
name: "music",
template: "music",
beforeAction: function () {
var category = this.params.category;
Session.set("musicFilteredCategory", category);
}
});
but when I open page "/music/latin-radio" and I check Session.get("musicFilteredCategory") I get "latest" instead of "latin-radio"
later I changed Session.setDefault("musicFilteredCategory", "latest") to outside the Template.global.onCreated({}) and the result is still the same.
What should be the best practice to do this?
I also want to add this feature once this is fixed:
when the user goes to "/music" to be redirected to "/music/:defaultMusicCategory"
PS: I'm using Meteor 1.2.0.1 & Iron Router 1.0.9
As #Kyll pointed out I should use onBeforeAction for the function to run.
This solved part of my problem, but the categories were not being changed when accessing the different routes.
Here's what I had to do:
Router.route("/music/:category?", {
name: "music",
template: "music",
onBeforeAction: function () {
var category = this.params.category;
if (category !== "undefined") {
Session.set("musicFilteredCategory", category);
}
this.render("music");
}
});
This doesn't cover the route "/music" (without the slash) so I also had to add this route, I placed it before the code above
Router.route("/music", {
name: "music",
template: "music"
});
To resolve this I had to move the Session.setDefault() outside the templates scope as they were overriding the Session established on the router, so I had to put them inside a Meteor.startup function
Meteor.startup(function () {
Session.setDefault("musicFilteredCategory", "latest");
});
Related
I am adding a new page to a website, and I am copying the code that already exists and is currently working in the website. Why is the FlowRouter.getParam coming up undefined when it works everywhere else?
client/JobInvoice.js
import { Invoices } from '../../../imports/api/Invoice/Invoice';
Template.InvoicePage.onCreated(function(){
const user = FlowRouter.getParam('_id');
console.log(user);
this.subscribe('invoices', user);
});
lib/router.js
Accounts.onLogout(function(){
FlowRouter.go('home');
});
FlowRouter.notFound = {
action: function() {
FlowRouter.go('/404');
}
};
const loggedIn = FlowRouter.group({
prefix: '/secure'
});
loggedIn.route( '/invoice', {
name: 'invoice',
action() {
BlazeLayout.render('FullWithHeader', {main:
'InvoicePage'});
}
});
What am I missing?
FlowRouter allows you to define routes with dynamic attributes (path-to-regexp), which are often representing document ids or other dynamic attributes.
For example
FlowRouter.route('/invoice/:docId', { ... })
would define a route that matches a pattern like /invoice/9a23bf3uiui3big and you usually use it to render templates for single documents.
Now if you want to access the document id as param docId inside the corresponding Template you would use FlowRouter.getParam('docId') and it would return for the above route 9a23bf3uiui3big.
Since your route definitions lacks a dynamic property, there is no param to be received by FlowRouter.getParam.
A possible fix would be
loggedIn.route( '/invoice/:_id', {
name: 'invoice',
action() {
BlazeLayout.render('FullWithHeader', {main:
'InvoicePage'});
}
});
to access it the same way you do for the other templates.
Readings
https://github.com/kadirahq/flow-router#flowroutergetparamparamname
Here is what I ended up doing and it works.
loggedIn.route( '/invoice/:id', {
name: 'invoice',
action() {
BlazeLayout.render('FullWithHeader', {main: 'InvoicePage'});
}
});
I'm quite new using Backbone and now I have found this new issue.
I use the route "jobprofile" to create a view which fetch the data from urlRoot= "job" (doing job/id using a default id) BUT if I add the :id to the route as "jobprofile/:id" which I need to type in the browser to be able to get the job.id view, then it stops to work and the url of the model change to: ".../jobprofile/job/id" which (obviously) give me 404 error.
Hope is clear. Thanks!
CODE:
I have a router.js
routes: {
...
"jobprofile/:id": "view", //without /:id works!
},
view:function(id){
console.log("view");
this.job = new Job();
this.job.setId(id); //This is set correctly
this.jobProfileView = new JobProfileView({
model: this.job,
el: $('.tab-content')
});
},
(View)JobProfileView.js:
...
initialize: function(){
var that = this;
this.model.fetch().done(function(){
console.log("fetch done!");
that.render();
});
},
...
(Model)Job.js:
urlRoot: 'job',
initialize: function () {
},
setId: function (job_id) {
this.set('id', job_id);
},
UPDATED:
Ok. So it looks that I "fix" the problem adding this.navigate('/jobprofile'); to the method view in router.js. I guess that the /:id which causes the problem is deleted from the route (actually when you see the browser its not there anymore) but I still keep the id in the method.
In any case, this is a really bad solution because when I try to go back it creates a bucle and it goes to jobprofile/id and navigate again to jobprofile. So if anyone has an idea it would be great...
Finally I understood what the problem was...
Basically there is a difference when in the url or urlRoot are set in the model. Thus, these two options appear:
url:'/foo'. In this case it will not take the base url.
example: www.web.com/foo
url:'foo'. In this case, it will take the base url
example: www.web.com/api/foo
My case is as follow:
(Model)Job.js:
urlRoot: '/job',
initialize: function () {
},
setId: function (job_id) {
this.set('id', job_id);
},
I have a single Ember app that needs to use different data depending on the domain that it's running on. For example, on domain1.com the site title might be "Domain 1 Website", whereas for domain2.org the site title could be "Cool Site!"
I need to be able to use the data in routes, controllers and templates.
My initializer so far:
import { request } from 'ic-ajax';
export function initialize(container, application) {
var domain = document.location.host;
return request('http://api/sites?domain=' + domain, {} ).then(function(response) {
// make the response available to routes, controllers and templates
});
}
export default {
name: 'site-data',
initialize: initialize
};
If you take a look at the docs:
http://guides.emberjs.com/v1.10.0/understanding-ember/dependency-injection-and-service-lookup/#toc_dependency-injection-with-code-register-inject-code
The second example
Ember.Application.initializer({
name: 'logger',
initialize: function(container, application) {
var logger = {
log: function(m) {
console.log(m);
}
};
application.register('logger:main', logger, { instantiate: false });
application.inject('route', 'logger', 'logger:main');
}
});
and the following explanation shows you how you can use .inject to make one of your variables, services or functions available to whatever objects you need within your application.
I am working on a site where I have to search in the DB for string that come after the / on the root domain. I can't find anything about it in the documentation.
I am trying to make it work with Iron Router but any other suggestion would work out.
Thanks for the help!
Edit: Basically I just want to pass anything that comes after domain.com/ to a variable.
Here's something i've been doing so maybe it'll lead you down the right path
Route sends URL params to ownedGroupList template
Router.route('/users/:_id/groups', {
name: 'owned.group.list',
template: 'ownedGroupList',
data: function() {
return {params: this.params};
}
});
Template ownedGroupList can access params object using this.data in onCreated, onRendered, and onDestroyed template event handlers
Template.ownedGroupList.onCreated(function(){
this.subscribe("owned-groups", this.data.params._id );
});
Template ownedGroupList can access params through this variable in helper methods
Template.ownedGroupList.helpers({
groups: function() {
return Groups.find({owner: this.params._id });
}
});
Template ownedGroupList can access params through template.data variable in event handlers
Template.ownedGroupList.events({
'click .a-button': function(event, template) {
var group = Groups.findOne({owner: template.data.params._id });
// do something with group
}
});
Here's a simple route that should do the trick
Router.route('/:keyword', {
name: 'keyword',
template: 'keywordTemplate',
data: function() {
return this.params.keyword;
}
});
This will pass the keyword as the data context to your template and then you can do whatever you want with it. Alternatively you can perform the search straight in the router (especially if you're passing the keyword to a subscription so that the search runs on the server). For example:
Router.route('/:keyword', {
name: 'keyword',
template: 'keywordTemplate',
waitOn: function(){
return Meteor.subscribe('keywordSearch',keyword);
},
data: function() {
return MyCollection.find();
}
});
This second pattern will send your keyword to a subscription named keywordSearch that will execute on the server. When that subscription is ready, the route's data function will run and the data context passed to your keywordTemplate will be whatever documents and fields have been made available in MyCollection.
When you want to use classes you created in Em.Application.create() in your router you need to specify the router outside of the application.create. But because the application is automatically initialized the router doesn't route to the / route.
You used to be able to defer the initialization by adding autoinit: false to the application.create. Now you are supposed to use App.deferReadiness() and App.advanceReadiness(). However this doesn't appear to work.
And I can't seem to escape the feeling that you are "supposed" to do it differently.
I added the minimal code to show the problem below. There is also a jsfiddle here
EDIT:
Apparently there is a new router in ember I kinda sorta overlooked that. I've changed the code to the new router, but guess what it still doesn't work :P
window.App = App = Em.Application.create({
ApplicationController: Em.Controller.extend({}),
ApplicationView: Em.View.extend({
template: Em.Handlebars.compile('{{outlet}}'),
}),
ExtendedPatientController: Em.ObjectController.extend({}),
ExtendedPatientView: Em.View.extend({
classNames: ['patient-view', 'extended'],
template: Em.Handlebars.compile('{{name}}')
}),
Patient: Em.Object.extend({
name: undefined,
}),
});
App.Router.map(function (match) {
match('/').to('application', function (match) {
match('/').to('extendedPatient');
})
});
App.deferReadiness();
App.ExtendedPatientRoute = Em.Route.extend({
setupController: function (controller) {
controller.set('', App.Patient.create({
name: "Bert"
}));
},
renderTemplates: function () {
this.render('extendedPatient', {
into: 'application'
});
}
});
App.advanceReadiness();
You're actually doing a lot more work than you need to here.
Here's all the code that you need to make your example work.
Template:
<script type="text/x-handlebars" data-template-name="index">
<div class="patient-view extended">
<p>Name: {{name}}</p>
</div>
</script>
App:
window.App = Em.Application.create();
App.Patient = Em.Object.extend({
name: null
});
App.IndexRoute = Em.Route.extend({
model: function() {
return App.Patient.create({
name: "Bert"
});
}
});
The working fiddle is at: http://jsfiddle.net/NXA2S/23/
Let me explain it a bit:
When you go to /, you are entering the automatic index route. All you need to do to show something on the screen for that route is to implement an index template. The easiest way to do that when you're getting up and running is to put your template in your index.html. Later, you will probably want to use build tools (see my answer here for more information).
You can control what model is displayed in a route's template by overriding the model hook in its route handler. In the case of index, the route handler is App.IndexRoute. In this case, the model is a brand new App.Patient.
You will probably want to implement controllers and events. You can learn more about the router on the Ember.js website
So the new router does solve this problem and does feel a bit shinier.
I finaly found out how to do this basic example this is what happens in the router:
App.Router.map(function (match) {
match('/').to('extendedPatient');
});
This what needs to happen in the views:
ExtendedPatientView: Em.View.extend({
classNames: ['patient-view', 'extended'],
//You need to specify the defaultTemplate because you extend the view class
//instead on initializing it.
defaultTemplate: Em.Handlebars.compile('{{name}}')
}),
You do not have to defer the readiness in the app the new router fixes that.
And in the route you do not need to specify the renderTemplates so the router now looks like:
App.ExtendedPatientRoute = Em.Route.extend({
setupController: function (controller) {
controller.set('content', App.Patient.create({
name: "Bert"
}));
},
});
http://jsfiddle.net/NXA2S/28/