Get slug from object in view won't work - mongodb

I have created a new object named Project, that contains a gallery and some other fields in it. In the view, I'm showing some data from it and I want to put a link to previous and next project. I already managed to get the previous project but when I try to get the slug from it, somehow it doesn't work.
This is the Project model:
var keystone = require('keystone');
var Types = keystone.Field.Types;
/**
* Project Model
* ==========
*/
var Project = new keystone.List('Project', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true }
});
Project.add({
title: { type: String, required: true },
state: { type: Types.Select, options: 'draft, published, archived', default: 'draft', index: true },
author: { type: Types.Relationship, ref: 'User', index: true },
publishedDate: { type: Types.Date, index: true, dependsOn: { state: 'published' } },
category: { type: String, required: false },
description: { type: Types.Html, wysiwyg: true, height: 150 },
shortDescription: { type: Types.Html, wysiwyg: true, height: 100 },
credits: { type: Types.Html, wysiwyg: true, height: 100 },
galleries: { type: Types.Relationship, ref: 'Gallery', many: false },
color: { type: String, required: false }
});
Project.schema.virtual('content.full').get(function() {
return this.content.extended || this.content.brief;
});
Project.defaultColumns = 'title, state|20%, author|20%, publishedDate|20%';
Project.register();
This is the controller:
var keystone = require('keystone');
exports = module.exports = function(req, res) {
var view = new keystone.View(req, res);
var locals = res.locals;
// Set locals
locals.section = 'projects';
locals.filters = {
project: req.params.project
};
locals.data = {
projects: [],
previousProject: []
};
// Load the current project
view.on('init', function(next) {
var q = keystone.list('Project').model.findOne({
state: 'published',
slug: locals.filters.project
}).populate('galleries');
q.exec(function(err, result) {
locals.data.project = result;
next(err);
});
});
//Load other projects
view.on('init', function(next) {
var q = keystone.list('Project').model.find({state: "published", publishedDate: {$lt: locals.data.project.publishedDate}}).sort('-publishedDate').limit(1);
q.exec(function(err, results) {
locals.data.previousProject = results;
next(err);
});
});
// Render the view
view.render('project');
};
And this is the view:
<div class="container">
<p>{{{data.project.title}}}</p>
<p>—</p>
<p>{{{data.project.category}}}</p>
{{#if data.project.galleries}}
{{#each data.project.galleries.images}}
<img src="{{url}}" />
{{/each}}
{{/if}}
<p>full project: {{data.previousProject}}</p>
<p>slug: {{data.previousProject.slug}}</p>
{{#if data.previousProject}}
<a href="/projects/{{data.previousProject.slug}}" >Previous project</a>
{{/if}}
</div>
Somehow, {{data.previousProject}} shows the correct record info but when I do {{data.previousProject.slug}} it returns nothing at all. I've been scratching my head against this for hours but I can't find where is the issue. Thanks in advance!!

I finally found what the issue was: in the controller I'm using model.find whereas I should be using model.findOne if I know I only need one record and I want to directly get the values from it with .slug. Using limit(1) was not enough.

Related

JqxScheduler Customization dialog

I want to customize the dialog box in jqx scheduler dialog box title. Instead of 'Create new appointment' i want to display "Create a new business schedule".
I work with python django so in the code there are some elements from django template code. So far i create , edit, delete appointments with ajax and django as backend.
var items;
function setAvailability(price_data,persons_data,start_date,end_date, id, a_type, description){
availability_data = {
pk : "{{form.instance.pk}}",
csrfmiddlewaretoken: "{{ csrf_token }}",
price:price_data,
persons:persons_data,
from_date:start_date,
to_date:end_date,
a_type_data:a_type,
av_description:description
}
if(id) {
availability_data['event'] = id;
}
$.ajax({
url: "{% url 'set-availability' %}",
method: "POST",
data: availability_data,
dataType: "json",
success: function(response){
var source = {
datatype: "json",
dataFields: [
{ name: 'id', type: 'integer' },
{ name: 'description', type: 'string' },
{ name: 'location', type: 'string' },
{ name: 'status', type: 'string' },
{ name: 'price', type: 'number' },
{ name: 'persons', type: 'number' },
{ name: 'subject', type: 'integer' },
{ name: 'start', type: 'date', format: "yyyy-MM-dd"},
{ name: 'end', type: 'date', format: "yyyy-MM-dd"}
],
id: 'id',
url: "{% url 'get-availability' %}?pk={{form.instance.pk}}"
};
var adapter = new $.jqx.dataAdapter(source);
$("#scheduler").jqxScheduler({
source: adapter
})
return response;
}
})
}
$.ajax({
url: "{% url 'get-availability' %}",
method: "GET",
data: { pk : "{{form.instance.pk}}" },
dataType: "json",
success: function(response){
items = response;
var source = {
datatype: "array",
dataFields: [
{ name: 'id', type: 'integer' },
{ name: 'description', type: 'string' },
{ name: 'location', type: 'string' },
{ name: 'status', type: 'string' },
{ name: 'price', type: 'number' },
{ name: 'persons', type: 'number' },
{ name: 'subject', type: 'integer' },
{ name: 'start', type: 'date', format: "yyyy-MM-dd"},
{ name: 'end', type: 'date', format: "yyyy-MM-dd"}
],
id: 'id',
localData: items
};
var adapter = new $.jqx.dataAdapter(source);
$("#scheduler").jqxScheduler({
date: new $.jqx.date(2018, 02, 01),
width: 850,
height: 600,
source: adapter,
editDialogDateTimeFormatString: 'yyyy-MM-dd',
editDialogDateFormatString: 'yyyy-MM-dd',
showLegend: false,
localization: { editDialogStatuses: {
available: "Available",
booked: "Booked"
}},
renderAppointment: function(data)
{
if (data.appointment.status == "available") {
data.style = "#B8E6A3";
}
else if (data.appointment.status == "booked") {
data.style = "#FF0013";
}
return data;
},
ready: function () {
$("#scheduler").jqxScheduler('ensureAppointmentVisible', 'id1');
},
resources:
{
colorScheme: "scheme05",
dataField: "id",
source: new $.jqx.dataAdapter(source)
},
appointmentDataFields:
{
id: "id",
description: "description",
location: "location",
subject: "subject",
price: "price",
persons: "persons",
status: "status",
calendar: 'calendar',
from: "start",
to: "end",
},
view: 'monthView',
views:
[
'monthView'
],
renderAppointment: function (dialog, fields, renderAppointment) {
console.info('render appointment:', dialog);
},
editDialogCreate: function (dialog, fields, editAppointment) {
fields.repeatContainer.hide();
fields.subjectContainer.hide();
fields.timeZoneContainer.hide();
fields.colorContainer.hide();
fields.resourceContainer.hide();
fields.allDayContainer.hide();
fields.locationContainer.hide();
fields.fromContainer.hide();
fields.toContainer.hide();
fields.fromLabel.html("Start");
fields.toLabel.html("End");
var priceField = ''
var personsField = ""
priceField += "<div>"
priceField += "<div class='jqx-scheduler-edit-dialog-label'>Price</div>"
priceField += "<div class='jqx-scheduler-edit-dialog-field'><input type='number' id='price' step='0.01' /></div>"
priceField += "</div>"
personsField += "<div>"
personsField += "<div class='jqx-scheduler-edit-dialog-label'>Persons</div>"
personsField += "<div class='jqx-scheduler-edit-dialog-field'><input type='number' id='persons' /></div>"
personsField += "</div>"
var i = 0;
$('#dialogscheduler').children('div').each(function () { // loop trough the div's (only first level childs) elements in dialogscheduler
i += 1;
if (i == 2) { // places the field in the third position.
$(this).after(priceField);
$(this).after(personsField);
};
});
},
editDialogOpen: function (dialog, fields, editAppointment) {
console.info(dialog);
fields.repeatContainer.hide();
}
});
$('#scheduler').on('editDialogOpen', function (event) {
var args = event.args;
var appointment = args.appointment;
if(appointment){
$('#dialogscheduler > div').find('#price').val(appointment.price);
$('#dialogscheduler > div').find('#persons').val(appointment.persons);
}
else {
$('#dialogscheduler > div').find('#price').val(0);
$('#dialogscheduler > div').find('#persons').val(0);
$('#dialogscheduler > div').find('#from').val('');
$('#dialogscheduler > div').find('#to').val('');
$('#dialogscheduler > div').find('#description').val('');
$('#dialogscheduler > div').find('#status').val();
}
});
$('#scheduler').on('appointmentAdd', function (event) {
var price_data = $('#dialogscheduler > div').find('#price').val();
var persons_data = $('#dialogscheduler > div').find('#persons').val();
var a_type = event.args.appointment.status;
var description = event.args.appointment.description;
var start_date = event.args.appointment.from.toString();
var id = null;
var end_date = event.args.appointment.to.toString();
var availability = setAvailability(price_data,persons_data,start_date,end_date,id, a_type, description);
});
$('#scheduler').on('appointmentDelete', function (event) {
var id = event.args.appointment.id;
deleteEvent(id);
});
$('#scheduler').on('appointmentChange', function (event) {
var id = event.args.appointment.id;
var price_data = $('#dialogscheduler > div').find('#price').val();
var persons_data = $('#dialogscheduler > div').find('#persons').val();
var description = event.args.appointment.description;
var a_type = event.args.appointment.status;
var start_date = event.args.appointment.from.toString();
alert(start_date);
var end_date = event.args.appointment.to.toString();
var availability = setAvailability(price_data,persons_data,start_date,end_date,id, a_type,description);
});
}
})
function deleteEvent(pk) {
$.ajax({
url: "{% url 'delete-availability' %}",
method: "GET",
data: { pk : pk, business_pk:'{{form.instance.pk}}'},
dataType: "json",
success: function(response){
console.info(response)
}
})
}
I think you need to put the below code snippet in the editDialogOpen() function. This is working for me.
setTimeout(function() {
dialogRef.find("div").first().find("div").first().html("Create a new business schedule");
}, 10);
You can use the localization to do it. I'm using jqxScheduler with Angular, so:
Add [localization]="localization" into the component definition that should looks like:
<jqxScheduler #scheduler [editDialogCreate]="editDialogCreate" [localization]="localization">
Add a localization property into the component class. jqxScheduler has different titles for create and edit appointents:
localization = {
editDialogTitleString: 'Edit a business schedule',
editDialogCreateTitleString: 'Create a new business schedule'
};
jQWidgets has implementation examples to other languages. JQuery here.
$("#scheduler").jqxScheduler({
localization: {
editDialogCreateTitleString: "Create a new business schedule",
},
});

Creating custom attributes in Sailsjs

I am trying to create a custom attribute in my sails app and I am not getting any result back in my get call.
My code looks something like this:
module.exports = {
attributes: {
id: {
type: 'string',
primaryKey: true,
defaultsTo: function () {
return uuid.v4();
},
unique: true,
index: true,
uuidv4: true
},
name: {
type: 'string',
required: true
}
fullName: function(){
return this.name ;
}
};
I get back all the fields expect full Name
In my opinion the sailsjs model attributes only create corresponding column and get the column in database based on the type. In your case, the fullName has no type. so it does not know what to get from database.
However, if what you want is the returned json object has some extra fields, you can overwrite the toJSON function to add it.
module.exports={
attributes:
id: {
type: 'string',
primaryKey: true,
defaultsTo: function () {
return uuid.v4();
},
unique: true,
index: true,
uuidv4: true
},
name: {
type: 'string',
required: true
},
toJSON:function(){
var obj=this.toObject();
obj.fullName=obj.name;
return obj;
}
}
}
This issue is a little old, but I've a better answer:
module.exports = {
attributes: {
firstname : 'string',
lastname : 'string,
fullName: function () {
return this.firstname + ' ' + this.lastname
}
toJSON:function(){
var obj = this.toObject();
obj.fullName = this.fullName()
return obj;
}
}
}

Eager Loading : How to disable specific fields of included table

I am trying to do Eager Loading in Sequelize with PostgreSQL where I need to find the Users which have a given specific Mail id or basically, i am performing find operation on Mail model while using include to include User model
UserModel :
module.exports = function (sequelize, Sequelize) {
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
return User;
};
MailModel :
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
});
Mail.belongsTo(User);
return Mail;
};
MailController :
var db = require('../services/db.js');
module.exports = {
create: function (req, res, next) {
var Mailm = db.MailModel;
var name = req.body;
try {
db.sequelize.sync().then(function () {
Mailm.create(name).then(function (found) {
return res.json({
success: true,
message: found.get({
plain: true
})
});
})
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
},
query: function (req, res, next) {
var Mailm = db.MailModel;
var Userm = db.UserModel;
var name = req.body;
var option = {};
option.where = name;
option.include = [{
model: Userm
}];
try {
Mailm.findAll(option).then(function (found) {
console.log(found);
return res.json({
success: true,
message: found
});
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
}
};
It is returning me the records of both User and Mail table in exactly the right way .
Output :
{
"success": true,
"message":[
{
"id": 2,
"name": "Mailb2",
"createdAt": "2015-07-30T07:32:51.807Z",
"updatedAt": "2015-07-30T07:32:51.807Z",
"UserUserId": 2,
"User":{
"userId": 2,
"firstname": "Prerna",
"lastname": "Jain",
"age": 20,
"phone": "9812123456",
"location": "Sirsa",
"createdAt": "2015-07-30T07:30:48.000Z",
"updatedAt": "2015-07-30T07:30:48.000Z"
}
}
]
}
But I want to disable createdAt and updatedAt fields of User table so that it does not give me these two fields in the output for User.
I have tried a lot as of how to do this but still in vain.Can anyone please help me out.
I bet this is coming late, add attribute/properties to your models called timestamps, it accepts a boolean as a value. For example:
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
},
{
// This does the magic
timestamps: false,
});
Mail.belongsTo(User);
return Mail;
};
Also, add it to the User model:
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
},
{
timestamps: false
});
return User;
};
You can use
Model.findAll({
attributes: { exclude: ['baz'] }
});
more examples with attributes - http://docs.sequelizejs.com/en/latest/docs/querying/#attributes

Sencha Touch: REST store and DELETE request

I'm new to Sencha Touch and I'm having problems to delete a record.
This is my model:
Ext.define('MyApp.model.Customer', {
extend: 'Ext.data.Model',
...
config: {
idProperty: 'key',
fields: [
{
name: 'key',
type: 'auto'
},
{
name: 'name',
type: 'string'
}
...
]
}
....
}
and this is an event associated with a delete customer button:
onDeleteCustomerBtnTap: function(button, e, eOpts) {
var data = this.getCustomerDetail().getData();
var store = Ext.getStore("CustomerStore");
var record = store.getById(data.key);
store.remove(record);
store.sync();
}
EDIT:
this is the store
proxy: {
type: 'rest',
url: 'http://example.com/customers',
useDefaultXhrHeader: false,
appendId: false,
reader: {
type: 'json',
idProperty: 'key',
rootProperty: 'data'
},
writer: {
type: 'json'
}
}
The problem is when it tries to sync the store. The request url is http://example.com/customers?_dc=1394840324234 instead of http://example.com/customers/10?_dc=1394840324234.
What am I doing wrong?
I finally used the erase method of Ext.data.Model. Here is my final version:
var data = this.getCustomerDetail().getData();
var store = Ext.getStore("CustomerStore");
var record = store.getById(data.key);
record.erase({
success: function() {
// ...
}
}
});
are you defining your proxy as 'rest' and not 'ajax'?
proxy: {
type: 'rest',
url : '/users'
}

How to dynamically create form.Fields from Stores in ExtJS 4

I have two stores, Assessor.store.Question and Assessor.store.Choice, along with their respective Models and Proxies. The load data from the server as intended. I also have a Panel, Assessor.view.QuizCards, with a "card" layout. This works fine and I can create dummy cards, Assessor.view.QuestionCard, and navigate through them fine using the Assessor.controller.Quiz controller.
What I need help with is programatically populating my QuizCards panel with questions and choices from the Questions and Choices stores. I've tried just about everything I can think of based on the docs and have had absolutely no success.
Specifically, I want the "value" of the "displayfield" on a QuestionCard to be the "text" property from the Question store/model. The "boxlabel" values in the "radiogroup" should come from the associated Choice store/model.
The detailed code is below. Thanks for any guidance you can provide.
Ext.define('Assessor.controller.Quiz', {
extend: 'Ext.app.Controller',
itemId: 'quizcontroller',
models: ['Question', 'Choice'],
stores: ['Question', 'Choice'],
views: ['QuestionCard'],
// Constants, kinda
NUM_QUESTIONS: 4,
// Custom Functions
/**
* create store instances
*/
createStores: function(){
if (Ext.getStore('questionstore') == null) {
var qs = Ext.create('Assessor.store.Question');
qs.load();
};
if (Ext.getStore('choicestore') == null) {
var cs = Ext.create('Assessor.store.Choice');
cs.load();
};
}, //end createStores
/**
* update buttons
*/
updateButtons: function(){
var index = this.getCardIndex();
var nb = Ext.ComponentQuery.query('#nextbutton')[0];
var pb = Ext.ComponentQuery.query('#prevbutton')[0];
var fb = Ext.ComponentQuery.query('#finishbutton')[0];
if (index<this.NUM_QUESTIONS) {
nb.enable();
fb.disable();
} else {
nb.disable();
fb.enable();
};
if (index>0){
pb.enable();
} else {
pb.disable();
};
}, //end updateButtons
/**
* get active question card index
*/
getCardIndex: function(){
return (Ext.ComponentQuery.query('quizcards')[0].getLayout().activeItem.itemId.split('-')[1]);
},
/**
* set active question card index
*/
setCardIndex: function(index){
Ext.ComponentQuery.query('quizcards')[0].getLayout().setActiveItem('questioncard-'+index);
},
/**
* start the quiz
*/
startQuiz: function(args) {
this.createQuestionCards();
var sb = Ext.ComponentQuery.query('#startbutton')[0];
sb.disable();
this.updateButtons();
},
/**
* create the UI cards with questions from server.
*/
createQuestionCards: function() {
var qc = Ext.ComponentQuery.query('quizcards')[0];
for (i=0; i<this.NUM_QUESTIONS; i++) {
card = Ext.create('Assessor.view.QuestionCard');
card.itemId = 'questioncard-' + i.toString();
qc.add(card);
};
this.updateButtons();
},
/**
* finishQuiz -- finishes and scores the quiz
* #param {Object} args
*/
finishQuiz: function(args) {
this.localState.set('quizFinished', true);
},
//
nextQuestion: function(args) {
console.log('\nnextQuestion');
var cardlayout = Ext.ComponentQuery.query('quizcards')[0].getLayout();
var activeIndex = cardlayout.activeItem.itemId.split('-')[1];
console.log(activeIndex);
if (activeIndex < this.NUM_QUESTIONS) {
activeIndex++;
this.setCardIndex(activeIndex);
};
this.updateButtons();
},
//
prevQuestion: function(args) {
console.log('\nprevQuestion');
var cardlayout = Ext.ComponentQuery.query('quizcards')[0].getLayout();
var activeIndex = cardlayout.activeItem.itemId.split('-')[1];
console.log(activeIndex);
if (activeIndex > 0) {
activeIndex--;
this.setCardIndex(activeIndex);
};
this.updateButtons();
},
//
init: function(){
this.control({
'#nextbutton': {
click: this.nextQuestion
},
'#prevbutton': {
click: this.prevQuestion
},
'#startbutton': {
click: this.startQuiz
},
'#finishbutton': {
click: this.finishQuiz
},
})
}
})
Ext.define('Assessor.view.QuizCards', {
extend: 'Ext.panel.Panel',
alias: 'widget.quizcards',
itemId: 'quizcards',
layout: 'card',
activeItem: 0,
items: []
})
Ext.define('Assessor.view.QuestionCard', {
extend: 'Ext.form.Panel',
alias: 'widget.questioncard',
layout: 'anchor',
items: [{
xtype: 'displayfield',
itemId: 'questionfield',
name: 'questionfield',
fieldLabel: 'Question',
value: ''
}, {
xtype: 'radiogroup',
itemId: 'choicegroup',
columns: 1,
vertical: true,
items: [{
boxLabel: '',
name: 'choice',
value: 1
}, {
boxLabel: (100*Math.random()),
name: 'choice',
value: 2
}, {
boxLabel: (100*Math.random()),
name: 'choice',
value: 3
}]
}]
})
Ext.define('Assessor.store.Question', {
extend: 'Ext.data.Store',
autoLoad: true,
autoSync: true,
model: 'Assessor.model.Question',
storeId: 'questionstore'
})
Ext.define('Assessor.model.Question', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'text', type: 'string'},
{name: 'resource_uri', type: 'string'}
],
proxy: {
type: 'rest',
url: '/api/v1/question/',
headers: {
'accept':'application/json',
'content-type':'application/json'
},
noCache: false,
reader: {
type: 'json',
root: 'objects',
idAttribute: 'id'
},
writer: {
type: 'json'
}
}
})
Choice model and store are similar, I'll post them if needed. Thanks
What I would suggest for the start is to make Assessor.view.QuestionCard more smart. I would rewrite initComponent there and pass record from the store during construction. This way you would have all logic for creating specific UI elements inside Assessor.view.QuestionCard and would call it something like that:
card = Ext.create('Assessor.view.QuestionCard', {
questionRecord: rec,
lastQuestion: true/false...
... whatever else you need
})