FindRecord with Ember and MongoDB - javascript

I have been looking on the web for an answer to this but I have not been able to find one, maybe I am not asking the right questions but here it goes.
So Im building a CRUD webapp using express, mongodb and ember cli, so far I can save data to my mongodb database (/events/new) and display my events (events/all) however I seem to be stuck when accessing an individual event my ideal URL will be /events/:eventname...
I know i'm getting the right result from the server, and I am getting ONLY ONE, however in the ember console, my model shows all my records, plus I cant seem to render the actual content from my model to the screen. IF I happen to go localhost:300/event/XXXX my ember console shows two records the one I want and some sort of dummy one having XXXX as the ID and the rest of the fields are undefined... Do i need to create a new route to look for the single record? or is there a way to use the same route I defined to look for all my records in events/all ? How do i get the data displayed on the screen ? Why do i see all my records when I access /events/eventname ?
Am I following best practices here ?
Express:
app.get('/api/events', function(req,res) {
eventsModel.find({},function(err,docs) {
if(err) {
res.send({error:err});
}
else {
// we only want to display the current events,
// so we filter throught them and return
// only the current ones
var current = [];
var today = new Date();
var tomorrow = today.setDate(today.getDate() - 1);
docs.forEach(function(item){
if(item.date >= tomorrow){
// console.log('new');
current.push(item);
}
});
res.send({events:current});
console.log('/api/events');
}
});
});
app.get('/api/events/:name', function(req, res){
console.log(req.url);
console.log('/api/events/event');
eventsModel.findOne({name: req.params.name},function(err,docs) {
if(err) {
res.send({error:err});
}
else{
res.send({event:docs});
}
});
});
Ember Router.js
Router.map(function() {
this.route('events', function() {
this.route('new');
this.route('update');
this.route('all');
this.route('event', { path: ':name' });
});
});
Ember events.event Routes
export default Ember.Route.extend({
model: function(params) {
this.store.find('event', params.name);
}
});
Ember events.all Template
{{#if model}}
{{#each model as |event| }}
<h2>{{#link-to 'events.event' event.name}}{{event.name}}{{/link-to}}</h2>
<p>{{event.date}}</p>
<p>{{event.location}}</p>
<p>{{event.address}}</p>
<a href={{event.url}}>Read More</a>
{{/each}}
{{else}}
Ember events.event Template
{{#each model as |item|}}
{{item.name}}
{{/each}}
{{outlet}}
My server response when going to /events/eventname is:
{ _id: 55d317d23281dfb23e9ecaa8,
url: 'http://gmail.com',
timestamp: Tue Aug 18 2015 07:32:34 GMT-0400 (EDT),
description: 'Another Address',
address: '123 Street',
long: 34.234,
lat: 12.123,
zip: 'h4t3y5',
city: 'Here',
location: 'Somewhere',
date: Mon Aug 31 2015 20:00:00 GMT-0400 (EDT),
name: 'Two Words',
__v: 0 }
The data is accurate and it changes depending on the post I click on.
Thanks for your help!

Assuming that you're using the RESTAdapter, your server response is in the wrong format. You can read about the format it expects here, but your response should look something like this:
{
"event": {
"id": "55d317d23281dfb23e9ecaa8",
"timestamp": "Tue Aug 18 2015 07:32:34 GMT-0400 (EDT)",
...
}
}

Related

How can I pass content to an element in clients html page?

I'm currently using Node.js to serve a webpage that takes in user inputs which are stored on a mongodb server. The web page also displays specified or all user inputs entered. I'm trying to figure how to pass the user inputs from node.js to the <p> element.
In my node.js file I am responding with the user data as a string like so:
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(stringifyMongoDBCollection(user_data_collection));
response.end();
When I do this, this re-directs the client to display the content as text/plain which I expected. The next step is to update just the content of <p>. How can I do this? I thought about re-serving the entire html content with the new populated <p> but that would make all current user inputs disappear...
The user data would be a mongodb collection array and look like this:
[ { _id: 5dda17065f7e9b64282e7291,
date: 'Sat Nov 23 2019 21:37:10 GMT-0800 (Pacific Standard Time)',
field: '127' },
{ _id: 5dda18ecf330d521a035c444,
date: 'Sat Nov 23 2019 21:45:16 GMT-0800 (Pacific Standard Time)',
field: 125},
{ _id: 5dda1951f330d521a035c445,
date: 'Sat Nov 23 2019 21:46:57 GMT-0800 (Pacific Standard Time)',
field: '111' } ]
You could do something like this.
In Node section
res.status(200).send(stringifyMongoDBCollection(user_data_collection));
Client side
function getContent() {
$.ajax({
url: "https://jsonplaceholder.typicode.com/todos",
success: function (res) {
if (res) {
res = res.slice(0, 5); // limiting data to 5
var val = '';
res.forEach(todo => {
val += '<p><b>Title:</b>' + todo.title + ' <b>Completed:</b> ' + todo.completed + '</p>';
});
}
$("#content").html(val);
},
error: function () {
var val = '<p>Error in loading content</p>'
$("#content").html(val);
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button onclick="getContent()">Get Content</button>
<h1>Todo</h1>
<div id="content"></div>
References
jQuery ajax() Method
Node.js - Response Object
AJAX Introduction

Node.js Express - How does res.render() create html with data when that data isn't passed to it?

I'm using the Getting MEAN With Mongo, Express, Angular, and Node by Simon Holmes and in Chapter 7.2.3 Using the API response data section there is some very weird behavior. It seems that html is being generated from res.render() with data not actually passed to it. When the correct data is being passed, different errors occur depending on how the jade file is being manipulated. The following are locations.js, index.js and locations-list.jade files including terminal output showing an expected JSON. All files are located within the app_server folder seen here https://imgur.com/a/Lfaj828
For those not following along in the book this question only refers to module.exports.homelist = function(req, res) {...} being called, this taking place when there is a request from the browser at localhost:3000/. All other request such as localhost:3000/location behave as expected. The key data mentioned in the previous paragraph is the response from the api request called body inside the request object. The res.render() function uses this data for locations and is called responseBody.
There are several options where code is commented out or uncommented in that produce different outputs as seen below. This takes place in the locations.js file in the renderHompage and module.exports.homelist functions except for Option Four. The following are the options
Removing locations: responseBody from var renderHomepage = function(req, res, responseBody) {...} produces the following html seen in this image https://imgur.com/a/KduEdDG
Including locations: responseBody for var renderHomepage = function(req, res, responseBody) {...} produces the following html seen in this image https://imgur.com/a/Ck4FpFl
Using var renderHomepage = function(req, res) {...} produces the same output as the first option.
Removing lines 24 - 26 (everything involving facilities - see the error message from the second option) from locations-list.jade and using Option Two where responseBody is included produces https://imgur.com/a/bYM8mVj and that list of empty locations is more than a hundred so it can be scrolled down for quite a while.
Terminal output when ever localhost:3000 is requested from browser using Option One, Three and Four. Option two is similar except for GET / 500 806.003 ms - 3396 in the proper place.
GET /api/locations?lng=-122.236353&lat=37.485217 200 19.818 ms - 297
[{"distance":285.97478710956176,"name":"Peet's Coffee","rating":0,"facilities":["hot drinks"," food"," classical music"],"_id":"5ae25bd42d69c6abe46ae307"},{"distance":700.0951415645783,"name":"Philz Coffee","rating":4,"facilities":["hot drinks"," food"," power"],"_id":"5ae25c472d69c6abe46ae30a"}]
GET / 304 128.077 ms - -
GET /bootstrap/css/amelia.bootstrap.css 304 3.148 ms - -
GET /stylesheets/style.css 304 4.557 ms - -
GET /javascripts/jquery-1.12.4.min.js 304 8.150 ms - -
GET /bootstrap/js/bootstrap.min.js 304 6.275 ms - -
GET /bootstrap/fonts/glyphicons-halflings-regular.woff 304 0.551 ms - -
locations.js
var request = require('request');
var apiOptions = {
server : "http://localhost:3000"
};
if (process.env.NODE_ENV === 'production'){
apiOptions.server = "https://example.herokuapp.com/"; // not actual location
}
// var renderHomepage = function(req, res) { // uncomment for Option Three
// res.render('locations-list', {
// title: 'Loc8r - find a place to work with wifi',
// pageHeader: {
// title: 'Loc8r',
// strapline: 'Find places to work with wifi near you!'
// },
// sidebar: "Looking for wifi and a seat? Loc8r helps you find places to work when out and about. Perhaps with coffee, cake or a pint? Let Loc8r help you find the place you're looking for.",
// });
// };
var renderHomepage = function(req, res, responseBody) { // comment out for Option Three
res.render('locations-list', {
title: 'Loc8r - find a place to work with wifi',
pageHeader: {
title: 'Loc8r',
strapline: 'Find places to work with wifi near you!'
},
sidebar: "Looking for wifi and a seat? Loc8r helps you find places to work when out and about. Perhaps with coffee, cake or a pint? Let Loc8r help you find the place you're looking for.",
// locations: responseBody // comment out for Option One, uncomment out for Option Two
});
};
/* GET 'home' page */
module.exports.homelist = function(req, res) {
var requestOptions, path;
path = '/api/locations';
requestOptions = {
url: apiOptions.server + path,
method : "GET",
JSON : {},
qs : {
lng : -122.236353,
lat : 37.485217
}
};
request(
requestOptions,
function(err, response, body){
console.log(body);
// renderHomepage(req, res); // use for Option Three
renderHomepage(req, res, body); // use for Option One and Two
}
);
};
/* GET 'Location info' page */
module.exports.locationInfo = function(req, res) {
res.render('location-info', {
title: 'Location info',
name: 'Starcups',
address: '125 High Street, Reading, RG6 1PS',
rating: 5,
facilities: ['Hot drinks', 'Premium wifi'],
distance: '109m',
description: " Simon's cafe is on Loc8r because it has accessible wifi and space to sit down with your laptop and get some work dont.",
times: [
"Monday - Friday : 7:00am - 7:00pm",
"Saturday : 8:00am - 5:00pm",
"Sunday : closed"
],
reviews: [{
name: "Simon Holmes",
description: "What a great place. I can't say enough good things about it.",
timestamp: "16 Febuary 2014",
rating: 3
},{
name: "Judy Holmes",
description: "It was okay. Coffee wasn't great, but the wifi was fast.",
timestamp: "26 January 2015",
rating: 3
},{
name: "Simon Cramp",
description: "It was okay. Coffee wasn't great, but the wifi was fast.",
timestamp: "6 September 2012",
rating: 3
},{
name: "Simoolmes",
description: "What a great place. I can't say enough good things about it.",
timestamp: "14 June 2013",
rating: 3
}]
});
};
/* GET 'Add review' page */
module.exports.addReview = function(req, res) {
res.render('location-review-form', {
title: 'Add review',
name: 'Starcups'
});
};
locations-list.jade
extends layout
include _includes/sharedHTMLfunctions.jade
block content
#banner.page-header
.row
.col-lg-6
h1= pageHeader.title
small #{pageHeader.strapline}
.row
.col-xs-12.col-sm-8
.error= message
.row.list-group
each location in locations
.col-xs-12.list-group-item
h4
a(href="/location/#{location._id}")= location.name
small
+outputRating(location.rating)
span.badge.pull-right.badge-default= location.distance
p.address= location.address
p
each facility in location.facilities
span.label.label-warning= facility
|
.col-xs.12.col-sm-4
p.lead= sidebar
index.js
var express = require('express');
var router = express.Router();
var ctrlLocations = require('../controllers/locations');
var ctrlOthers = require('../controllers/others');
/* Location pages */
router.get('/', ctrlLocations.homelist);
router.get('/location', ctrlLocations.locationInfo);
router.get('/location/review/new', ctrlLocations.addReview);
/* Other pages */
router.get('/about', ctrlOthers.about);
module.exports = router;
So the big question is how the html is displaying locations that aren't being passed into res.render() as can be seen in the first and third options? Option One and Three are obviously very similar but I just had to double check because it just seems so weird. I thought it was possible that the data was being cached somewhere so I changed the latitude longitude numbers in the requestOptions the html shows that change for Option One and Three
Not shown here I've replaced responseBody with hardcoded sample location data from earlier in the book similar to what is seen in module.exports.locationInfo = function(req, res) {...} and everything looked fine.

Extjs Store to Array get object property

I am working on a Rally App I am using a Store to pull data from the Portfolio/Feature Modle.
This is working as expected. I want to convert what the lisener returns to an arry
The issue I am facing is the array is just retruning the Object and I need the data from the Object Property.
Results of the array look like this
["F1870", "25343 - some project name ", "", Object, Object, Mon Apr 27 2015 02:00:00 GMT-0400 (Eastern Daylight Time)]
The first Objects Value should be John Smith.
John Smith Sits in the propery of the
Object
0: "F1870"
1: "25343 - Some "
2: ""
3: Object
_p: "0"_ref: "blah Balh"
_refObjectName: "John Smith"
_refObjectUUID: "blah blah"
_type:
Owner[_refObjectName] what i need to get and i am lost.
******Edited To add more detials****
The store returns values look like this
data: Object
FormattedID:F1223
Name: Some project
Description: Blah blah blah
Owner: Object
_p:
_ref:
_refObjectName: John Smith
I need the array to return
FormattedID:F1223
Name: Some project
Description: Blah blah blah
Owner: John Smith
Here is the code i have so far.
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function () {
console.log("App Launched")
//App Calls the portfolio feature data store
this._getfeaturedatastore();
},
//Get the portfolio feature data from Rally
_getfeaturedatastore: function(){
var getfeaturedata = Ext.create('Rally.data.wsapi.Store', {
model: 'PortfolioItem/Feature',
autoLoad: true,
//Create Fillter for the Store
filters: [
{
property: 'State.Name',
value: 'Story Definition',
}
],
listeners: {
load: function(getfeaturedatastore, getfeaturedatadata, success) {
console.log("Got Feature Data Woot",getfeaturedatastore, getfeaturedatadata, success)
this._displayFeatureCard(getfeaturedata);
},
scope: this
},
fetch: ['State', 'Name', 'Description', 'Owner', 'Parent','PlannedStartDate','FormattedID','Tags']
});
},
_displayFeatureCard: function(getfeaturedata){
var MAX_NAME_LEN = 115;
var name,i,theMarkup, description, owner, parent, plannedstartdate, formattedid;
var data =[];
getfeaturedata.each(function(record){
var recordArray = [
record.get("FormattedID"),
record.get("Name"),
record.get("Description"),
record.get("Owner"),
record.get("Parent"),
record.get("PlannedStartDate")
];
data.push(recordArray);
console.log(recordArray)
});
The clues as to what data you have access to and how to do it, is accessible via the WebServices documentation ( go to the help link accessible via your avatar in the top right hand corner)
Any artifact that is held in the Rally database can come back to you looking like: a string, a number, an object or a collection (of objects).
In the case of the 'Owner' of a portfolio item, it is an object of type User. The contents of the object describe the owner and not just providing the name. As it is an object, you have to do record.get("Owner") to get the object and then do record.get("Owner").Name to get the name of the owner.
FormattedID comes back as a string, so you just need to do record.get("FormattedID") to get the text.
You can get all the records from the store using the getRange method and then get all the data from each record using the getData method.
listeners: {
load: function(store) {
var data = _.map(store.getRange(), function(record) {
return record.getData();
});
var feature1 = data[0],
ownerName = feature1.Owner._refObjectName;
}
}
This example also uses the lodash map function to reduce the lines of code necessary. At this point data will be an array of plain old javascript objects with the data from the store.

meteor not printing any data

I have the following application and when I render data I am getting null or no data for the {{video}} tag even though data exists in the collection Can anyone help me find the mistake
routes.js
Router.route('/videos/:id', function () {
var item = Videos.find({_id: this.params._id});
this.render('VideoItem', { data:item});
});
video.html
<template name="VideoItem">
<div class="container">
<h3> Video Information</h3>
{{video}}
</div>
</template>
video object when Videos.find().fetch()
_id: "FEXm65hwZ9QWXFSY8"
created_at: Mon May 18 2015 14:22:59 GMT+0200 (CEST)
duration: 10000
video: "temp"
videourl: "http://google.com"
__proto__: Object
find returns a cursor. That would work if you were iterating over a set of videos with #each. In your case you want a specific video so you'd need to use findOne like this:
Router.route('/videos/:_id', function () {
this.render('VideoItem', {
data: function () {
return Videos.findOne(this.params._id);
}
});
});

Remove unverified Meteor users after certain time period?

I'm working on a Meteor app and I want to rid the app of the antiquated "Create New Account" then "Check Your Email to Verify Account" workflow. I want users to sign up with an email (not a username) and immediately have some access to the app, a verification email will fire, and then they can verify at a later time to get full access. So I would be calling Accounts.createUser immediately and always (as long as their email isn't already taken.)
How would I go about "taking out the garbage" of any email-based accounts that get created but are never verified? Like if I wanted to delete an unverified account after 3 days for example?
The only way I can think of is to do a really long Meteor.setTimeout command in the Accounts.onCreateUser hook that would check if the accounts email is verified after three days (which is 259,200,000 ms BTW). Is this practical? Will it work? Is there another method in Meteor to do something like this? That isn't dependent on user actions. I don't want to do this when the user logs in because a user could create an account with a bad email and then never log in again, but a future user with that email would then be locked out.
Does Meteor have any type of "server rules" that would fire every so often to run checks? Like setup some kind of nightly maintenance function/routine? Also, is it possible to remove a User like this? I was reading another article that said something about not being able to remove users through the API. I definitely need to be able to do that because the whole point is to make that email/account available to a user that actually owns that email.
If I have to I can go to the "force verification" methodology, but I see other sites doing the above and I like it much better. It's also way slicker on mobile.
EDIT: I was just looking at the Meteor docs and sending a "verification email" requires a userId which means you have to create a user no matter what - Accounts.sendVerificationEmail(userId, [email]). So I guess no matter what a user with a bad email could get created. So it would be nice to know how to do the above.
You can use a simple cron with Meteor.setInterval.
I wouldn't advise using Meteor.setTimeout with the onCreateUser hook. This is because if you're server is restarted/has a crash/you update the code within this 3 day period the snippet won't run.
Server Side Code:
Meteor.setInterval(function() {
// new Date must always be new Date()
var three_days_ago = new Date(new Date().getTime() - (3600000*72))
Meteor.users.find({
createdAt: {
$lte: three_days_ago //Users created less than 3 days ago
},
'emails.0.verified': false
}).forEach(function(user) {
//Do action with 'user' that has not verified email for 3 days
});
}, 3600000);
The above code runs every hour, checking for users that were created more than 3 days ago (72 hours) that have not yet verified their first email address.
To mix and update the old answers (#Akshat wrote new Date(new Date.getTime() - (3600000*72)) but it's new Date(new Date().getTime() - (3600000*72)) ), in a cron job way every day
Install synced-cron
meteor add percolate:synced-cron
On server, in a cron.js
import { SyncedCron } from 'meteor/percolate:synced-cron';
import { deleteUnverifiedUsers } from './delete-unverifiedUsers.js';
SyncedCron.config({ log: false, utc: true });
SyncedCron.add({
name: 'Check verified Users',
schedule(parser) {
//return parser.text('every 10 seconds');
//return parser.text('every 1 hour');
return parser.text('every 24 hours');
},
job() {
deleteUnverifiedUsers();
}
});
SyncedCron.start();
On server in the task file (here delete-unverifiedUsers.js)
import { Meteor } from 'meteor/meteor';
// Set since variable in milliseconds*hours : ex 1h = 3600000*1
var one_day_ago = new Date(new Date().getTime() - (3600000*24))
Meteor.users.find({
// condition #1: users created since variable ago
createdAt: {
$lte: one_day_ago,
},
// condition #2: who have not verified their mail
'emails.0.verified': false
}).forEach(function(user) {
// Delete the users who match the 2 conditions
return Meteor.users.remove({_id: user._id})
});
Use percolatestudio:synced-cron:
meteor add percolatestudio:synced-cron
Then in your Javascript on the server:
if (Meteor.isServer) {
Meteor.startup(function () {
SyncedCron.add({
name: 'Remove unverified users',
schedule: function(parser) {
// parser is a later.parse object
return parser.text('every Wednesday at 12am');
},
job: function() {
//TODO: implement RemoveUnverifiedUsers function
var numUsersRemoved = RemoveUnverifiedUsers();
return numUsersRemoved;
}
});
// start the cron daemon
SyncedCron.start();
}
}
Edit #1:
The Meteor user will have an email object with verified:false in the Meteor.users collection. This part of the meteor docs provides the following example:
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId()
username: "cool_kid_13", // unique name
emails: [
// each email address can only belong to one user.
{ address: "cool#example.com", verified: true },
{ address: "another#different.com", verified: false }
],
createdAt: Wed Aug 21 2013 15:16:52 GMT-0700 (PDT),
profile: {
// The profile is writable by the user by default.
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}

Categories

Resources