MeanJS stack, debugging Angular error: fnPtr is not a function - javascript

I'm learning the MeanJS stack (Mongo, Express, Angular, Node) and writing a simple todo webapp. I can list all todos and create a new one. When I 'edit' a todo, I'm encountering this error:
TypeError: fnPtr is not a function
I'm assuming I have some naming or syntax wrong (based on this and this SO question)
The problem is I don't know where to look for wrong naming or bad syntax since the file structure is pretty large ('app' and 'public' maps are 484 files). I don't get into the todo.client.controller nor 'todo.server.controller' update function, as there is a console log there that doesn't get printed. The edit button is a submit input, but I don't know where it goes next.
Code:
Piece form 'edit' page
<div class="form-group">
<input type="submit" value="Update" class="btn btn-default">
</div>
Client controller:
// Update existing Todo
$scope.update = function() {
console.log('update');
var todo = $scope.todo;
todo.$update(function() {
$location.path('todos/' + todo._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
Server controller:
/**Update a Todo*/
exports.update = function(req, res) {
console.log('todo controller');
var todo = req.todo;
todo = _.extend(todo, req.body);
Todo.update({_id: req.body._id}, req.body, function(err, update) {
//todo.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(todo);
}
});
};
Anyone experienced with the MeanJS stack or Angular that could point me in a direction to start debugging?

You have not added ng-click to the "Update" submit button.
Change your code to this:
<div class="form-group">
<input type="submit" value="Update" ng-click="update()" class="btn btn-default">
</div>
As for the fnPtr error, add your full stack trace so that it can be analyzed.
Also check if your code has closed all the brackets, you are not using same name for 2 variables and typos.

Related

How do nested pages/mongoose schemas work? Getting errors

I am making a site that is a mock forum where the user clicks on a forum topic, goes to a unique .ejs page with forums, and then goes to another unique .ejs page where the forum is, where both pages are altered based on what the user fills into a Form on the previous page. The first step worked, but I keep getting either a TypeError "can't read property name of null" or a reference error (depending on what object info I type into the .ejs page) when I end up clicking on a link that would go from the "forum topic" page to the "forum". Data is being added to the database and I created corresponding mongoose schemas that are linked to each other and the main app so I'm not sure what the issue is.
I'm using this with express and mongodb/mongoose, the page that's getting the type error is a .ejs page. I'm assuming the main issue is in one of the routing blocks in the main app document, but it may not be and it may be some other concept or page configuration I'm not getting. What is the deal with nested schemas and pages for those who are more experienced with this?
Here is the page itself that has the error:
TypeError: /Users/roberts4/project_4/views/forum.ejs:4
2|
3| <nav class = "container">
>> 4| Welcome to Forum/ <%= forum.name %>
5| Reply
6| <a class = "btn btn-primary btn-sm" href="/main/<%= forumtopic._id
%>/<%= forum._id %>/reply/new">Add New Forum</a>
7|
Cannot read property 'name' of null
The post function that you will see has has the "forum" created inside of the "forumtopic" id, unlike for what I did when posting the "forumtopic" itself.I tried adding req.body variables but it seems like that's only used if you're not posting them to a different page.
///there is code above this for forumtopic that's similar, except the
////.post block has the .create by itself and not inside of .findById
app.get("/main/:id/forum", function(req, res){
Forum.find({}, function(err, allForums){
if(err){
console.log(err);
} else {
res.render("forumtopic", {forums: allForums});
}
})
});
app.post("/main/:id/forum", function(req, res){
ForumTopic.findById(req.params.id, function(err, forumtopic){
if(err){
console.log(err);
res.redirect("/main");
} else {
//console.log(req,body.comment);
Forum.create(req.body.forum, function(err, forum){
if(err){
console.log(err);
} else {
forumtopic.forums.push(forum);
forumtopic.save();
res.redirect('/main/' +
forumtopic._id);
}
})
}
})
});
app.get("/main/:id/forum/new", function(req, res){
ForumTopic.findById(req.params.id, function(err, forumtopic){
if(err){
console.log(err);
} else {
res.render("newforum", {forumtopic: forumtopic});
}
})
});
app.get("/main/:id/:forumid", function(req, res) {
Forum.findById(req.params.id).populate("replies").exec(function(err,
foundForum) {
if(err){
console.log(err);
} else {
console.log(foundForum);
res.render("forum", {forum: foundForum});
}
})
});
///there is code below this that's not accessed yet because of the error
Getting a TypeError 'cannot read property name of null', expecting
a page that displays information based off of node in the app.js.

Angular 4 and Nodejs http post request implementation error

I created small test application using Angular 4 and Nodejs. I want to insert a text field value to the database either Mongo or mysql.
Following files I created server.js for running nodejs, Created server>router folder with another file api.js and Angular 4 user component created.
What i did code shown below
user.component.html
<form (submit)="addHobby(hobbies,hobby.value)">
<input type="text" #hobby>
<input type="submit" value="Submit">
</form>
user.component.ts
addHobby(arrHob,hobby) {
if(arrHob.indexOf(hobby) == -1) {
this.hobbies.unshift(hobby);
this.dataService.postHobby(hobby).subscribe((posts) => {
console.log(posts);
})
}
return false;
}
Services folder contain data.service.ts
postHobby(hobby) {
return this.http.post('/api/insertHobby',hobby).map(res => res.json().data);
}
server router folder contain api.js
router.post('/insertHobby', (req, res) => {
console.log("Welcome to post method");
console.log(req.body.data);
})
When form submitting i'm getting output as only welcome to post method
req.body.data i'm getting as *'Undefined'* How to resolve this issue. Any way thanks to everyone..
since you are not passing the variable as object , you need to pass it in object format as below :
postHobby(hobby) {
return this.http.post('/api/insertHobby',{hobby: hobby}).map(res => res.json().data);
}

How do I call inputs and buttons in my EmberJS acceptance tests?

This is maddening, as there is little to no help on google/the internet for this. https://guides.emberjs.com/v2.4.0/testing/acceptance/ is also not very helpful, even though it tries. I am basically learning this from scratch. I know a modest amount of HTML, handlebars, and javascript, but emphasis on the modest.
Here is my template, much of it is copied code from my architect's design who doesn't have time to help me :
<form {{action "login" on="submit"}} class="col-md-4 col-md-offset-4">
{{#if loginFailed}}
<div class="alert">Invalid username or password.</div>
{{/if}}
<div class="form-group">
<label for="inputEmail">Email address</label>
{{input value=username type="email" class="form-control" id="inputEmail1" placeholder="Email"}}
</div>
<div class="form-group">
<label for="inputPassword">Password</label>
{{input value=password type="password" class="form-control" id="inputPassword" placeholder="Password"}}
</div>
<button type="submit" class="btn btn-default" disabled={{isProcessing}}>Log in!</button>
</form>
Note the application runs correctly (I'm able to generate a login screen which connects to my local database, and I am able to log in correctly when the credentials are correct and not login when they aren't).
There is also a large .js file for the route which has an ajax call and corresponding promise from it, which I can sort of understand, but bottom line, it works :
import Ember from 'ember';
import ajax from 'ic-ajax';
export default Ember.Route.extend({
loginFailed: false,
isProcessing: false,
beforeModel: function(){
this.store.unloadAll('security-setting');
this.store.unloadAll('org');
var user = this.modelFor('application').user;
user.setProperties({email: '', auth: ''});
},
actions: {
login: function() {
this.setProperties({
loginFailed: false,
isProcessing: true
});
var _this = this;
ajax({
url: _this.modelFor('application').url + '/signin.json/',
type: 'post',
data: {session: {email: this.controller.get("username"), password: this.controller.get("password")}},
}).then(
function(result) {
// proprietary stuff, it all works
},
function(error){
alert(error.jqXHR.responseText);
this.set('isProcessing', false);
_this.set("loginFailed", true);
}
);
},
},
reset: function() {
this.set('isProcessing', false);
this.controller.set('password', '');
this.controller.set('username', '');
}
});
Here is the acceptance test I am trying to write :
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'ember-super-user/tests/helpers/start-app';
module('Acceptance | login', {
beforeEach: function() {
this.application = startApp();
},
afterEach: function() {
Ember.run(this.application, 'destroy');
}
});
test('visiting /login and fail a login attempt', function(assert) {
visit('/login');
fillIn('input.username', 'insert-username-here');
fillIn('input.password', 'insert-password-here');
click('button.submit');
// I know this assert is wrong but I haven't even gotten this far yet so I'm // not thinking about it; basically what happens is a popup appears and says // wrong-username-or-password-etc
andThen(function() {
assert.equal(currentURL(), '/login');
});
});
Execution dies on the fillIn lines of code. I really don't know what to do here, I've tried all combinations of 'input.username', 'input.inputEmail1', 'input.inputEmail'... I'm just not sure what I'm supposed to do, at all. I'm also pretty sure that 'button.submit' will not just magically work either. Then, I know I'll be even more lost when I try to fill in the andThen promise to acknowledge the fact that a popup appeared saying wrong-password-etc.
Please help; thanks very much for your time.
EDIT: I have been able to fix the fillIn parts of the test, but the click (probably the click, anyway, as the error messages are unclear as to which line is the problem) is producing some errors that I am unable to diagnose. Error messages appear in the output of the QUnit test suites that don't make sense to me --
TypeError: Cannot read property 'set' of undefined# 4286 ms
Expected:
true
Result:
false
Diff:
trufalse
at http://localhost:7357/assets/test-support.js:3592:13
at exports.default._emberTestingAdaptersAdapter.default.extend.exception (http://localhost:7357/assets/vendor.js:52460:7)
at onerrorDefault (http://localhost:7357/assets/vendor.js:43162:24)
at Object.exports.default.trigger (http://localhost:7357/assets/vendor.js:67346:11)
at Promise._onerror (http://localhost:7357/assets/vendor.js:68312:22)
at publishRejection (http://localhost:7357/assets/vendor.js:66619:15)
EDIT 2: The latest change for changing 'button' to 'submit' still doesn't work. Current error message :
Error: Element input[type='submit'] not found.# 166 ms
Expected:
true
Result:
false
Diff:
trufalse
Source:
at http://localhost:7357/assets/test-support.js:3592:13
at exports.default._emberTestingAdaptersAdapter.default.extend.exception (http://localhost:7357/assets/vendor.js:52460:7)
at onerrorDefault (http://localhost:7357/assets/vendor.js:43162:24)
at Object.exports.default.trigger (http://localhost:7357/assets/vendor.js:67346:11)
at Promise._onerror (http://localhost:7357/assets/vendor.js:68312:22)
at publishRejection (http://localhost:7357/assets/vendor.js:66619:15)
Your selector for each input is wrong. Since you gave each one an id, you can do this:
fillIn('#inputEmail1', 'insert-username-here');
fillIn('#inputPassword', 'insert-password-here');
Remember that you are using CSS selectors for the first argument of fillIn, IDs use # prefix and classes use ..
For the submit button, you did not add a class or ID, but if it is the only submit button on the page you can target it like this:
click('button[type="submit"]');
The fillIn and click functions takes in css selectors. so for example, clicking on the submit would look like,
click("input[type='submit']");

Posting Input value as param to web service API in Angular

I have started learning Angular and I'm stuck on something that should be trivial. I'm from a Rails background and created a very simple Rails web-service that manages a MailingList. Currently 2x API endpoints exist in my web-service:
/api/v1/mailing_lists [GET]
/api/v1/mailing_lists [POST]
The POST endpoint requires a valid email as a PARAM in the API call. And this is where I'm stuck. How do I pass this param to my API in Angular? I've tried using solutions from around the web but I'm just not getting it right. Here's what I have thus far:
In my services.js:
angular.module('webFrontendApp.services', []).factory('MailingList', function($resource) {
var data = $resource(
'http://localhost:3000/api/v1/mailing_lists.json',
{},
{
// 'get': { method:'GET', cache: false},
'query': { method:'GET', cache: false, isArray:true },
'save': {method: 'POST', cache: false }
});
return data;
});
In app.js
....
.when('/list/new', {
templateUrl: 'views/list-new.html',
controller: 'MailingListCtrl'
})
....
Controller as mailinglist.js
angular.module('webFrontendApp.controllers', [])
.controller('MailingListCtrl', function($scope, MailingList) {
// Get all posts
$scope.mailing_lists = MailingList.query();
// Our form data for creating a new post with ng-model
$scope.memberData = {};
$scope.newMember = function() {
var member = new MailingList($scope.memberData);
member.$save();
};
});
The form looks like this:
<form role="form" ng-submit="newMember()">
<div class="row">
<div class="input-group">
<input type="text" ng-model="memberData.email" placeholder="New mailing list member" class="form-control">
<span class="input-group-btn">
<input type="submit" class="btn btn-primary" value="Add">
</span>
</div>
</div>
</form>
When I submit the form, nothing seems to happen, but I am receiving a 405 error response from my web service. After looking into my Web Service logs, I can see that a POST request was received but no params were passed and thus it was rejected.
Any help will be greatly appreciated!
PS. I have made sure to Enable CORS on my Rails app.
Pass params inside $save method, pass post_data inside MailingList, so basically this should work
$scope.newMember = function() {
var member = new MailingList();
member.$save({email: $scope.memberData.email});
};

Meteor entering route when calling a method

Can someone explain to me why when I have collections code inside router will cause the route to be called when a method is called?
Consider the following code:
home.html
<template name="home">
{{ duplicate }}
<form>
<input type="text" name="test" value="somevalue">
<input type="submit" value="Submit">
</form>
</template>
script.js
Template.home.events({
'submit form': function (e) {
e.preventDefault();
console.log('Enter Meteor call');
Meteor.call('createDoc', { 'test': e.target.test.value });
}
});
route.js
Router.onBeforeAction(function () {
console.log('Enter onBeforeAction');
$('#loading').show();
this.next();
});
Router.route('/', function () {
console.log('Enter action');
var foo = collection.findOne({ test: 'somevalue' }) ? 'true' : 'false';
this.render('home', {
data: {
'duplicate' : foo
}
});
Template.home.rendered = function () {
console.log('Enter rendered');
$('#loading').hide();
};
});
methods.js
collection = new Mongo.Collection('collection');
Meteor.methods({
createDoc: function (data) {
console.log('Enter createDoc');
collection.insert(data);
}
});
The problem is that if I press submit on the form, after the method is called the router will activate, even though e.preventDefault() presents. The console log shows this behaviour clearly:
"Enter Meteor call" script.js:4:3
"Enter createDoc" methods.js:5:3
"Enter onBeforeAction" routes.js:2:2
"Enter action" routes.js:8:2
"Enter onBeforeAction" routes.js:2:2
"Enter action" routes.js:8:2
Furthermore, you can see that the router is called twice and that it never enters Template.home.rendered. This causes the loading div to appear and never leaves. I can confirm that data are being inserted correctly.
If I remove collection.findOne() in routes.js, however, this behaviour will disappear and everything works as expected.
Questions
Why is the route being called only when I have collection.findOne() inside the route?
Why collection.findOne({ test: 'somevalue' }) never returns anything inside the route? (I know how I can get around this by using Session variables and helpers in script.js, but I want to know exactly why)
This is causing a lot of unexpected behaviour in my app. Thank you very much in advance.
As answered by others the problem you have arises from the fact that Meteor will reactively re-run code that runs in a reactive context, if and only if, that code issues a call to a reactive data source.
In your case, the call to findOne is a call to a reactive data source and the context in Router.route('/', function () { // context }); is a reactive context.
There are two important tools that let you control this behavior: one is good design. Be aware of the reactivity and try to design your code around it.
The other is checking Tracker.active and using Tracker.nonreactive to avoid reactivity inside a reactive data context.
This should answer your first question. As to why your findOne query never finds anything: have you published the data from the server to the client? Please check out Publish-Subscribe. You basically need:
// on the server
Meteor.publish('myPublication', function(author) {
return collection.find();
});
// on the client
Meteor.subscribe('myPublication');
The call to collection.findOne() inside the route is listening to any new changes on the database, every time text is saved on the database the query is run.
A possible solution:
Router.js
Router.onBeforeAction(function () {
console.log('Enter onBeforeAction');
$('#loading').show();
this.next();
});
Router.route('/', {
template: 'home',
waitOn: function() {
return Meteor.subscribe('collection');
},
data: function() {
var foo = collection.findOne({ test: 'somevalue' }) ? 'true' : 'false';
return {
'duplicate': foo
};
},
action: function() {
this.render();
}
});
And a publish file on server/publish.js
Meteor.publish('collection', function () {
return collection.find();
});
I hope this can help you solving your problem.
Best.

Categories

Resources