Flowrouter - have to reload whole page for route to be recognised - meteor

Quite basic setup - user submits a post, it's inserted by a method, then user should be routed to a confirm page with the _id of the newly created post:
const onSubmitPost = (post) => {
createPost.call(post, (err, res) => {
if(err) {
instance.errorMessage.set(err.reason);
} else {
FlowRouter.go("create-post/:postId/confirm", { postId: res });
}
});
};
// Route definition
FlowRouter.route("/create-post/:postId/confirm", {
name: "create-confirm",
action() {
BlazeLayout.render("MainPage", { content: "ConfirmPostContainer" });
}
});
But when I try this, I get There is no route for the path: create-post/abc123/confirm
If I manually press reload, it works fine - no problems.
Anyone have any idea what's going on, and how to fix?
EDITS
This is called on the /create-post route - redirect to confirm the
post after it's created
Added route definition
Tried using redirect instead of go - no difference

There are 2 things I can suggest you to try. My hunch is that the problem stems from calling the .go method from /create-post with a relative path.
So, first, try route-names instead: FlowRouter.go('create-confirm', { postId: res });
Second, try absolute paths: FlowRouter.go('/create-post/' + res + '/confirm'); - notice the leading slash / !
Does that work?

try FlowRouter.redirect(YOUR ROUTE NAME , { postId: res }

Related

IronRouter dataNotFound on blog post

I would like show 404 page if blog/xyz don't work. So i've add dataNotFound on my routes.js, but i've no result :
Router.route('/blog/:slug', {
name: 'blogPost',
parent: 'blog',
itemName: function () {
return this.data().post.title;
},
data: function () {
let post = Posts.findOne({
'slug': this.params.slug
});
return {
post,
profil
};
}
});
Router.onBeforeAction('dataNotFound', {
only: 'blogPost'
});
If i test wrong url with blog/ojhojeofje, i don't have 404 page, just post without data.
Do you have any idea ?
Thank you !
First of all, you need to register dataNotFound as plugin instead of in onBeforeAction:
Router.plugin('dataNotFound', { only: ['blogPost'] });
Secondly the dataNotFound plugin works by checking if your route data() returns a falsy value. Since you want to load multiple data object in your data() function, you need to alter your function so that it will return something falsy if the post is not found. For example you can simply do this:
data: function () {
let post = Posts.findOne({
'slug': this.params.slug
});
if (!post) {
return false;
}
...
Note that you also need to make sure that your subscription to the Posts collection is ready before you run data in order to avoid going to the not found page unnecessarily.

Stop users from navigating to a page that is restricted to their role

I want to restrict access to certain pages depending on users roles. So I don't want a logged in user to be able to just change the URL in their browser to navigate to a page they shouldn't have access to. So for such a route I'm doing something like:
action: function () {
if (!Roles.userIsInRole(Meteor.user(), 'admin')) {
this.render("AcressRestricted");
} else {
// Do routing for admin users here....
}
}
Is that the standard way to go? And do I need to add this code to every page I want to restrict or is there a more general solution / short cut?
You can use Router.onBeforeAction:
Router.onBeforeAction(function() {
if (!Roles.userIsInRole(Meteor.user(), 'admin')) {
this.render("AcressRestricted");
} else {
this.next();
}
}, {only : 'route_one', 'route_two'});
This will only work for route_one and route_two.
Be sure to name what you use in 'only' or 'except' in your route definitions:
Router.route('/' {
name: 'route_one',
...
});
You could set your code up a little differently in order to make it more readily reusable, and avoid having to copy and paste any changes across routes:
var adminFilter = function () {
if (Meteor.logginIn()) {
//Logic for if they are an admin
this.render('loading');
this.stop();
} else if (!user.admin()) {
// Logic for if they are
this.render('AcressRestricted');
this.stop();
}
};
And then whenever you need it, just drop it in along side "before:"
Router.map(function () {
this.route('adminPage', {
path: '/admin',
before: adminFilter
});
});

Correct way to manage routes after sign in for different roles

I have 2 roles, admins and users. I have a home route '/' that is just a sign in page. When admin users sign in they must go to one route ('adminPortal') and when user users log in they must go to the 'userPortal' route. On signing out both roles should route back to '/'.
Before I had an admin role, I was routing on sign in like so:
Router.onBeforeAction(function() {
this.render('loading');
if (! Meteor.userId()) {
this.render('Home');
} else {
this.next();
}
});
which worked fine (actually it was breaking my waitOn: render loading template stuff which I just discovered but more on that later). I then added roles like this (from a Stack Overflow answer I can't find right now):
server/
insertUsers=function(){
var adminId=Accounts.createUser({
username:"admin",
password:"password"
});
Roles.addUsersToRoles(adminId,"admin");
var userIdd = Accounts.createUser({
username:"user",
password:"password"
});
Roles.addUsersToRoles(userIdd,"user");
};
and
Meteor.startup(function () {
// always start from scratch (you will want to comment this line at some point !)
Meteor.users.remove({});
if(Meteor.users.find().count()===0){
insertUsers();
}
})
and
Meteor.publish("user", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'roles': 1}});
} else {
this.ready();
}
});
And I tried to route to the user/admin portals like this:
router.js
Router.route('/', {
before: function() {
if (! Meteor.userId()) { // I acutally added this check that the user is not logged in after the infinite loading problem but I thought the question was getting too long so I just left it in rather
this.render('Home')
} else {
if (Roles.userIsInRole(Meteor.user(), 'admin')) {
Router.go('/admin')
} else {
Router.go('/user')
}
}
},
waitOn: function () {
return Meteor.subscribe('user');
}
});
Now this very almost works! If I log is as either user I go to the right portal. However, when I sign out, my onBeforeAction (i.e. first code block in this question) only renders the Home template and does not actually change the URL to '/' (i.e. the URL remains either '/user' or '/admin'). Now when I try log in a second time, it will always take me to the route that I was taken to on the first log in unless I manually change the browser URL to '/'. So I thought I'd just replace the this.render('Home') with a Router.go('/'); but that seems to have created some sort of infinite loop where the Home template never renders (it did incidentally now for the first time correctly render my loading template though).
So thanks for reading all that! What's the right way to do this?
Try adding Router.go('/'); in your logout button event, along with Meteor.logout();
Example:
Template.loginButtons.events({
'click #login-buttons-logout' : function (event, template) {
Meteor.logout(function(err) {
Router.go('/');
});
}
});
I had the same issue as you, and that was the simplest way I've found to return to home page after logout.

Is a server route the right way to do AJAX responses with Meteor/Iron Router

Still trying to get my footing with Meteor. I need an AJAX like method to trigger something on the server and get a response back that it was done.
What I want to do is something like this:
Router.map(function() {
// Remove Blog Posting
this.route('blogRemove', {
path: '/blogRemove/:_id',
where: 'server',
handler: function() {
var request = this.request;
var response = this.response;
// Do some deleting here
}
});
});
This would trigger some server call to remove the blog with the given _id. I would then reply with JSON via the response object. But after 15yrs of development work I have learned: Just because it's possible, doesn't mean it's the right way...
So, the question is: For AJAX-type calls, is this the preferred way to do them in Meteor/Iron Router, or is there some more efficient/elegant way to do them?
Normally you would use a meteor method for that. For instance:
Server:
Meteor.methods({
blogRemove: function (id) {
// delete the blog
return {status: "OK", msg: "removed blog " + id};
}
});
Client:
Meteor.call('blogRemove', id, function(err, result) {
console.log(result);
});

access data from iron-router in rendered function

I'm trying to access data passed from iron router in the javascript function
router.js
this.route('editOrganization', {
path: '/editOrganization',
waitOn: function() {
return [
Meteor.subscribe('organization', this.userId)
];
},
data: function() {
return Organizations.findOne();
}
});
now if I wanted to access a property of organization in html (editCompany.html) I can do the following
{{name}}
but how do I access that same property in the js file
Template.editOrganization.rendered = function() {
//how do I access name?
}
UPDATE:
so if I click a link to edit organization I can get the value via
this.data.name
However, if I reload the page (same url) it throws an error saying data is null.
It is accessible through the rendered function context.
Template.editOrganization.rendered = function() {
var name = this.data && this.data.name;
};
This is confusing for many people but you need to configure the router to actually wait for the subscriptions you returned with waitOn.
Router.onBeforeAction('loading')
You can read the author's explanation here:
https://github.com/EventedMind/iron-router/issues/554#issuecomment-39002306

Resources