I upgraded to Meteor 1.0, installed the latest iron-router package, tried to run my app and got this nice warning in my console log:
Route dispatch never rendered. Did you forget to call this.next() in
an onBeforeAction?
So I tried to modify my routes according to the new version.
this.route('gamePage', {
path: '/game/:slug/',
onBeforeAction: [function() {
this.subscribe('singlePlayer', this.params.slug).wait();
var singlePlayer = this.data();
if (singlePlayer) {
if (singlePlayer.upgrade) {
this.subscribe('upgrades', this.params.slug).wait();
this.next();
}
this.next();
}
this.next();
}],
data: function() {
return Games.findOne({slug: this.params.slug});
},
waitOn: function() { return [Meteor.subscribe('singleGame', this.params.slug)]}
});
How can I fix this?
Any help would be greatly appreciated.
Try removing all the .wait()s and removing the array around your onBefore function.
With the new API this.next() replaces .wait().
Related
I am using gulp to run and build to run my application. I am getting file contents using $http service in my index.js file and then setting value of a variable like
window.variablex = "http://localhost:8080/appname".
here is how I am doing it (in index.js)
(function ()
{
'use strict';
angular
.module('main')
.controller('IndexController', IndexController);
function IndexController($http){
$http.get('conf/conf.json').success(function(data){
window.variable = data.urlValue;
}).error(function(error){
console.log(error);
});
}
});
And I've created a factory to call the rest APIs of my backend application like
(function(){
'use strict';
angular
.module('main')
.factory('testService',['$resource',testService]);
function agentService($resource){
var agents = $resource('../controller/',{id:'#id'},
{
getList:{
method:'GET',
url:window.variable+"/controller/index/",
isArray:false
}
});
Now, I except a rest call to made like
http://localhost:8080/appname/controller
But it always sends a call like http://undefined/appname/controller which is not correct.
I can get the new set value anywhere else, but this value is not being set in resource service objects somehow.
I am definitely missing something.
Any help would be much appreciated
As you are using Gulp, I advise you to use gulp-ng-config
For example, you have your config.json:
{
"local": {
"EnvironmentConfig": {
"api": "http://localhost/"
}
},
"production": {
"EnvironmentConfig": {
"api": "https://api.production.com/"
}
}
}
Then, the usage in gulpfile is:
gulp.task('config', function () {
gulp.src('config.json')
.pipe(gulpNgConfig('main.config', {
environment: 'production'
}))
.pipe(gulp.dest('.'))
});
You will have this output:
angular.module('myApp.config', [])
.constant('EnvironmentConfig', {"api": "https://api.production.com/"});
And then, you have to add that module in your app.js
angular.module('main', [ 'main.config' ]);
To use that variable you have to inject in your provider:
angular
.module('main')
.factory('testService', ['$resource', 'EnvironmentConfig', testService]);
function agentService($resource, EnvironmentConfig) {
var agents = $resource('../controller/', {id: '#id'},
{
getList: {
method: 'GET',
url: EnvironmentConfig + "/controller/index/",
isArray: false
}
});
}
#Kenji Mukai's answer did work but I may have to change configuration at run time and there it fails. This is how I achieved it (in case anyone having an issue setting variables before application gets boostrap)
These are the sets that I followed
Remove ng-app="appName" from your html file as this is what causing problem. Angular hits this tag and bootstraps your application before anything else. hence application is bootstratped before loading data from server-side (in my case)
Added the following in my main module
var injector = angular.injector(["ng"]);
var http = injector.get("$http");
return http.get("conf/conf.json").then(function(response){
window.appBaseUrl = response.data.gatewayUrl
}).then(function bootstrapApplication() {
angular.element(document).ready(function() {
angular.bootstrap(document, ["yourModuleName"]);
});
});
This will load/set new values everytime you refresh your page. You can change conf.json file even at runtime and refreshing the page will take care of updating the values.
I am really struggling with waiting on a subscription to load for a specific route before returning the data to the template. I can see on from the publish on the server that a document is found, but on the client there is no document.
If I do a find().count() on the publish, it shows 1 document found, which is correct, but when I do the count on the subscription, it shows 0 documents.
I have tried a number of different methods, like using subscriptions:function() instead of waitOn:function(), but nothing works.
Collections.js lib:
SinglePackage = new Mongo.Collection("SinglePackage");
SinglePackage.allow({
insert: function(){
return true;
},
update: function(){
return true;
},
remove: function(){
return true;
}
});
Publications.js server:
Meteor.publish("SinglePackage", function(pack_id) {
return Packages.find({shortId: pack_id});
});
Iron Router:
Router.route('/package/:id', {
name: 'package.show',
template: 'Package_page',
layoutTemplate: 'Landing_layout',
waitOn: function() {
return Meteor.subscribe('SinglePackage', this.params.id);
},
data: function() {
return SinglePackage.find();
},
action: function () {
if (this.ready()) {
this.render();
} else {
this.render('Loading');
}
}
});
Am I doing something very wrong, or is this just a complicated thing to achieve? One would think that waitOn would make the rest of the function wait until the subscription is ready.
Any help would be highly appreciated.
It appears that the data function is running before the subscription is ready. Even if the data function did run after the subscription was ready, it wouldn't be a reactive data source rendering the pub/sub here pointless. Here's a great article on reactive data sources.
Referring to the example from the Iron Router Docs for subscriptions, you would do something like this:
Router.route('/package/:id', {
subscriptions: function() {
// returning a subscription handle or an array of subscription handles
// adds them to the wait list.
return Meteor.subscribe('SinglePackage', this.params.id);
},
action: function () {
if (this.ready()) {
this.render();
} else {
this.render('Loading');
}
}
});
Then in your template.js:
Template.Package_page.helpers({
singlePackage() {
// This is now a reactive data source and will automatically update whenever SinglePackage changes in Mongo.
return Package.find().fetch();
}
});
In your template.html you can now use singlePackage:
<template name="Package_page">
{#with singlePackage} <!-- Use #each if you're singlePackage is an array -->
ID: {_id}
{/with}
</template>
I'm new to meteor and coding in general. I need to change this to flow router so that I can get the comments to work(I guess). Does anyone want to lend a hand?
Router.map(function () {
this.route('post/:id', {
waitOn: function() {
return [
Meteor.subscribe('post', this.params.id),
Meteor.subscribe('postComments', this.params.id)
]
},
data: function() {
return {
post: Posts.findOne({_id: this.params.id}),
comments: Comments.find({postId: this.params.id})
}
}
});
});
And btw I'm using flow router on everything in the app so I guess iron conflicts with it which gives me this:
Oops, looks like there's no route on the client or the server for url: "http://localhost:3000/recipe-book."
I'm enjoying working with Meteor and trying out new things, but I often try to keep security in mind. So while I'm building out a prototype app, I'm trying to find the best practices for keeping the app secure. One thing I keep coming across is restricting a user based on either a roll, or whether or not they're logged in. Here are two examples of issues I'm having.
// First example, trying to only fire an event if the user is an admin
// This is using the alaning:roles package
Template.homeIndex.events({
"click .someclass": function(event) {
if (Roles.userIsInRole(Meteor.user(), 'admin', 'admin-group') {
// Do something only if an admin in admin-group
}
});
My problem with the above is I can override this by typing:
Roles.userIsInRole = function() { return true; } in this console. Ouch.
The second example is using Iron Router. Here I want to allow a user to the "/chat" route only if they're logged in.
Router.route("/chat", {
name: 'chatHome',
onBeforeAction: function() {
// Not secure! Meteor.user = function() { return true; } in the console.
if (!Meteor.user()) {
return this.redirect('homeIndex');
} else {
this.next();
}
},
waitOn: function () {
if (!!Meteor.user()) {
return Meteor.subscribe("messages");
}
},
data: function () {
return {
chatActive: true
}
}
});
Again I run into the same problem. Meteor.user = function() { return true; } in this console blows this pattern up. The only way around this I have found thus far is using a Meteor.method call, which seems improper, as they are stubs that require callbacks.
What is the proper way to address this issue?
Edit:
Using a Meteor.call callback doesn't work for me since it's calling for a response asynchronously. It's moving out of the hook before it can handle the response.
onBeforeAction: function() {
var self = this;
Meteor.call('someBooleanFunc', function(err, res) {
if (!res) {
return self.redirect('homeIndex');
} else {
self.next();
}
})
},
I guess you should try adding a check in the publish method in server.
Something like this:
Meteor.publish('messages') {
if (Roles.userIsInRole(this.userId, 'admin', 'admin-group')) {
return Meteor.messages.find();
}
else {
// user not authorized. do not publish messages
this.stop();
return;
}
});
You may do a similar check in your call methods in server.
Im getting the "waiting on local host" in Chrome and Firefox but doubt there is an issue with browsers with my laptop; the issue is with Iron Router. See my router.js:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() { return Meteor.subscribe('jobs'); }
});
Router.route('/', {name: 'jobsList'});
Router.route('/jobs/:_id/', {
name: 'jobPage',
data: function() { return Jobs.findOne(this.params._id); }
});
// route to edit a job post
Router.route('/jobs/:_id/edit/', {
name: 'jobEdit',
data: function() { return Jobs.findOne(this.params._id); }
});
Router.route('/jobs/create', {name: 'jobCreate'});
var requireLogin = function() {
if (! Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('accessDenied');
}
} else {
this.next();
}
}
Router.onBeforeAction('dataNotFound', {only: 'jobPage'});
Router.onBeforeAction(requireLogin, {only: 'jobCreate'});
Going to /jobs/sgjdhdbhbbd the page does not load, but if I change Router.route('/jobs/:_id/', { to Router.route('/:_id/', {, the page loads. I am lost. Are there any tweaks to try?
********EDIT********
I am also using Polymer if that helps.
Thank you all for your support. I should have mentioned I was using Polymer in the first my so my apologies.
The issue is with Polymer as discussed here. I have done what ThaumRystra said and all is well.
I have solved the issue by including a trailing forward slash on all polymer imports:
Replace:
rel="import" href="bower_components/paper-toast/paper-toast.html">
rel="import" href="bower_components/paper-fab/paper-fab.html">
With:
rel="import" href="/bower_components/paper-toast/paper-toast.html">
rel="import" href="/bower_components/paper-fab/paper-fab.html">
If you have an image src url, include the forward slash at the front and it will display.
I think the problem comes from here:
Router.route('/jobs/create', {name: 'jobCreate'});
The "create" is used as "id" on your route 'jobCreate'
Just try something like this
Router.route('/jobs/view/:_id/', {
name: 'jobPage',
data: function() { return Jobs.findOne(this.params._id); }
});
// route to edit a job post
Router.route('/jobs/edit/:_id/', {
name: 'jobEdit',
data: function() { return Jobs.findOne(this.params._id); }
});
Router.route('/jobs/create/', {name: 'jobCreate'});
EDIT:
The source of problem is here:
Router.route('/jobs/:_id/', {
All path after /jobs/ is used as :id