how to passing few variables from mongodb query to jade tpl - node.js

//posts
var docs, cats;
var db = req.db;
var catcollection = db.get('catcollection');
var postcollection = db.get('postcollection');
// find all post
postcollection.find({},{},function(e,docs){
console.log('posts ---> '+util.inspect(docs));
}); // end find all post
catcollection.find({},{},function(e,catss){
cats=catss;
console.log('cats --> '+util.inspect(cats)); //<<<---- write objects from mongo
}); // end find all cats for select
res.render('newpost', {
posts : docs, cats:cats, title: 'Add New post'});
}); **//<<<---it didn't passing the cats:cats and post vars to jade **
jade template
extends layout
block content
h1= title
form#formAddPost(name="addpost",method="post",action="/addpost")
input#inputPostTitle(type="text", placeholder="posttitle", name="posttitle")
textarea#inputPostTitle(placeholder="postdesc", name="postdesc")
textarea#inputPostTitle(placeholder="posttext", name="posttext")
select#selectPostCats(placeholder="postdesc", name="posttext")
each cat, i in cats
option(value="#{cat._id}") #{cat.titlecat}
button#btnSubmit(type="submit") submit
ul
each post, i in posts
li= i+" "
a(href="/editpst/#{post._id}")=#{post.title}
I get this error message in jade tpl
Cannot read property 'length' of undefined
but if I wrote
catcollection.find({},{},function(e,catss){
cats=catss;
console.log('cats --> '+util.inspect(cats));
**res.render('newpost', {
cats:cats, title: 'Add New post'});**
}); // end find all cats for select
It passing category list to jade , but i can't pass post list to jade.
How to passing few variables (posts and cats ) to jade tpl?

Both of the .finds execute asynchronously so you don't know when (or if) either one will complete. That is to say you need to wait until both of the callbacks are called before you attempt to render the template.
The simplest way in your current implementation would be to nest everything:
postcollection.find({},{},function(e,docs){
// handle errors
catcollection.find({},{},function(e,cats){
res.render('newpost', {
posts : docs, cats:cats, title: 'Add New post'});
});
});
});
However you can do these queries simultaneously because they do not depend on each other. The best way to do that is to probably use promises.
Promise.all([postcollection.find(), catcollection.find()])
.then(function (docs, cats) {
res.render('newpost', {
posts : docs, cats:cats, title: 'Add New post'});
});
});
This assumes that .find returns a promise. It should for the current Mongo driver.

Related

Dynamic URL with mongo method

Regarding these post : Mongoose prototype : how to insert an url dynamically?
I'm looking to do pretty much the same (URI dynamically insert).
The solution provided is good but I do not understand some things...
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PicturesSchema = new Schema({
album : { type : String, required : true, trim : true },
pictures : { type : Array, required : false, trim : true }
});
PicturesSchema.virtual('pictureUrls').get(function() {
return this.pictures.map(function(picture) {
return 'https://s3.amazonaws.com/xxxxx/'+ picture;
});
});
var Pictures = mongoose.model('Pictures', PicturesSchema);
// Demo:
var pictures = new Pictures({
album : 'album1',
pictures : [
'1434536659272.jpg',
'1434536656464.jpg',
'1434535467767.jpg'
]
});
console.log( pictures.getPics() );
The given solution injects a "virtual field" in addition to retaining the "pictures" field with the name of the images (without URL).
How to retain only the field "PicturesURL" containing the full URL of the image without displaying a redundancy with the "Pictures" field?
How to retrieve and display the JSON format query results as it knowing that data.ToJson returns an error: has no method 'toJson'?
Pictures.find().exec(function(err, data){
if(err){
console.log(err);
}
console.log('%j', data.toJSON({ virtuals : true }) );
});
A strategy to store only the picture url without the picture would be to create a Mongoose middleware on save.
You could then populate the picture field with the name of the picture, and the full url will be the one stored in the database and later retrieved.
For example:
PictureSchema.pre('save', function (next){
if (this.isModified(pictureUrls){
this.pictureUrls = this.pictureUrls.map(function(picture){
return 'https://s3.amazonaws.com/xxxxx/'+ picture;
})
}
});
With this solution, every time you modify the pictures array, it will go over every element of the array and add the s3 path.
Since it won't be a virtual, it will be easier for you to use the model with event using toObject or toJson.

Express js Mongoose alternative to MySQL % Wildcard

I've been reading up and tried a few different code snippets that others have had success with, but I can't seem to get it to work.
What I'd like is for users to search using only part of the term i.e pe for 'peter'. I'd like to have a wildcard on the search term.
My code so far, which isn't working:
router.get('/:callsign', function(req,res){
var search = req.params.callsign;
var term = escape(search);
term = term.toUpperCase();
if(search=="*" || search==""){
res.redirect("/");
}
User.find({'callsign' : new RegExp('^'+term+'$', "i") }, function(err, callsign){
if(err)
{
console.log('No user found'+err);
req.flash('message','Sorry, something went wrong. Try again.');
res.render('callSearchResults'),{
message: req.flash('message'),
title: 'Sorry, no results'
}
}
if(callsign){
console.log('Callsign:'+callsign+term);
res.render('callSearchResults',{
call: callsign,
title: 'You searched for '+search,
query: term
});
}else{
console.log('No entries found'+search);
}
});
});
Also, 'callsign' callback is constantly true - even when there are no results!
You are using an RegExp for this search. Literal ^ mean that pattern must be at the beggining of string, and $ that at the end. If you want just to match part you don't need to add them, so example below:
new RegExp(term, "i")
Also there is a good mechanism of full-text search build-in mongo. You can read about them from official docs.
About queries in mongoose, when there is now object and checking in callback. The returned object is Array of documents, and in JS empty array is casted to true statement. Instead check lenght array, like this:
if(callsign.length > 0) {
// Logic
} else {
// Nothing found
}

Sailjs: how do I save collections with particular fields to the mongo db via waterline

I'm using no FeedParser to loop through a particular XML feed -
I'm getting the required data I want in my console using the following:
console.log('Got article: %s', item.title);
console.log('Got url %s', item.link);
Here is what I want to do:
Save the title + links generated as a document in the collection with the same name as the controller name
I've also setup a cron job so I want to make sure that if the title exists in that collection, the loop should continue to the next article in the RSS feed
Here is what I tried writing below my console.log statements mentioned above and the loop broke after executing once (Buzzfeed is the name of the model so appropriately 'buzzfeed' is the name of the collection where I want the data to be stored)
Buzzfeed.findOrCreate()
.populate('title')
.populate('url')
.exec(function (err, title, url){
buzzfeed[1].title.add(item.title );
buzzfeed[1].url = ( item.url );
buzzfeed[1].save(function (err) {
});
});
Addition: Also tried the following and it did not work:
db.buzzfeed.save( { title: item.title, url: item.link } );
As Andi mentioned above, my code was incorrect, the following query solved it for me:
Buzzfeed.create({'title': newData.title, 'url': newData.link}, function (err, newTitles) {
});

How can I add markers to Google Maps using Node.js/Express/MongoDB?

I decided this week that I'd like to learn Node.js (I'm not a programmer). I've been having a lot of fun with it so far but I'm stuck at the moment.
I've created a basic app using Express. Let's say I have a route /locations. I set my get request to render the related view and to find (using Mongoose's .find method) all docs in my location model. I know I can pass the docs to the view like this:
app.get('/locations', function(req, res) {
Location.find({}, function(err, docs) {
res.render('locations/index', {
title: 'Locations',
user: req.user,
docs: docs
});
});
});
I can then, for example, access the results in the (Jade) view and list them by doing something like:
if(docs.length)
each location in docs
p #{location.name} is at #{location.coordinates}
I want to add all these locations (using coordinates stored with each 'location') to a Google Map. I have an example map displaying on the view using the following script in the head, taken from the Google Maps API documentation.
function initialize() {
var myLatlng = new google.maps.LatLng(-25.363882,131.044922);
var mapOptions = {
zoom: 4,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP,
}
var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
var marker = new google.maps.Marker({
position: myLatlng,
title: "Hello World!"
});
marker.setMap(map);
}
google.maps.event.addDomListener(window, 'load', initialize);
I figured where the marker variable is created then set I could loop through my 'docs' and create and set a marker for each location stored in my database. That said, I'm too new to this and I can't seem to figure out how to do it as the script in the head can't access the 'docs' that I've passed to the view.
Any advice? Thanks in advance, it's much appreciated.
I JSON.stringify() any objects that my client scripts need and insert it as HTML5 data-whatever attributes.
For example:
//app.js
app.get('/map', function(req, res){
var data = {
id: '1234',
LL: {
lat: 42.1,
lng: 80.8,
};
res.locals.docsJSON = JSON.stringify([data]);
res.render('locations/index');
});
//jade
!!!
html
body(data-locations=locals.docsJSON)
script
var docs = JSON.parse($('body').attr('data-locations'));
console.log(docs[0].LL);

Fetch Backbone collection with search parameters

I'd like to implement a search page using Backbone.js. The search parameters are taken from a simple form, and the server knows to parse the query parameters and return a json array of the results. My model looks like this, more or less:
App.Models.SearchResult = Backbone.Model.extend({
urlRoot: '/search'
});
App.Collections.SearchResults = Backbone.Collection.extend({
model: App.Models.SearchResult
});
var results = new App.Collections.SearchResults();
I'd like that every time I perform results.fetch(), the contents of the search form will also be serialized with the GET request. Is there a simple way to add this, or am I doing it the wrong way and should probably be handcoding the request and creating the collection from the returned results:
$.getJSON('/search', { /* search params */ }, function(resp){
// resp is a list of JSON data [ { id: .., name: .. }, { id: .., name: .. }, .... ]
var results = new App.Collections.SearchResults(resp);
// update views, etc.
});
Thoughts?
Backbone.js fetch with parameters answers most of your questions, but I put some here as well.
Add the data parameter to your fetch call, example:
var search_params = {
'key1': 'value1',
'key2': 'value2',
'key3': 'value3',
...
'keyN': 'valueN',
};
App.Collections.SearchResults.fetch({data: $.param(search_params)});
Now your call url has added parameters which you can parse on the server side.
Attention: code simplified and not tested
I think you should split the functionality:
The Search Model
It is a proper resource in your server side. The only action allowed is CREATE.
var Search = Backbone.Model.extend({
url: "/search",
initialize: function(){
this.results = new Results( this.get( "results" ) );
this.trigger( "search:ready", this );
}
});
The Results Collection
It is just in charge of collecting the list of Result models
var Results = Backbone.Collection.extend({
model: Result
});
The Search Form
You see that this View is making the intelligent job, listening to the form.submit, creating a new Search object and sending it to the server to be created. This created mission doesn't mean the Search has to be stored in database, this is the normal creation behavior, but it does not always need to be this way. In our case create a Search means to search the DB looking for the concrete registers.
var SearchView = Backbone.View.extend({
events: {
"submit form" : "createSearch"
},
createSearch: function(){
// You can use things like this
// http://stackoverflow.com/questions/1184624/convert-form-data-to-js-object-with-jquery
// to authomat this process
var search = new Search({
field_1: this.$el.find( "input.field_1" ).val(),
field_2: this.$el.find( "input.field_2" ).val(),
});
// You can listen to the "search:ready" event
search.on( "search:ready", this.renderResults, this )
// this is when a POST request is sent to the server
// to the URL `/search` with all the search information packaged
search.save();
},
renderResults: function( search ){
// use search.results to render the results on your own way
}
});
I think this kind of solution is very clean, elegant, intuitive and very extensible.
Found a very simple solution - override the url() function in the collection:
App.Collections.SearchResults = Backbone.Collection.extend({
urlRoot: '/search',
url: function() {
// send the url along with the serialized query params
return this.urlRoot + "?" + $("#search-form").formSerialize();
}
});
Hopefully this doesn't horrify anyone who has a bit more Backbone / Javascript skills than myself.
It seems the current version of Backbone (or maybe jQuery) automatically stringifies the data value, so there is no need to call $.param anymore.
The following lines produce the same result:
collection.fetch({data: {filter:'abc', page:1}});
collection.fetch({data: $.param({filter:'abc', page:1})});
The querystring will be filter=abc&page=1.
EDIT: This should have been a comment, rather than answer.

Resources