I'm trying to setup the reset password route however there appears to be something wrong with the route and I'm not sure what it is.
email link
http://localhost:3000/reset-password/MKlpo8uJVAsZ5P_UnU1yUHi_4Ez2csh0DtEo4umazwU
Path: routes.js
FlowRouter.route( '/reset-password/:token', {
name: 'atResetPwd',
action: function() {
BlazeLayout.render('loginTemplates', {
navbar: 'navbar',
content: 'atResetPwd',
footer: 'footer'
});
}
});
you are not passing the token to your route, so it's not available to your functions.
FlowRouter.route( '/reset-password/:token', {
name: 'atResetPwd',
action: function(params) {
BlazeLayout.render('loginTemplates', {
navbar: 'navbar',
content: 'atResetPwd',
footer: 'footer'
});
}
});
Then you can try to retrieve the token from your client side code, to call the appropriate method.
Related
I would like to the difference if any in the following ways of subscribing data,
using waitOn
waitOn:function(){
Meteor.subscribe('//some published function)
}
using onBeforeAction
Router.onBeforeAction : function(){
Meteor.subscribe('//some published function)
}
using subscriptions
subscriptions: function() {
this.subscribe('items');
}
If you want to publish data only for authorized users, it is possible that you check (if user is authenticated) for a route, in onBeforeAction. Something like:
Router.map(function(){
this.route('home', {
path : '/',
template : 'home'
});
this.route('register', {
path : '/register',
template : 'register'
});
this.route('login', {
path : '/login',
template : 'login'
});
this.route('requestlisting', {
path : '/requestlisting',
template : 'requestlisting',
waitOn : function(){
return Meteor.subscribe('RequestsPublication');
}
});
...
});
var requireLogin = function () {
if(!Meteor.user()){
if(Meteor.loggingIn()){
this.render(this.loadingTemplate);
}else{
Router.go('login');
}
} else {
this.next();
}
}
Router.onBeforeAction(requireLogin, {only: ['requestlisting',...]});
In this example, in onBeforeAction, routing occurs for 'requestlisting' only if a user logged in, after then it makes sense to subscribe to any data.
Let's say I have two routes.
One route is a "new Item" route and one is a "view Item" route.
In order to create a blank form I was just inserting a "blank" object into the database and then redirecting to the item view.
I understand this may not be the best way to do this, and I'm open to suggestions because I don't find a lot of tutorials that deal with this paradigm. Most just have a separate input form (which I don't want).
So the problem I'm running into is that when I navigate to my /new route, it ends up duplicating all the template content in the dom for my /org route even though it's just getting redirected to my /org route. I can reload the /org route all day with different "org data" and it never duplicates anything in my DOM. But the instant I hit that /new route button, it will add another set of DOM elements in duplicate fashion.
Can someone help me shed some light on what's going on? Here's my /new route code...
Router.route('/new/', {
name: 'new',
action: function() {
var newId = Organizations.insert({
// Set some defaults
init_date: new Date(),
modified_date: new Date()
});
this.redirect('/org/' + newId);
}
});
Here's my /org route code:
Router.route('/org/:_id', {
name: 'org',
subscriptions: function() {
return [
Meteor.subscribe('contacts', this.params._id),
Meteor.subscribe('organization', this.params._id)
];
},
onBeforeAction: function() {
user = Meteor.user();
if (!Roles.userIsInRole(user, ['admin'])) {
this.redirect('/submit-ticket');
this.stop();
} else {
this.next();
}
},
action: function() {
if (this.ready()) {
Session.set('currentOrgId', this.params._id);
this.layout('appLayout');
this.render('orgHead', {
to: 'orghead',
data: function() {
return Organizations.findOne({
_id: this.params._id
});
}
});
this.render('orgInfo', {
to: 'content',
data: function() {
return Organizations.findOne({
_id: this.params._id
});
}
});
} else {
this.layout('appLayout');
this.render('loading', {
to: 'content'
});
}
}
});
So i'm just getting started with iron-router, and I've been building a login system. It works via a .onBeforeAction hook before every route, checking if the user is logged in. However, there are a few routes I want public, so I've added an except option, as per the docs. Except the problem is it doesn't work :( can anybody see why?
Router.route('/new', function () {
name: 'new',
this.render('newComp');
});
Router.route('/c/:_id', {
name: 'compPage',
data: function() { return Comps.findOne(this.params._id); }
});
Router.route('/c/:_id/embed', function () {
name: 'embed',
this.layout('empty'),
this.render('compEmbed', {
data: function () {
return Comps.findOne({_id: this.params._id});
}
});
});
function loginFunction(){
// all properties available in the route function
// are also available here such as this.params
if (!Meteor.user()) {
// if the user is not logged in, render the Login template
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.layout('empty');
this.render('login');
}
} else {
// otherwise don't hold up the rest of hooks or our route/action function
this.next();
}
}
Router.onBeforeAction( loginFunction, {
except: ['embed'] // this aint working
});
The problem seems to be in your route definition, the name param should be in the third param of Router.route(), like this (so your route actually didn't have a name, thus the except:['route.name'] doesn't work):
Router.route('/c/:_id/embed', function () {
this.layout('empty'),
this.render('compEmbed', {
data: function () {
return Comps.findOne({_id: this.params._id});
}
});
}, {
name: 'embed',
});
More info about named routes here: http://eventedmind.github.io/iron-router/#named-routes
This came out recently: https://meteorhacks.com/server-side-rendering.html but there doesn't seem to be a full fledged example of how to use this with iron-router.
If I had a template like:
/private/post_page.html
{{title}}
{{#markdown}} {{body}} {{/markdown}}
How would I populate it with a single records attributes from a request for a specific ID ?
E.g page requested was localhost:3000/p/:idofposthere how to populate it with data and render it in iron-router for that route / server side?
Actually a bit easier than you think (I just followed Arunoda's SSR example and converted it to iron-router for you):
if(Meteor.isServer) {
Template.posts.getPosts = function(category) {
return Posts.find({category: category}, {limit: 20});
}
}
Router.map(function() {
this.route('home');
this.route('view_post', {
path: 'post/:id',
where:"server",
action : function() {
var html = SSR.render('posts', {category: 'meteor'})
var response = this.response;
response.writeHead(200, {'Content-Type':'text/html'});
response.end(html);
}
});
});
It only gets tricky if you share the same route for client and server side for example, if you want it to render client-side based on user agent.
Source: We use this strategy on our own apps.
UPDATE
While the above code is simply what the question is asking for, we can get this working to follow Google's Ajax spec by checking the ?_escaped_fragment_= query string before we reach the client..
Basically, what we mostly don't know about Iron-Router is that if you have identical routes declared for server and client, the server-side route is dispatched first and then the client-side.
Here's the main javascript (with annotations):
ssr_test.js
Router.configure({
layout: 'default'
});
Posts = new Mongo.Collection('posts');
// Just a test helper to verify if we area actually rendering from client or server.
UI.registerHelper('is_server', function(){
return Meteor.isServer ? 'from server' : 'from client';
});
myRouter = null;
if(Meteor.isServer) {
// watch out for common robot user-agent headers.. you can add more here.
// taken from MDG's spiderable package.
var userAgentRegExps = [
/^facebookexternalhit/i,
/^linkedinbot/i,
/^twitterbot/i
];
// Wire up the data context manually since we can't use data option
// in server side routes while overriding the default behaviour..
// not this way, at least (with SSR).
// use {{#with getPost}} to
Template.view_post_server.helpers({
'getPost' : function(id) {
return Posts.findOne({_id : id});
}
});
Router.map(function() {
this.route('view_post', {
path: 'post/:id', // post/:id i.e. post/123
where: 'server', // this route runs on the server
action : function() {
var request = this.request;
// Also taken from MDG's spiderable package.
if (/\?.*_escaped_fragment_=/.test(request.url) ||
_.any(userAgentRegExps, function (re) {
return re.test(request.headers['user-agent']); })) {
// The meat of the SSR rendering. We render a special template
var html = SSR.render('view_post_server', {id : this.params.id});
var response = this.response;
response.writeHead(200, {'Content-Type':'text/html'});
response.end(html);
} else {
this.next(); // proceed to the client if we don't need to use SSR.
}
}
});
});
}
if(Meteor.isClient) {
Router.map(function() {
this.route('home');
this.route('view_post', { // same route as the server-side version
path: 'post/:id', // and same request path to match
where: 'client', // but just run the following action on client
action : function() {
this.render('view_post'); // yup, just render the client-side only
}
});
});
}
ssr_test.html
<head>
<title>ssr_test</title>
<meta name="fragment" content="!">
</head>
<body></body>
<template name="default">
{{> yield}}
</template>
<template name="home">
</template>
<template name="view_post">
hello post {{is_server}}
</template>
<template name="view_post_server">
hello post server {{is_server}}
</template>
THE RESULT:
I uploaded the app at http://ssr_test.meteor.com/ to see it in action, But it seems to crash when using SSR. Sorry about that. Works fine if you just paste the above on Meteorpad though!
Screens:
Here's the Github Repo instead:
https://github.com/electricjesus/ssr_test
Clone and run!
SSR is lacking real life examples, but here is how I got it working.
if (Meteor.isServer)
{
Router.map(function() {
this.route('postserver', {
where: 'server',
path: '/p/:_id',
action: function() {
// compile
SSR.compileTemplate('postTemplate', Assets.getText('post_page.html'));
// query
var post = Posts.findOne(this.params._id);
// render
var html = SSR.render('postTemplate', {title: post.title, body: post.body});
// response
this.response.writeHead(200, {'Content-Type': 'text/html'});
this.response.write(html);
this.response.end();
}
});
});
}
Assets are documented here: http://docs.meteor.com/#assets.
I have a Meteor app with an editor page that should only be accessible to editors. I am using Iron-Router and my Router.map looks like the following. However, this is not working in an odd way. If I provide a link to the editor page, then all is well, but if I try entering the /editor url, then it always redirects to home, even if the user role is correctly set.
(One thing I ruled out was if Meteor.userId() is not set before Roles.userIsInRole is called in before.)
Anyone know why this would be?
Router.map(function() {
...
this.route('editor', {
path: '/editor',
waitOn: function() {
//handle subscriptions
},
data: function() {
//handle data
},
before: function() {
if ( !Roles.userIsInRole(Meteor.userId(), 'editor') ) {
this.redirect('home');
}
}
});
...
});
The Roles package sets up an automatic publication that sends the roles property on the Meteor.users collection. Unfortunately, you can't get a subscription handle for automatic publications, so you'll need to make your own.
Set up a new subscription that publishes the required data of a user, then configure Router to check that the data is ready before showing any page.
eg:
if (Meteor.isServer) {
Meteor.publish("user", function() {
return Meteor.users.find({
_id: this.userId
}, {
fields: {
roles: true
}
});
});
}
if (Meteor.isClient) {
var userData = Meteor.subscribe("user");
Router.before(function() {
if (Meteor.userId() == null) {
this.redirect('login');
return;
}
if (!userData.ready()) {
this.render('logingInLoading');
this.stop();
return;
}
this.next(); // Needed for iron:router v1+
}, {
// be sure to exclude the pages where you don't want this check!
except: ['register', 'login', 'reset-password']
});
}