Node.js dependson access relationship attribute? - node.js

I have two simple modes:
PresentationType:
var keystone = require('keystone');
var PresentationType = new keystone.List('PresentationType', {
autokey: { from: 'name', path: 'key', unique: true },
});
PresentationType.add({
name: { type: String, required: true },
t1: { type: Boolean },
t2: { type: Boolean },
});
PresentationType.relationship({ ref: 'StaticPage', path: 'pages', refPath: 'presentationType' });
PresentationType.register();
Static Page:
var keystone = require('keystone');
var Types = keystone.Field.Types;
var StaticPage = new keystone.List('StaticPage', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true },
drilldown: 'presentationType',
});
StaticPage.add({
title: { type: String, required: true },
presentationType: { type: Types.Relationship, ref: 'PresentationType', many: false },
text1: { type: String, dependsOn: { presentationType.t1: true } },
text2: { type: String, dependsOn: { presentationType.t2: true } },
});
StaticPage.defaultColumns = 'title';
StaticPage.register();
First i create a presentation type that has boolean attributes, text1 and text2
Secondly when i create a page and specify it's presentation type, i want to be able to display certain fields based on the presentation type boolean.
So far i cant seem to find an answer to it.

The dependsOn attribute cannot be used across a relationship field; that field would constantly need to be populated with that relationship. dependsOn within a model can only be used within other static fields of the same model (and not across different models.)
http://keystonejs.com/docs/database/#fields-conditional

Related

How to reorder fields in Keystone's Admin UI

I'm trying to make Keystone into a CMS. So, I need models for Article, Category, ImagePage, AttachmentPage and so on. Every model I mentioned has a subset of common fields like: title, content, meta: {title, description, keywords} and so on.
In Keystone a model is constructed like this:
Article.add(fieldsCollectionObject)
so I defined the common fields in external file:
var T = require('keystone').Field.Types;
module.exports = {
title: { type: T.Text, required: true },
content: { type: T.Html, wysiwyg: true, height: 400 },
meta: {
title: { type: T.Text },
desc: { type: T.Textarea, height: 50 },
keywords: { type: T.Text },
},
publishedDate: { type: T.Date, index: true, dependsOn: { state: 'published' } },
state: { type: T.Select, options: 'draft, published, archived', default: 'draft', index: true },
};
and having require'd it in model's file I do:
const _ = require('lodash');
const pageDef = require('./common/Page.js');
const keystone = require('keystone');
const T = keystone.Field.Types;
<...>
Article.add(_.defaultsDeep({
brief: { type: T.Html, wysiwyg: true, height: 150 },
category: { type: T.Relationship, ref: 'Category', many: false, collapse: true },
tags: { type: T.Relationship, ref: 'Tag', many: true },
}, defs.authored, pageDef));
Now, the problem is with the order of fields in the Admin UI - unsurprisingly the brief, category and tags go before fields from pageDef. Is there any way to impose an order I want? Like title, brief, content, <the rest>?
defaults and defaultsDeep mutate the first object passed as a parameter to it (your initial object of Keystone fields). To have your own order, you would need to pass the objects to _.defaultsDeep in the order that you want them to appear in the object, and hence the order that they appear in the Admin UI.
Helpfully, duplicate items will not be included in the resulting object. So you would have something like this:
const _ = require('lodash');
const pageDef = require('./common/Page.js');
const keystone = require('keystone');
const T = keystone.Field.Types;
//....
let articleDef = {
brief: { type: T.Html, wysiwyg: true, height: 150 },
category: { type: T.Relationship, ref: 'Category', many: false, collapse: true },
tags: { type: T.Relationship, ref: 'Tag', many: true };
};
Article.add(_.defaultsDeep({
title: pageDef.title,
brief: articleDef.brief,
content: pageDef.content},
pageDef, articleDef));
The answer above turned out to be way to go. So I expanded and built upon it:
lib/util.js
const _ = require('lodash');
class Util {
static sourceFields (fields, ...sources) {
const source = _.defaultsDeep(...sources);
const result = [];
for (let fieldSet of fields) {
result.push(_.isArray(fieldSet) ? _.pick(source, fieldSet) : fieldSet);
}
return result;
}
}
module.exports = Util;
models/common/traits.js
var T = require('keystone').Field.Types;
module.exports = {
title: { type: T.Text, required: true },
content: { type: T.Html, wysiwyg: true, height: 400 },
indexImage: { type: T.CloudinaryImage },
meta: {
title: { type: T.Text },
desc: { type: T.Textarea, height: 50 },
keywords: { type: T.Text },
},
// <...>
}
models/Article.js
const util = require('../lib/utils.js');
const defs = require('./common/traits.js');
const keystone = require('keystone');
const T = keystone.Field.Types;
// < var Article declaration... >
const ownDef = {
brief: { type: T.Html, wysiwyg: true, height: 150 },
category: { type: T.Relationship, ref: 'Category', many: false, collapse: true },
tags: { type: T.Relationship, ref: 'Tag', many: true },
images: { type: T.Relationship, ref: 'Image', many: true, collapse: true },
attachments: { type: T.Relationship, ref: 'Attachment', many: true, collapse: true },
};
Article.add(...util.sourceFields([
'Content', ['title', 'brief', 'content', 'indexImage', 'category', 'tags'],
'Media', ['images', 'attachments'],
'SEO', ['meta'],
'Status', ['pageType', 'author', 'state', 'publishedDate'],
], ownDef, defs));
So, in traits.js I define common fields, in Article.js - fields I use only in Article model. Then, in Article model I add the fields to the List with the help of sourceFields() function. sourceFields() gets an array of fieldsets and unspecified number of field definition objects (like ownDef and defs).
The fieldset is either a string or an array of field names (keys in definition objects). If it's string it'll be a header in Admin UI, if it's array then it'll be a set of fields ordered just like field names in the array - the function basically inserts field definition into a "slot" specified in fieldset.

Push object in array with meteor 1.4?

I'm new in meteor and mongo I'd like to push one object in an array that is content in the other array. I'd like push giorni to cantieri. But I'd like push giorni in one specific cantieri. how can I make it? this my schema's collections.
`Clienti.Giorni = new SimpleSchema({
giorno: {
type: Date,
label: "giorno del lavoro"
},
oraPartenza: {
type: Date,
label: 'Giorno e ora partenza',
},
oraInizio: {
type: Date,
label: 'Giorno e ora inizio',
optional: true
},
oraFine: {
type: Date,
label: 'Giorno e ora fine',
optional: true
},
dipendenti: {
type: [Dipendenti]
}
});
Clienti.Cantieri = new SimpleSchema({
_id:{
type: String,
autoValue: function(){
var id = new Meteor.Collection.ObjectID();
return id._str
}
},
nome: {
type: String
},
luogo: {
type: String
},
inizio: {
type: Date
},
scadenza: {
type: Date
},
inCorso: {
type: Boolean,
defaultValue: false
},
createdAt: {
type: Date,
label: "Creato il",
autoValue: function() {
return new Date()
}
},
giorni: {
type: [Clienti.Giorni],
optional: true,
autoform: {
type: "hidden"
}
}
});
Clienti.ClienteSchema = new SimpleSchema({
nome: {
type: String,
label: "nome"
},
iva: {
type: String,
label: "Partita iva",
max: 16
},
referente: {
type: String,
label: "Nome persona di rifermento"
},
email: {
type: String,
label: "email"
},
indirizzo:{
type:String,
label: 'Indirizzo'
},
createdAt: {
type: Date,
label: "Creato il",
autoValue: function() {
return new Date()
},
autoform: {
type: "hidden"
}
},
cantieri: {
type: [Clienti.Cantieri],
optional: true,
autoform: {
type: "hidden"
}
}
});
Clienti.attachSchema( Clienti.ClienteSchema );`
I'm surprised you are not getting errors when trying to update your Clienti collection. According to the Simple Schema documentation in your schema definition, the type field should be a data type like String, Number, Boolean, Object or a constructor function like Date, and you can use any of these inside of square brackets to define it as an array of those data types (e.g., [String]).
So, one issue is that in your Clienti collection, you have defined your data type for cantieri as [Clienti.Cantieri]. This is not an acceptable data type. If I am understanding what you are trying to do correctly, you probably want the cantieri field definition in your Clienti collection to look like:
cantieri: {
type: [Object],
optional: true,
autoform: {
type: "hidden"
}
}
And after this, you need to add each cantieri field under this item using the format:
cantieri.$.nome: {
type: String
},
cantieri.$.luogo: {
type: String
}
You also want to add the giorni fields under the cantieri fields in the Clienti collection in the same format:
giorni: {
type: [Object],
optional: true,
autoform: {
type: "hidden"
}
},
giorni.$.giorno: {
type: Date,
label: "giorno del lavoro"
},
giorni.$.oraPartenza: {
type: Date,
label: 'Giorno e ora partenza',
}
Then, your method to update the database would look something like:
aggiungiGiorno: function(id, idC, doc,) {
Clienti.update({
_id: id,
"cantieri._id": idC
}, {
$push: {
"cantieri": doc
}
});
}
UPDATE:
If you want to combine your schemas as above, you should be able to also update the document using the query:
aggiungiGiorno: function(id, idC, doc,) {
Clienti.update({
_id: id,
"cantieri._id": idC
}, {
$push: {
"cantieri.$.giorni": doc
}
});
}

Is there a tag field type in Keystone JS?

I'm looking for a tag field type which will autocomplete if the tag already exists, or simply add the tag if it doesn't. I think there are a lot of implementations of this in other CMS' and I wanted to shake the tree to see if someone had already done this before I roll up my sleeves. Assuming it existed, I imagine it would be implemented as follows:
var keystone = require('keystone'),
Types = keystone.Field.Types;
var Verbiage = new keystone.List('Verbiage', {
autokey: { path: 'slug', from: 'title', unique: true },
map: { name: 'title' },
defaultSort: '-createdAt',
label: "Verbiage",
plural : "Verbiage"
});
Verbiage.add({
title: { type: String, required: true },
author: { type: Types.Relationship, ref: 'User' },
tagged: { type: Types.Tag, required: false, many: true },
createdAt: { type: Date, default: Date.now },
publishedAt: Date
});
Verbiage.register();

Image gallery with caption using CloudinaryImage on keystonejs

I'm using keystonejs and CloudinaryImages to create an Image Gallery.
{ type: Types.CloudinaryImages }
I need the ability to add a caption to the images.
I was also reading this:
https://github.com/keystonejs/keystone/pull/604
but I could not figure out if this option is already in place or not.
Any idea?
Thanks.
I had a similar problem, I wanted to be able to give Images there own descriptions and other attributes, while also being included in a Gallery with a Gallery description.
This may be more than you are looking for but here is a Image model:
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* Image Model
* ==================
*/
var Image = new keystone.List('Image', {
map: { name: 'name' },
autokey: { path: 'slug', from: 'name', unique: true }
});
Image.add({
name: { type: String, required: true },
image: { type: Types.CloudinaryImage, autoCleanup: true, required: true, initial: false },
description: { type: Types.Textarea, height: 150 },
});
Image.relationship({ ref: 'Gallery', path: 'heroImage' });
Image.relationship({ ref: 'Gallery', path: 'images' });
Image.register();
And the Galleries that contain these images looks like this:
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* Gallery Model
* =============
*/
var Gallery = new keystone.List('Gallery', {
map: { name: 'name' },
autokey: { path: 'slug', from: 'name', unique: true }
});
Gallery.add({
name: { type: String, required: true},
published: {type: Types.Select, options: 'yes, no', default: 'no', index: true, emptyOption: false},
publishedDate: { type: Types.Date, index: true, dependsOn: { published: 'yes' } },
description: { type: Types.Textarea, height: 150 },
heroImage : { type: Types.Relationship, ref: 'Image' },
images : { type: Types.Relationship, ref: 'Image', many: true }
});
Gallery.defaultColumns = 'title, published|20%, publishedDate|20%';
Gallery.register();
You will need to create Template Views and Routes to Handle this, but it isn't too much more work - these are just the Models - let me know if you would like me to post the routes I am using for this, I am using Handlebars for my views so that may not be as helpful.

How can I auto-case key names when saving to Mongoose?

I have an object:
{ SKU: 'TR1234',
Description: 'Item 1',
UoM: 'each',
client_id: '531382e3005fe0c926bd3957',
Meta: { Test: 'test1', Image: 'http://www.aol.com' } }
I'm trying to save it given my schema:
var ItemSchema = new Schema({
sku: {
type: String,
trim: true,
},
description: {
type: String,
trim: true,
},
company_id: {
type: Schema.ObjectId,
ref: 'Client',
},
createdOn: {
type: Date,
default: Date.now
},
updatedOn: {
type: Date,
default: Date.now
}
}, {versionKey: false});
But it doesn't save and I assume it's because of the capitalized key names. However, those are dynamically generated from a CSV which is parsed with https://github.com/Keyang/node-csvtojson
Ideas?
You can also just use a setter in your mongoose schema, like that:
function toLower (v) {
return v.toLowerCase();
}
var UserSchema = new Schema({
email: { type: String, set: toLower }
});
Just apply it to your fields.
There is also one more approach, just:
email : { type: String, lowercase: true }
Update for keys:
If you would like to change keys, you should the approach likes 'ecdeveloper' mentioned below. My answer was for values, so it makes sense to give this reputation to 'ecdeveloper'. Sorry for confusing.
Here is one more approach without creating a new object:
Object.prototype.keysToUpper = function () {
var k;
for (k in this) {
if (this.hasOwnProperty(k))
this[k.toLowerCase()] = this[k];
delete this[k];
}
return this;
};
What about calling toLowerCase() on each key from your object, and build a new object with lower case keys?
// Assumy your object name is obj
var newObj = {};
Object.keys(obj).forEach(function(key) {
newObj[key.toLowerCase()] = obj[key];
});
// Here you can save your newObj

Resources