Can't implement KeystoneJS Pagination concurrently - javascript

I'm trying to implement pagination in keystone js andf I'm doing it as follows:
Model
var keystone = require('keystone');
var Types = keystone.Field.Types;
var Test = new keystone.List('Test', {
map: {name: 'title'},
label: 'Test',
singular: 'Test',
plural: 'Tests',
autokey: {path: 'slug', from: 'title', unique: true}
});
Test.add({
description: {type: Types.Html, wysiwyg: true, height: 300 }
});
Test.register();
Route / View
var keystone = require('keystone');
var Test = keystone.list('Test');
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// Set locals
locals.section = 'test';
locals.data = {
test: []
};
Test.paginate({
page: req.query.page || 1,
perPage: 2,
maxPages: 5
})
.exec(function(err, results) {
locals.data.test = results;
next(err);
});
// Load All Terms
view.query('test', keystone.list('Test').model.find());
// Render view
view.render('test');
}
In Index.js
app.all('/test', routes.views.test);
In middleware.js
res.locals.navLinksFooter = [
{ label: 'Test', key: 'test', href: '/test' },
.....
];
Template
<div class="row">
<div class="col-xs-12">
<span class="pull-right">
{{>pagination}}
</span>
</div>
</div>
<div class="row row-content panel--outer panel--outer--trans">
<div class="col-xs-12" id="panel--news-detail">
{{# each test}}
<p>{{{description}}}</p>
{{/each}}
</div>
</div>
Unfortunately, when I try to go to the page I get the following error:
"ReferenceError: next is not defined"
Some pointers and possible causes. I've already set up pagination for blog posts on a separate page using:
locals.data = {
test: []
};
If I comment out "next(err)", the pagination buttons appear but clickiong on the arrow takes me to the second page of blog/news items.
Things are obviously getting mixed up somewhere here. I suspect it's due to my mishandling of locals but I can't seem to find any in depth explanation on the keystone site of how locals are supposed to work.
I ultimately want to apply different filters to the mongo collection and have it running in a series of bootstrap tabs but I think I need to get the basics sorted out first.
Has anyone been faced with a similar situation and been able to find the solution?

next is a callback function that runs within your Express route. You don't have it defined as an argument in your route at all; part of the problem is also that you are trying to load the data synchronously, which doesn't work here.
Use the view.init function to expose the next function, then call it when you've loaded information from your database into locals.data.
route/view
var keystone = require('keystone');
var Test = keystone.list('Test');
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// Set locals
locals.section = 'test';
locals.data = {
test: []
};
view.on('init', function (next) {
Test.paginate({
page: req.query.page || 1,
perPage: 2,
maxPages: 5
})
.exec(function(err, results) {
locals.data.test = results;
next(err);
});
});
// Load All Terms
view.query('test', keystone.list('Test').model.find());
// Render view
view.render('test');
}

Related

.find is not a function error

I'm developing a node js rest server and having an issue with my Schema queries. When I hit my end points I get the error TypeError: user.find is not a function
The following is my user.js file
var {mongoose} = require('../../dbcore/mongoose');
var Schema = mongoose.Schema;
module.exports = mongoose.model('User',new Schema( {
basicId: Schema.ObjectId,
activePurchaseIDs: {
type: [Schema.ObjectId],
default: []
},
activeOrderIDs: {
type: [Schema.ObjectId],
default: []
},
paymentOptionIDs: {
type: [Schema.ObjectId],
default: []
},
addressIDs: {
type: [Schema.ObjectId],
default: []
},
interestIDs: {
type: [Schema.ObjectId],
default: []
}
}));
and this is where it's imported/required.
var URLS = require('./urls');
var User = require('../schemas/user/user');
function init(app,mongoose) {
app.get(URLS.USERS_URL,(req,res)=>{
var user = new User({});
user.find().then((users)=>{
res.send({users});
},(err)=>{
res.status(400).send(err);
});
});
}
module.exports = init;
I was following a tutorial while writing this code and I was expecting it to work as I followed the tutorial step by step.
When you call var user = new User({}) you are creating a new MongoDB document based on the User model and assigning it to var user.
A single user document does not have a find() function, but your User model does.
var user = new User({});
User.find().then(...);
app.get(URLS.USERS_URL, async (req,res)=>{
const userList = await User.find();
if(!userList) {
res.status(500).json({success: false});
}
res.send(userList);
});
Your call to the database needs to look like this:
User.find().then((users)=>{
res.send({users});
}).catch((err)=>{
res.status(400).send(err);
});
You should call it directly on the module, because mongoose will handle creation implicitly and creating a new object isn't neccesary.
I'm not sure if your schema is correctly defined, but I'm not going to say your tutorial is wrong on that. You should go into mongo shell and check if the schema was created to verify it was designed correctly.
In my case, I wrote wrong this so check your file exports module.exports = XYZ format.
PS:- I wrote like this exports.module = XYZ

Query,url and body parameters not merged correctly in seneca with seneca-web

I'm in trouble with seneca and seneca-web. I'm a really a beginner.
This is my code so far:
"use strict";
var express = require('express');
var Web = require("seneca-web");
var bodyParser = require('body-parser')
var plugin = require('./products_actions/products_actions');
module.exports = plugin;
var entities = require('seneca-entity')
var seneca = require('seneca')();
seneca.use(plugin);
seneca.use(entities);
seneca.use('mysql-store', {
name : 'ecrm',
host : 'localhost',
user : 'root',
password : 'ecrm',
port : 3306
})
seneca.ready(function(err) {
var Routes = [ {
pin : 'area:product,action:fetch,criteria:*',
prefix : '/products/fetch',
map : {
byId : {
GET : true,
suffix : "/:id"
}
}
}, {
pin : 'area:product,action:*',
prefix : '/products',
map : {
fetch : {
GET : true
},
add : {
GET : false,
PUT : true
}
}
} ];
var app = express();
app.use(bodyParser.json());
var config = {
routes : Routes,
adapter : require('seneca-web-adapter-express'),
context : app,
options : {
parseBody : false
}
}
seneca.use(Web, config);
app.listen(3000);
});
and here is the code where are defined the seneca actions:
module.exports = function(options) {
var seneca = this;
// ADD
seneca.add({
area : "product",
action : "add"
}, function(req, done) {
var products = this.make$("prodotti");
var args = req.args.body;
console.log(args);
products.nome = args.nome;
products.categoria = args.categoria;
products.descrizione = args.descrizione;
products.prezzo = args.prezzo;
products.save$(function(err, product) {
done(err, products.data$(false));
});
});
// get by Id , PROBLEM!!!
seneca.add({
area : "product",
action : "fetch",
criteria : "byId"
}, function(req, done) {
console.log("HERE");
var id = req.args.params.id;
var product = this.make("prodotti");
product.load$(id, done);
});
// LIST ALL
seneca.add({
area : "product",
action : "fetch"
}, function(args, done) {
var products = this.make("prodotti");
products.list$({}, done);
});
}
The problem is in the getbyId handler (route /products/fetch/byId/id_of_the product).
The code so far works but I want to get the id variable represented by id_of_the product not doing var id = req.args.params.id;
but I want it merged in the req object and I would like to do var id = req.id;
The same in the ADD handler, I did var args = req.args.body; but I would like to see the body merged in the req object as I've seen in the book
'Developing Microservices in node.js' by David Gonzales.
The problem arises when in a handler I want to call another seneca action passing the id of the product. In that case the id is available to the req object and not in the url parameters.
Of course I could test if the variable is defined in req.args.params and if not use the one from the req object but it's not a clean solution.
Is this possible with the current version of seneca and seneca web? I have installed them from npm and I have these versions:
seneca 3.2.2 and seneca-web 2.0.0;
Thanks in advance!
You should marshal out the information you are interested, verify it's validity and call the underlying action with just that information. The role:web can be a good gatekeeper for general sanity checks from user input prior to hitting your actual services.
Remember that the user can pass anything as that route parameter, body, querystring, etc. one must take care to sanitize user-provided information and only send valid information into the underlying actions.
seneca.add('role:web,domain:product,action:getById', function (msg, done) {
const id = parseInt(msg.args.params.id, 10)
if (isNaN(id)) {
return done(new Error('needs to be numeric'))
}
seneca.act('role:entity,domain:product,action:getById', {id}, done)
})
seneca.add('role:entity,domain:product,action:getById', function (msg, done) {
this.make$('product').load$(msg.id, done)
})
Retrieving information about the request from (in example above) msg.args is the name of the game in seneca-web - it's the only way to get at that information.

How to get a list of files to the template in Meteor?

New to meteor, I'm having a hell of a time with the whole client/server thing. Super fun.
I want to read a list of all files that are in a folder, and just print them on the UI, on a template. It has to be possible. Here's the code.
js file:
var availableFiles = [];
if (Meteor.isServer) {
var fs = Npm.require('fs');
availableFiles = fs.readdirSync(process.env.PWD + '/public/uploads');
console.log(availableFiles);
}
Router.map(function() {
this.route('home', {
path: '/',
template: 'home',
data: {files: availableFiles}
});
});
html file:
<template name="home">
{{#each data.files}}
{{this}}
{{/each}}
</template>
I've tried to put the fs calls inside a function on data on the route definition, but I get "Npm is not defined" errors. I don't know if that's how it should be structured or not, remember, newb here. Any help would be greatly appreciated.
I'd probably do this with a reactive array (http://reactivearray.meteor.com/) and a Meteor method. Something like:
if (Meteor.isServer) {
var fs = Npm.require('fs');
Meteor.methods({
getFiles() {
return fs.readdirSync(process.env.PWD + '/public/uploads');
}
});
}
if( Meteor.isClient ) {
Template.home.onCreated( function() {
Template.instance().files = new ReactiveArray();
Meteor.call('getFiles', function(error, result) {
if( error ) {
console.log( error );
} else {
Template.instance().files = files.concat( result );
}
});
});
Template.home.helpers({
files() {
return Template.instance().files.array();
}
});
}
Router.map(function() {
this.route('home', {
path: '/',
template: 'home',
data: {files: availableFiles}
});
});
Syntax might not be perfect, but that should get the point across. You'll also need to change your template to {{#each files}}

Cannot return id specific mongo documents in Angular controller

Trying to return a list of articles specific to a sectionId in angular controller. I can get back the entire list of articles using articles.query but the passed sectionId query param gets completely ignored. I tried a to abstract it to an articles service but I'm not sure how to build services correctly yet so it threw more errors. Any help / examples of how I might achieve this, preferable as a service, would be great. I am using mean.js. Thanks in advance!
Sections controller
angular.module('sections').controller('SectionsController', ['$scope', '$stateParams', '$location', 'Authentication', 'Sections', 'SectionArticlesList', 'Articles',
function($scope, $stateParams, $location, Authentication, Sections, SectionArticlesList, Articles) {
..........
// Find existing Section
$scope.findOne = function() {
$scope.section = Sections.get({
sectionId: $stateParams.sectionId // sections return fine
});
// problem starts here
$scope.articles = Articles.query({
section:$stateParams.sectionId // this param is ignored
});
$scope.articles.$promise.then(function(data) {
// all articles in collection returned instead of section specific
console.log('Articles: '+ JSON.stringify(data));
$scope.articles = data;
});
Articles Model
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
section: {
type: Schema.ObjectId,
ref: 'Section'
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
UPDATE:
Following up on the advice of Kevin B and Brad Barber below, I tried adding a factory and node server route, passing the $stateParams.sectionId to the specific factory instance. I created a new route in the articles.routes.server to make sure not to have a conflict with the standard ‘/articles/:articleId’ route. Unfortunately, anything I try still either throws errors or everything in the articles collection which is the opposite of what I want to do.
section.client.controller
// Find existing Section
$scope.findOne = function() {
$scope.section = Sections.get({
sectionId: $stateParams.sectionId
});
// fails no matter what is passed to query
$scope.articles = SectionArticlesList.query($stateParams.sectionId);
//$scope.SectionArticlesList.query($stateParams.sectionId);
//$scope.articles = SectionArticlesList.query({sectionId});
$scope.articles.$promise.then(function(data) {
// still only ever returns full list of articles
console.log('Length of articles: '+ JSON.stringify(data.length));
$scope.articles = data;
});
….
articles.client.services
angular.module('articles').factory('SectionArticlesList', ['$resource',
function($resource) {
return $resource('articles/:articleSectionId', {
articleSectionId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
articles.server.routes
// Custom route
app.route('/articles/:articleSectionId')
.get(articles.listBySectionID);
// Custom binding
app.param('articleSectionId', articles.listBySectionID);
articles.server.controller
The server side controller function never appears to get called because neither of the console.logs show up.
exports.listBySectionID = function(req, res, id) {
console.log('listBySection Called....'); // this is never printed
// have tried passing - id, sectionId and {section:id}
Article.find( {section:id} ).sort('-created').populate('user', 'displayName').exec(function(err, articles) {
if (err) {
console.log('listBySection err triggered...'); // this neither
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
I think I have tried everything I can think of to pass the sectionId correctly but nothing has worked yet. The latest error in the console is the 404 below.Interestingly the sectionId IS getting through but as if it is looking for one single resource such as a single blog post.
GET /sectionArticles?sectionId=555bfaf29a005c30cbfe6931 404
I don't quite understand how the default mean.js /articles route and it's corresponding list function works but duplicating a similar structure and passing an id as a param to the query to retrieve only specific results doesn't.
Would really like to understand how this should be wired up. If anyone can point out what I am doing wrong I’d appreciate it!

Rendering with Backbone

I am trying to learn backbone and I was following along the code school backbone.js course to build my own backbone app. So far I have this code but I am having problems with rendering anything.
var PostsApp = new (Backbone.View.extend({
Collections: {},
Models: {},
Views: {},
start: function(bootstrap){
var posts = new PostsApp.Collections.Posts(bootstrap.posts);
var postsView = new PostsApp.Views.Posts({collection: posts});
this.$el.append(postsView.render().el);
}
}))({el : document.body});
PostsApp.Models.Post = Backbone.Model.extend({});
PostsApp.Collections.Posts = Backbone.Collection.extend({});
PostsApp.Views.Post = Backbone.View.extend({
template: _.template("<%= name %>"),
render: function(){
this.$el.html(this.template(this.model.toJSON()));
}
});
PostsApp.Views.Posts = Backbone.View.extend({
render: function(){
this.collection.forEach(this.addOne, this);
},
addOne: function(post){
var postView = new PostsApp.Views.Post({model:post});
this.$el.append(postView.render().el);
}
});
var bootstrap = {
posts: [
{name:"gorkem"},
{name: "janish"}
]
}
$(function(){
PostsApp.start(bootstrap);
});
I am just trying to create a very simple backbone app, CodeSchool is great but it not good at combining the pieces together and when I try to do that myself I am having problems.
So far the error I am getting is "Uncaught TypeError: Cannot read property 'el' of undefined" in the addOne function of the Posts View. Any help would be much appreciated.
edit: The answer below solved my initial problem, but I also set up an express server to send data to the front end with the code :
app.get('/tweet', function(req,res){
res.send([{ name: 'random_name' }, {name: 'diren_gezi'}] );
});
and then I am trying to fetch this data to my collection like this :
var PostsApp = new (Backbone.View.extend({
Collections: {},
Models: {},
Views: {},
start: function(bootstrap){
var posts = new PostsApp.Collections.Posts(bootstrap.posts);
posts.url = '/tweet';
posts.fetch();
var postsView = new PostsApp.Views.Posts({collection: posts});
postsView.render();
this.$el.append(postsView.el);
}
}))({el : document.body});
But in the page the initial data (name: gorkem and name: janish) is displayed instead of the recently fetched data..
This is the problem line (I see it in a few spots).
this.$el.append(postsView.render().el);
Try changing it to
postsView.render();
this.$el.append(postsView.el);
Render function doesn't return a function to self (the object with a reference to el).

Categories