Why do I receive undefined value after reloading page in Meteor? - javascript

So I have the following form:
template(name='editUser')
.row
.col-md-4.col-md-offset-4
.page-header
h1 Edit user
form#edit-user-form
.form-group
label(for='name') Name
input#user-name.form-control(type='text' placeholder='Name' value='{{user.name}}')
.form-group
label(for='email') E-Mail
input#user-email.form-control(type='text' placeholder='E-Mail' value='{{getEmail user}}')
button.btn.btn-primary(type='submit') Update
the following handlebars.js-Helper:
Handlebars.registerHelper('getEmail', function (user) {
if (user.emails && user.emails[0] && user.emails[0].address)
return user.emails[0].address;
return '';
});
and the following iron-Router code:
EditUserController = RouteController.extend({
template: 'editUser',
waitOn: function () {
return Meteor.subscribe('user', this.params._id);
},
data: function () {
return {
user: Meteor.users.findOne( { _id: this.params._id } )
};
}
});
If I run my application and click on the link to the edit-User-Form I can see the E-Mail Address. But if I change my code and Meteor automatically refreshes the page, the E-Mail-Field is empty and the console says, that it can't fetch the value of undefined.
If I use the same form, but with a with-Helper, the E-Mail is displayed even if Meteor automatically refreshes the page:
template(name='editUser')
.row
.col-md-4.col-md-offset-4
.page-header
h1 Edit user
form#edit-user-form
with user
.form-group
label(for='name') Name
input#user-name.form-control(type='text' placeholder='Name' value='{{name}}')
.form-group
label(for='email') E-Mail
input#user-email.form-control(type='text' placeholder='E-Mail' value='{{getEmail this}}')
button.btn.btn-primary(type='submit') Update
Why is this so? And should I always use the with-Helper if I get single Results (only one Result to display)?
Thanks in advance!

Replace Meteor.users.findOne with Meteor.users.find.
When findOne doesn’t find anything, it returns undefined which causes your error; when find doesn’t find anything, it returns an empty cursor which Meteor knows what to do with. Essentially all you were doing by adding with was to cause Meteor to check if the value was undefined, but that check isn’t necessary for a cursor, empty or otherwise.

Related

SOLVED: JQuery reading EJS variable as a String and not the value of the EJS request

Alright, so I'm having a bit of a problem. I have an app that displays some facts via a search input. If that fact isn't existing, I want to display an error.
I'm producing that error via Node on the backend, and via EJS sends the error message to the HTML (.ejs) and javascript files.
Long story short, the error message displays correctly, but the error popup also displays when refreshing the page, even though there isn't any errors to display.
Error.js
var clientError = "<%=clientError%>"; //<--- (1) see comment below
$("#error").hide();
if(clientError !== "") { //<--- (2) see comment below
$("#error").fadeIn().show();
setTimeout(function(){
$("#error").fadeOut().hide();
}, 4000);
}
(1) This is being interpreted as the word "clientError" and characters "<%=%>" of "<%=clientError%>", and NOT the value of the .ejs query, for example, "An error occurred". This leads to problem no. 2, see below.
(2) Because "<%=clientError%>" isn't being read as an empty string, even if there aren't any errormessages, it runs the code either way and displays the error-popup. So when I refresh the website I get the popup, because the string isn't empty (even though it doesn't display any message, because there aren't any errors).
I have also tried some other variants of the error.js code, for example:
if(clientError.length >= 17) ...executes error popup // no luck with this either, see comment 1 and 2 above.
//I have also tried not defining clientError in App.js:
var clientError;
//And then inside error.js:
if(clientError !== undefined) ...executes error popup //no luck with this, since error.js reads it as a defined string.
App.js
var clientError = ""; //<-- (3)
...
...
app.get("/:option/:input", function(req, res) {
var opt = req.params.option;
var inp = req.params.input;
Article.find({
option: lodash.capitalize(opt),
input: lodash.capitalize(inp)
}, function(err, foundArticle) {
if (err) {
clientError = "Internal Server Error. Contact Administrator.";
setTimeout(function(){
clientError = "";
},4000);
console.log(err);
}
else if ((!foundArticle) || (foundArticle.length <= 0)) {
const notFound = new Notfound({
option: searchOptions,
input: searchInput
});
clientError = "Article not found. Try again."
setTimeout(function(){
clientError = "";
},4000);
res.redirect("/");
} else {
Article.findById(someIdOrSomething, function(err, someArticle){
res.render("page", {
...
clientError: clientError,
});
});
}
});
})
(3) An empty string. So the string in error.js should be read as an empty string, shouldn't it?
At last, we have the error.EJS
error.ejs
<div id="error" class="error-popup">
<h4>An error occurred.</h4>
<p id="errormessage"><%=clientError%></p>
</div>
One idea might be to have an input instead of the paragraph element above that's disabled as such...
<input id="errormessage" disabled type="text" value="<%=clientError%>">
... and then use Jquery to get the value of the input.
EDIT:
The idea above worked! Here is the new code:
error.js
$("#error").addClass("error-popup");
if($("#errormessage").val() !== "") {
$("#error").fadeIn().addClass("show-error-popup");
setTimeout(function(){
$("#error").fadeOut().removeClass("show-error-popup");
}, 4000);
}
error.ejs
<div id="error" class="error-popup">
<h4>An error occurred</h4>
<input id="errormessage" disabled type="text" value="<%=clientError%>">
</div>
Next step is just to style the input so it doesn't look like a regular input.

KeystoneJS post request does not work

This is my view
form.contact-form(method="post").col-md-12
input(type='hidden', name='action', value='notes.edit' + data.post.id)
.form-group.col-md-12
.form-group.col-md-12
label.text-center Title
input.form-control.input-box(type='text', name='title', value=data.post.title, placeholder='Title' required)
.form-group.col-md-12
label.text-center Content *
.row
.col-md-6
input.form-control.input-box(type='text', name='briefcontent', value=data.post.content.brief, placeholder='brief content')
.col-md-6
input.form-control.input-box(type='text', name='extendedcontent', value=data.post.content.extended placeholder='extended content')
button(type='submit').btn.btn-success Edit Notes
form.contact-form(method="post").col-md-12
This is my post route
view.on('post', { action: 'notes.edit'}, function(next) {
console.log('edit notes')
res.redirect('/')
});
This is my route bindings
// Setup Route Bindings
exports = module.exports = function (app) {
// Views
app.all('/', routes.views.index);
app.get('/blog/:category?', routes.views.blog);
app.get('/blog/post/:post', routes.views.post);
app.get('/gallery', routes.views.gallery);
app.get('/registration', routes.views.registration);
app.post('/registration', routes.views.registration);
app.all('/signin', routes.views.signin);
app.all('/signout', routes.views.signout);
app.all('/contact', routes.views.contact);
app.all('/addnotes', routes.views.addnotes);
app.all('/editnotes/:post', routes.views.editnotes);
app.all('/editnotes', routes.views.editnotes);
The post request does not seem to work at all. I try console.log for the post request but in does not appear in terminal.
You're appending the data.post.id to the value property of your input. Thus, the input value changes to be something that isn't notes.edit. Your POST route is expecting a request with an action value of only notes.edit, therefore the POST request isn't being handled by that route.
In your Pug template:
input(type='hidden', name='action', value='notes.edit')
EDIT:
You have a second form within your form. That may have something to do with it as well. Try removing it.

Save the current user value to use it in HTML

[EDIT] i'm using Meteor
Hi everyone,
i've searched and tried many things but i can't do what i want.
I'm doing a tutorial to do a ToDo list, and when u check a task, i want to put the name of the user who checked the task.
<template name="task">
<li class="{{#if checked}}checked{{/if}}">{{#if checked}}<!-- the name of the user who checked it -->{{/if}}
<!-- the rest isn't useful for my question -->
I've tried with {{currentUser.username}} but when i log with someone else the name change...
There is the JS for the event handler
'click .toggle-checked'() {
// Set the checked property to the opposite of its current value
Meteor.call('tasks.setChecked', this._id, !this.checked);
}
And the JS for the method call
'tasks.setChecked'(taskId, setChecked) {
check(taskId, String);
check(setChecked, Boolean);
Tasks.update(taskId, { $set: { checked: setChecked } });
}
Thank you for the help
If you use {{currentUser.username}} you will always have the data of the logged in user.
To get what you want you need to register the _id of the user who checked the task in your method:
'tasks.setChecked'(taskId, setChecked) {
check(taskId, String);
check(setChecked, Boolean);
// Check that 'setChecked' is true and that the user is logged in,
// Otherwise just update the status
if (setChecked && this.userId) {
Tasks.update(taskId, {
$set: {
checked: setChecked,
userId: this.userId,
}
});
} else {
Tasks.update(taskId, {
$set: {
checked: setChecked,
}
});
}
}
Make sure you update your schema accordingly if you are using one.
Then in your template, retrieve the user data and display it:
// file task.js
import './task.html';
Template.task.helpers({
checkerUser() {
// The template data are those of the current task, check if userId is defined and task is checked
const { userId, checked } = Template.currentData();
/* Or you can do
* const userId = Template.currentData().userId;
* checked = Template.currentData().checked;
*/
if (userId && checked) {
return Meteor.users.findOne({ _id: userId }).profile.username;
}
return null;
}
});
Template.task.events({
'click .toggle-checked'() {
// Set the checked property to the opposite of its current value
Meteor.call('tasks.setChecked', this._id, !this.checked);
}
})
And finally in HTML:
// file task.html
<template name="task">
<li class="{{#if checked}}checked{{/if}}">
{{#if checkerUser}}Completed by {{checkerUser}}{{/if}}
</li>
</template>
Technically, in your method you should also check more in-dept the different situations. For instance, when you uncheck a task, it should remove the userId from the record so that if a non logged-in user checks it again, the name won't be the one of the first user (or you could $unset userId if user is not logged in when setting checked = true)

Meteor easy search and iron-router

I'm using this easy serach for my project https://github.com/matteodem/meteor-easy-search
my search Index
EasySearch.createSearchIndex('searchIndex', {
'collection' : blog,
'field' : ['title', 'tags'],
'limit' : 20,
"use":"mongo-db",
'query' : function (searchString) {
// Default query that will be used for searching
var query = EasySearch.getSearcher(this.use).defaultQuery(this, searchString);
return query;
}
});
and now I have search box and when User enters something and click on enter I want to route to search page
this.route("search",{
path:'/search/:slug',
waitOn:function(){
//here I want the search data to be sent to search page
var query=this.params.slug;
EasySearch.search('searchIndex', query, function (err, data) {
console.log(data);
});
},
data:function(){
}
});
In this router I want the searchIndex data to be sent to search page, how to do this
my click event
'submit .search':function(e){
e.preventDefault();
var quer=$("#query").val();
// Router.go('search');
}
UPDATE
My main question is in the router waiton function how we get the data in callback and send it to the search page?
In your click event handler, you have commented out the line: Router.go('search').
If you write
Router.go('search', {slug: quer})
That would route you to the search page with the query data collected from the page, if that is what you want.

AngularJs two way data binding implementation error with resources

I have a page with a couple of controllers and ng-includes.
Before coming to actual problem i should give a little detail about my AngularJS app
It is on top of node and express
It is communicating with passport running on express for authentication
Now i wanted to have the same page for authenticated as well as unauthenticated users with some differences, lets say the first change was the profile option in nav bar -
ul.nav.navbar-nav.navbar-right(ng-show="identity.isAuthenticated()")
this unordered list has the options to be shown to authenticated users which works fine as soon as i login it is shown which means that the variables identity and the method is isAuthenticate are working fine
But the problem is with the profile form which is a bootstrap modal box -
div.modal.fade(id="profileModal")
div.modal-dialog
div.modal-content
div.modal-header
button.close(data-dismiss="modal") ×
h4 Update Profile
div.modal-body
form.form-horizontal(name="profileForm")
fieldset
.form-group
label.col-md-2.control-label(for="email") Email
.col-md-10
input.form-control(name="email", type="email", placeholder="Email", ng-model="email", required)
.form-group
label.col-md-2.control-label(for="fname") First Name
.col-md-10
input.form-control(name="fname", type="text", placeholder="First Name", ng-model="fname", required)
.form-group
label.col-md-2.control-label(for="lname") Last Name
.col-md-10
input.form-control(name="lname", type="text", placeholder="Last Name", ng-model="lname", required)
.form-group
label.col-md-2.control-label(for="password") Password
.col-md-10
input.form-control(name="password", type="password", placeholder="Password", ng-model="password")
.form-group
div.modal-footer
button.btn.btn-primary(ng-click="update()", ng-disabled="profileForm.$invalid") Submit
|
a.btn.btn-default(data-dismiss="modal") Cancel
Now here i am using a different controller mvProfileCtrl which is as follows -
angular.module('app').controller('mvProfileCtrl', function($scope, mvAuth, mvIdentity, mvNotifier) {
$scope.email = mvIdentity.currentUser.username;
$scope.fname = mvIdentity.currentUser.firstName;
$scope.lname = mvIdentity.currentUser.lastName;
$scope.update = function() {
var newUserData = {
username: $scope.email,
firstName: $scope.fname,
lastName: $scope.lname
}
if($scope.password && $scope.password.length > 0) {
newUserData.password = $scope.password;
}
mvAuth.updateCurrentUser(newUserData).then(function() {
mvNotifier.notify('Your user account has been updated');
}, function(reason) {
mvNotifier.error(reason);
})
}
})
The problem here is as soon as i open the page and the user is not logged in the currentUser is empty object so it doesn't have anything inside it.
So i get this error in console -
TypeError: Cannot read property 'username' of undefined
at new <anonymous> (http://localhost:5000/app/account/mvProfileCtrl.js:2:42)
at invoke (http://localhost:5000/vendor/angular/angular.js:3899:17)
at Object.instantiate (http://localhost:5000/vendor/angular/angular.js:3910:23)
at http://localhost:5000/vendor/angular/angular.js:7164:28
at http://localhost:5000/vendor/angular/angular.js:6566:34
at forEach (http://localhost:5000/vendor/angular/angular.js:327:20)
at nodeLinkFn (http://localhost:5000/vendor/angular/angular.js:6553:11)
at compositeLinkFn (http://localhost:5000/vendor/angular/angular.js:6007:15)
at compositeLinkFn (http://localhost:5000/vendor/angular/angular.js:6014:13)
at publicLinkFn (http://localhost:5000/vendor/angular/angular.js:5916:30)
But as soon as i refresh the page the currentUser has all the required data and thats why there is no error. So, how do i make sure that when the user is authenticated then only bind the $scope variables to this controller or after that only start the controller.
The project source is a bit big thats why i did not include complete detail here, so if i missed out on any detail please suggest
Edit:: another possible bug after removing the mvIdentity and the currentUser part for debugging when i tried this -
angular.module('app').controller('mvProfileCtrl', function($scope, mvAuth, mvNotifier) {
$scope.update = function() {
console.log($scope.email);
var newUserData = {
username: $scope.email,
firstName: $scope.fname,
lastName: $scope.lname
}
if($scope.password && $scope.password.length > 0) {
newUserData.password = $scope.password;
}
mvAuth.updateCurrentUser(newUserData).then(function() {
$('#profileModal').modal('toggle');
mvNotifier.notify('Your user account has been updated');
}, function(reason) {
mvNotifier.error(reason);
})
}
});
It gave me undefined , which suggests that my $scope is not bound to the profileForm object but if i use the watcher and then use the previous code suggested by then i find that the input elements indeed have the currentUsers firstname and lastname which tells that the $scope is attached there. I am not understanding what is going here, please can anyone explain a bit
i am applying the controller like this -
div(ng-include="'/partials/account/authNavBar'",ng-controller="mvProfileCtrl")
where authNavBar has this bootstrap modal code and the profileForm
Use the scope and watchers to your advantage. If you set mvIdentity on your scope with a reference to the service, you can add watchers and only update those properties when currentUser exists.
angular.module('app').controller('mvProfileCtrl', function($scope, mvAuth, mvIdentity, mvNotifier) {
$scope.mvIdentity = mvIdentity;
$scope.$watch('mvIdentity.currentUser', function(currentUser) {
if (!currentUser) return;
$scope.email = currentUser.username;
$scope.fname = currentUser.firstName;
$scope.lname = currentUser.lastName;
});
$scope.update = function() {
var newUserData = {
username: $scope.email,
firstName: $scope.fname,
lastName: $scope.lname
}
if($scope.password && $scope.password.length > 0) {
newUserData.password = $scope.password;
}
mvAuth.updateCurrentUser(newUserData).then(function() {
mvNotifier.notify('Your user account has been updated');
}, function(reason) {
mvNotifier.error(reason);
})
}
});

Categories