I've been following the Discover Meteor tutorials in order to create an application that has autopublish and insecure disabled. Things have gone well with autopublish but I haven't been able to successfully display the posts in the collection on the webpage after removing insecure. Could you guys take a look and see what's going on? Thanks.
The tutorials:
https://www.discovermeteor.com/blog/getting-started-with-meteor/
https://www.discovermeteor.com/blog/meteor-and-security/
The code:
forum.js
Posts = new Meteor.Collection("posts");
if (Meteor.isClient) {
Meteor.subscribe("posts", "published");
Template.posts.helpers({
posts: function() {
Meteor.call("viewPosts","published");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
Meteor.publish("posts", function(status) {
return Posts.find({status: status});
});
Meteor.methods({
viewPosts: function(status) {
return Posts.find({status: status}).fetch(); //This is still problematic
}
});
Posts.allow({
insert: function() {
return true;
}
})
}
forum.html
<head>
<title>Forum</title>
</head>
<body>
{{> posts}}
</body>
<template name="posts">
<h1>Posts</h1>
<ul>{{#each posts}}
<li>{{name}}</li>
{{/each}}</ul>
</template>
The error message. This appears whether I add or remove .fetch() from the viewPosts method:
=> Meteor server restarted
I20150320-14:27:09.418(0)? Internal exception while processing message { msg: 'method',
I20150320-14:27:09.419(0)? method: 'viewPosts',
I20150320-14:27:09.419(0)? params: [ 'published' ],
I20150320-14:27:09.419(0)? id: '1' } Maximum call stack size exceeded undefined
The problem is coming from this block in your client code:
Template.posts.helpers({
posts: function() {
Meteor.call("viewPosts","published");
}
});
The primary way Meteor retrieves data for you on the client is through its publish/subscribe architecture, which you already have in place and looks good. That means that the posts you need to display are already there for you on the client and you don't need to do a Meteor.call() to the server to go and get them. Instead, your helper can just look like this:
Template.posts.helpers({
posts: function(){
return Posts.find({status: "published"});
}
});
Generally, you want to use Meteor.call() for responding to template events, e.g. in Template.posts.events({}). Server calls generally don't belong in helpers, though I guess there can be exceptions. See this excellent article under 'overworked helpers' for more on that: https://dweldon.silvrback.com/common-mistakes
Related
I am new to Meteor. I am trying to call a Meteor.method('addTask') from an event helper and I keep getting the error: "Error invoking Method 'addTask': Method 'addTask' not found [404]". I will put my code below:
Template.add_task.events({
'submit .js-emoticon': function(event){
event.preventDefault();
// console.log('clicked');
// var text = event.target.text.value;
// $('#text_display').html(text);
// $('#text_display').emoticonize();
Meteor.call("addTask");
}
});
And the Meteor.method here:
Meteor.methods({
'addTask':function(){
var task = event.target.text.value;
Items.insert({
created:new Date().toLocaleDateString("en-US"),
task:task
});
console.log(task);
}
});
Both are on the main.js in the client folder. I have tried to put the method on the server/main.js and I get an error: "Error invoking Method 'addTask': Internal server error [500]".
If it is on the client it will log the value of #text to console but on the server it doesn't even do that.
As I said I have been learning Meteor and researched this as the way to do it. I am obviously missing something and can't figure it out. Any help would be appreciated.
You are trying to look at a DOM element from your server code. You need to get that element on the client and then pass it to the method, which you can put in the /lib folder for latency compensation if you wish.
Client:
Template.add_task.events({
'submit .js-emoticon': function(event){
event.preventDefault();
var task = event.target.text.value;
Meteor.call("addTask",task);
}
});
Server:
Meteor.methods({
'addTask':function(task){
check(task,String);
Items.insert({ created: new Date(), task: task });
console.log(task);
}
});
You never want to convert your dates to strings when you persist them in mongo. That makes searching on dates impossible.
I am developing an app using angularjs and this is my first hands on using angular. Although, I have started understanding it and have developed some part of the app but I am stuck at one particular point.
I am trying to implement login functionality, so as the page loads, I am authenticating user and redirecting him to login page. On successful login, I am storing some values of user in one of the config provider.
Now I am using an API which has their own method of authentication and they have expose the ajax method which I can use to authenticate a user.
I have provided a snippet below. What I am primarily doing is using the external API, authenticating the user and once authenticated, I am getting roles associated to that user using another ajax method of the API, called "GetUserDetails".
And inside the response of the "GetUserDetails", I am injecting a provider and setting some values, so I can use this across my app.
The problem here is the app.config method is never called/executded. I mean the ajax request is returning response, and the alert is displayed on my page, but app.config is never executed.
But the same app.config if I call inside the done() of GetUser method, the app.config gets executed and stores values in my provider. But I want the GetuserDetails values also to be stored before I do anything in my app as I want to execute certain functionality based on user.
Below is my function in main.js file
function(angular,angularRoute,app,routes,configService){
var $html = angular.element(document.getElementsByTagName('html')[0]);
angular.element().ready(function() {
$.c.authentication.getUser()
.done(function(response){
if(response.userName!="anonymous"){
$.c.ajax({
method: "GetUserDetails",
parameters: {
User: response.user
}
})
.done(function(res) {
alert("I have reached the destination").
app.config(['configServiceProvider', function(configServiceProvider){
configServiceProvider.setLoginStatus(true);
configServiceProvider.setUserName(response.userName);
configServiceProvider.setUserObject(response);
configServiceProvider.setUserRoleDetails(res);
}]);
})
.fail(function(res) {
alert("Error while getting user roles ."+res);
});
angular.resumeBootstrap([app['name']]);
}
else
{
app.config(['configServiceProvider', function(configServiceProvider){
configServiceProvider.setLoginStatus(false);
configServiceProvider.setUserName(response.userName);
}]);
//Show Login Screen
var url = window.location.href.split("#")[0];
window.location.href = url + "#/Login";
angular.resumeBootstrap([app['name']]);
}
})
.fail(function(response){
$rootScope.isLoggedIn=false;
});
});
Here is my configServiceProvider
define(['../app'],function(app){
return app.provider('configService', function(){
var options={};
this.setLoginStatus = function(status){
//$rootScope.isLoggedIn = status;
options.isLoggedIn=status;
};
this.setPreLoginInfo=function(info){
options.preLoginInfo=info;
};
this.setUserName=function(name){
options.username=name;
}
this.setUserObject = function(userObject) {
options.userObject = userObject;
}
this.setUserRoleDetails = function(userRoleDetails) {
options.userRoleDetails = userRoleDetails;
}
this.$get=[function(){
if(!options){
}
return options;
}];
});
})
Can anyone please explain me what's going wrong here or what I am missing ?
Also, is there any alternative to achieve the same functionality ?
No luck in figuring out why the above scenario was not working. Since I had already spent lot of time behind this, I have found a workaround to achieve the same with the use of services.
I'm using this roles meteor package and I can't get to my admin panel.
The behaviour I've got at the moment is logged in admin user gets redirected to home page instead of being shown the admin area.
If I run the following code I get returned "false" suggesting that the user is not an admin user:
Roles.userIsInRole(Meteor.user(), ['admin']);
If I try adding the roles on the console I get this:
Roles.addUsersToRoles("Nvu2wZRg3K2wd4j4u", ['admin']);
undefined
insert failed: MongoError: E11000 duplicate key error index: meteor.roles.$name_1 dup key: { : "admin" }
update failed: Access denied
Roles.addUsersToRoles("Meteor.user()", ['admin']);
undefined
insert failed: MongoError: E11000 duplicate key error index: meteor.roles.$name_1 dup key: { : "admin" }
This seems to suggest that this user already has the role assigned to it. The permission error I expect is down to the roles not being published. If there's a duplicate error then it seems like this user should have the admin role assigned to it.
Here's the relevant code.
Templates:
<template name="adminTemplate">
{{#if isAdminUser}}
{{> accountsAdmin}}
{{else}}
Must be admin to see this...
{{/if}}
</template>
<template name="accountsAdmin">
accountsAdmin template
</template>
Helper:
Template.adminTemplate.helpers({
// check if user is an admin
isAdminUser: function() {
return Roles.userIsInRole(Meteor.user(), ['admin']);
}
})
my router.js file
Router.configure({
layoutTemplate: 'layout'
});
Router.map( function () {
this.route('home', {path: '/'});
this.route('about', {path: '/about'});
this.route('faq', {path: '/faq'});
this.route('myaccount', {path: '/myaccount'});
this.route('admin', {
layoutTemplate: 'adminlayout',
path:'/admin',
template: 'accountsAdmin',
onBeforeAction: function() {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else if(!Roles.userIsInRole(Meteor.user(), ['admin'])) {
console.log('redirecting');
this.redirect('/');
}
}
});
});
I've got the following in my startup.js
Meteor.startup(function () {
// This is temporary
if (Meteor.users.findOne("Nvu2wZRg3K2wd4j4u"))
Roles.addUsersToRoles("Nvu2wZRg3K2wd4j4u", ['admin']);
});
Any one got any ideas on this one?
Setting the user role in the client is not possible, you should do this stuff server side, and hard-coding UUID in code is never a good idea (even temporary).
For testing purpose, you can use this code :
server/collections/users.js :
insertUsers=function(){
var adminId=Accounts.createUser({
username:"admin",
password:"password"
});
//
Roles.addUsersToRoles(adminId,"admin");
};
server/startup.js :
// always start from scratch (you will want to comment this line at some point !)
Meteor.users.remove({});
if(Meteor.users.find().count()===0){
insertUsers();
}
You can get rid of your isAdminUser helper, Roles provides a helper specifically designed for this purpose, it's called {{isInRole "list of comma separated roles"}}
{{#if isInRole "admin"}}
...
{{else}}
Of course you have to be logged in because the helper is testing against Meteor.user().
I guess your admin user never got the chance of being affected the admin role...
The rest of your code looks OK so hopefully it should work after addressing these details.
I have been building a simple application to learn angular.js. So far I hooked up all the pieces in the MEAN stack and I am able to save and retrieve data from Mongo.
The app is essentially a todo list. The user can create a project and inside the project create "cards" with "todos" which can then be moved from state to state ("backlog", "in progress", "complete", etc.)
I would like to be able to push the notifications to all the people who are connected to tell their apps that a refresh is needed to get the latest todos. In other words, let's say that user A adds a new card to project A, I would like to send a message out to all users who are currently watching project A so that their application issues a project refresh to get the latest and greatest.
Any suggestions on how to proceed? Which technology, if any, I need to add to the MEAN stack to be able to do something like this?
Thanks in advance
Since you're on the MEAN stack, the standard recommendation in Node would be to use the Socket.IO API.
They provide the following example of two way messaging (which would facilitate your push messages very easily):
Client
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
Server
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
It will use websockets where possible, and fallback to AJAX long polling or Flash polling in browsers where there is no websocket support.
As for integrating with Angular, here's a good blog post on Socket.IO and Angular:
I'll be writing about how to integrate Socket.IO to add real-time
features to an AngularJS application. In this tutorial, I'm going to
walk through writing a instant messaging app.
If you're already working with Express, you should check out express.io.
It has a bunch of cool features like Session support and the ability to forward normal HTTP routes to realtime routes.
Here is a module we have written for getting AngularJS push notifications working in PhoneGap / Cordava (with full instructions):
http://www.scorchsoft.com/blog/free-angularjs-cordova-push-notification-plugin/
Simply download the example code and install. There is also code included for setting up the pushing component in PHP.
Why not with HTML5 Notification API....
export default class NotificationService {
/**
* Constructor of the class.
*
*/
constructor() {}
showPushNotification(title: string = '', message: string, iconPush) {
if (window.Notification && Notification.permission !== "denied") {
Notification.requestPermission(function(status) {
var n = new Notification(title, {
body: message,
icon: iconPush
});
});
}
}
}
Server Side Code:
if (Meteor.isClient) {
Meteor.subscribe("messages");
Template.hello.greeting = function () {
Messages = new Meteor.Collection("messages");
Stuff = new Meteor.Collection("stuff");
return "Welcome to feelings.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
var response = Messages.insert({text: "Hello, world!"});
var messages = Messages.find
console.log("You pressed the button", response, Messages, Stuff);
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Messages = new Meteor.Collection("messages");
Messages.insert({'text' : 'bla bla bla'});
});
}
Client Side Code
<head>
<title>Test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click"/>
</template>
The problem:
When in javascript console I type Messages.insert({'text' : 'test test test'});
or click the button, underneath which a database insertion call is written
I don't see a document inserted in mongo. Going to mongo console and doing show dbs shows
messages (empty)
I have a few other questions, I have read through the meteor docs and also googled but I can't seem to find a clear answer to this:
Why do I need to declare a collection in client as well as server code?
I'm declaring collections inside Template.hello.greeting, what's the difference if I put it in if(Meteor.isClient) block directly.
Is any plans of adding some app directory structure in meteor like rails? where models and templates are separate? I'm not talking about express.js
Thanks.
You need to create the MongoDB collection in a global scope like outside of both the isClient and isServer scopes. So remove Messages = new Meteor.Collection("Messages") from that helper function and place it in global scope.
You cannot perform insertion directly through client, as meteor doesn't allows database insertion from client code. If you still want to insert/update from client, you must define database rules for client, see docs.
Or the preferred way is to create a server method to insert document, and call it from client using Meteor.call().
Creating collections inside Template.hello.greeting doesn't makes any sense, since the collections are used to store data on server that is accessible from client.
Update: Meteor > 0.9.1
Creating collections in Meteor is now:
Messages = new Mongo.Collection("Messages")
instead of:
Messages = new Meteor.Collection("Messages")