Is it possible to show/hide fields in the KeystoneJS 5 AdminUI? - keystonejs

Basically what the title says -- we are working on a project where we'd like to be able to show and hide various fields based on the value of other fields. This seems to have been possible in KeystoneJS 4 but I see no mention of it in KeystoneJS 5.

dependsOn feature of keystoneJs v4 has not made it to latest KeystoneJs iteration. v5 (as we call it) is complete rewrite and does not have many features from v4.
however there is a Pull Request which may add this feature but unfortunately that is not the priority for the core team and they have not responded on the PR.
once that PR is merged you can do something like this
keystone.createList('Test field', {
fields: {
price: { type: Decimal, symbol: '$' },
currency: { type: Text, dependsOn: { $lt: { price: 3 } } },
hero: { type: File, adapter: fileAdapter, dependsOn: { $gt: { price: 3 } } },
markdownValue: { type: Markdown, dependsOn: { $gt: { price: 6 } } },
fortyTwo: {
type: Virtual,
graphQLReturnType: `Int`,
resolver: () => 42,
},
}});

Related

How to search for/select by included entity but include all related entities into result set

In my application, I am using sequelize ORM. There are several entities: A Tool can have Tags and Categories.
Now I want to search for all Tools, that have a specific Tag, but I want to include all relating Tags of that tool (not just the specific one). If I now place a where statement into the include, only specified Tags are included into the result set (see [2]). I tried to limit the Tags in the outer where statement (see [1]), but this does not help either.
Example
Tool A has Tags t1, t2 and t3. Now I want to search all Tools that have the Tag t3, but the result set shall contain all three tags.
Expected result:
Tool A
\
- Tag t1
- Tag t2
- Tag t3
db.Tool.scope('published').findAll({
where: { '$tool.tag.name$': filter.tag }, // [1] Does not work
include: [
{
model: db.User,
attributes: ['id', 'username']
},
{
model: db.Tag,
attributes: ['name'],
through: { attributes: [] },
// [2] Would limit the result specified tag
// where: {
// name: {
// [Op.and]: filter.tag
// }
// }
},
{
model: db.Category,
attributes: ['id', 'name', 'views'],
through: { attributes: ['relevance'] },
where: {
id: {
[Op.and]: filter.category
}
}
}
],
where: {
title: {
[Op.like]: `%${filter.term}%`,
}
},
attributes: ['id', 'title', 'description', 'slug', 'docLink', 'vendor', 'vendorLink', 'views', 'status', 'createdAt'],
order: [['title', 'ASC'], [db.Tag, 'name', 'ASC']]
})
I know I could perform this by performing a select via the Tag in the first place (db.Tag.findAll() instead of db.Tool.findAll(); I've already done this elsewhere in my project), but at the same time I also want to be able to filter by another entity (Category) the same way. So the Tool.findAll() should be the starting point.
Any help appreciated!
First off, you have two where clauses in your top-level query:
where: { '$tool.tag.name$': filter.tag }, // [1] Does not work
// ...
where: {
title: {
[Op.like]: `%${filter.term}%`,
}
},
I think your best approach is going to be with a literal subquery in the WHERE clause. Basically we want to find the ids of all of the tools that have the right tag and that contain the filter.term.
The subquery part for the WHERE looks something like...
SELECT ToolId FROM ToolTags WHERE TagId='t2';
Inspired by the subquery solution from this post Sequelize - subquery in where clause
// assuming your join table is named 'ToolTags' in the database--we need the real table name not the model name
const tempSQL = sequelize.dialect.QueryGenerator.selectQuery('ToolTags',{
attributes: ['ToolId'],
where: {
TagId: filter.tag
}})
.slice(0,-1); // to remove the ';' from the end of the SQL
db.Tool.scope('published').findAll({
where: {
title: {
[Op.like]: `%${filter.term}%`,
},
id: {
[Op.In]: sequelize.literal(`(${tempSQL})`)
}
},
include: [
{
model: db.User,
attributes: ['id', 'username']
},
{
model: db.Tag,
attributes: ['name'],
through: { attributes: [] },
},
// {
// model: db.Category,
// attributes: ['id', 'name', 'views'],
// through: { attributes: ['relevance'] },
// where: {
// id: {
// [Op.and]: filter.category
// }
// }
// }
],
attributes: ['id', 'title', 'description', 'slug', 'docLink', 'vendor', 'vendorLink', 'views', 'status', 'createdAt'],
order: [['title', 'ASC'], [db.Tag, 'name', 'ASC']]
})
I commented out your category join for now. I think you should try to isolate the solution for the tags before adding more onto the query.

Using rally app lookback API - unable to fetch defects that are tagged

I am using rally lookback API and creating a defect trend chart. I need to filter defects that do not have a tag "xyz".
Using the following:
this.myTrendChart = Ext.create('Rally.ui.chart.Chart', {
storeType: 'Rally.data.lookback.SnapshotStore',
storeConfig: {
find: {
_TypeHierarchy: "Defect",
State: { $lt: "Closed"},
Tags.Name: { $nin: ["xyz"] },
_ProjectHierarchy: projectOid,
_ValidFrom: {$gte: startDateUTC}
}
},
calculatorType: 'Calci',
calculatorConfig: {},
chartConfig: {
chart: {
zoomType: 'x',
type: 'line'
},
title: {
text: 'Defect trend'
},
xAxis: {
type: 'datetime',
minTickInterval: 7
},
yAxis: {
title: {
text: 'Number of Defects'
}
}
}
});
This does not return any data. Need help with the filter for tags.
Tags is a collection of tag-oids so you'll need to find and use the oid of the tag vs the name, at which point it'll just be Tags: { $nin: [oid] }. Caveat: technically, due to how expensive it is, $nin is unsupported (https://rally1.rallydev.com/analytics/doc/#/manual/48e0589f681160fc316a8a4802dc389f)...but it doesn't throw an error so maybe it works anyway.

set 'dependsOn' on a Relationship field in KeystoneJS

I wonder if there is a way to achieve dependsOn option for a Types.Relationship field in KeystoneJS.
Example:
Event.add({
category: { type: Types.Relationship, ref: 'EventCategory' },
singer: { type: String, dependsOn: { category: { type: 'concerts' } } },
});
Currently, I can only walk around this by manually set dependsOn to the id of the related field.

Keystone.js nested categories

I am trying to implement nested categories for Post model.
What I have:
Post.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' } },
content: {
extended: { type: Types.Html, wysiwyg: true, height: 300 },
},
categories: { type: Types.Relationship, ref: 'PostCategory', index: true }
});
And category
PostCategory.add({
name: { type: String, required: true },
subCategories: { type: Types.TextArray }
});
Now I can add a list of subcategories to each category.
What I can't do is to display subcategories while creating a post. Also if I change category I need to load sub categories related to selected category.
My plan was to achieve that with watch functionality but it seems only works on save.
Another thing I was thinking about was to add subcategories as relationship, see ref:
categories: { type: Types.Relationship, ref: 'PostCategory.subCategories', index: true }
But it isn't working as well.
So, if anybody has any ideas how to achieve that, please share.
Thanks.
P.S. Don't hesitate to ask any additional information.
I created nested categories by creating a new model 'PostSubCategory' that allows the user to assign the parent category to the child category when they create the child category:
var keystone = require('keystone');
var Types = keystone.Field.Types;
/**
* PostSubCategory Model
* ==================
*/
var PostSubCategory = new keystone.List('PostSubCategory', {
autokey: { from: 'name', path: 'key', unique: true },
});
PostSubCategory.add({
name: {
type: String,
required: true
},
parentCategory: {
type: Types.Relationship,
ref: 'PostCategory',
required: true,
initial: true
}
});
PostSubCategory.relationship({ ref: 'Post', path: 'subcategories' });
PostSubCategory.register();
Then in my Post.js, I add a field to choose a subcategory with a filter on that field to only select from subcategories that are children of the parent category selected:
subcategory: {
type: Types.Relationship,
ref: 'PostSubCategory',
many: false,
filters: { parentCategory: ':categories' }
}
I'm not sure how well this would work for deeper nesting, and I do have an issue in the edit Post admin ui where changing the parent category for a post doesn't update the available subcategories to choose from until you save and refresh. But it got me far enough along for getting parent/child categories to work.

Ember-data building relationships without ids

I'm trying to build an Ember application with a MongoDB backend. Ember-data now supports embedded objects which makes it really easy and great to be able to pull the objects right out of the nested document.
My problem is in figuring out how to relate objects to each other.
Lets look at an example of a class room with students and assignments.
{
students: [
{ name: 'Billy' },
{ name: 'Joe' }
],
assignments: [
{ name: 'HW1', score: 70, student_name: 'Billy' },
{ name: 'HW2', score: 80, student_name: 'Billy' },
{ name: 'HW1', score: 60, student_name: 'Joe' },
{ name: 'HW2', score: 75, student_name: 'Joe' }
]
}
How would I build a relationship for the student so that I could pull back all of their assignments?
Related, I'm trying to figure out how to relate objects that are nested inside each other. I created a jsbin trying to build a relationship between nested objects (going up the tree rather than just down) but I'm not sure how to do it.
You can use the repo below to guide you on how to do embedded association. Though they are not using mongodb, the important thing is on the ember-data side, they are doing embedded association.
https://github.com/dgeb/ember_data_example/blob/master/app/assets/javascripts/controllers/contact_edit_controller.js
Note that here App.PhoneNumber is embedded in App.Contact. But it should give you an idea on how to go abut resolving yours.
App.Contact = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string'),
notes: DS.attr('string'),
phoneNumbers: DS.hasMany('App.PhoneNumber'),
});
App.PhoneNumber = DS.Model.extend({
number: DS.attr('string'),
contact: DS.belongsTo('App.Contact')
});
https://github.com/dgeb/ember_data_example/blob/master/app/assets/javascripts/store.js
App.Adapter = DS.RESTAdapter.extend({
bulkCommit: false
});
App.Adapter.map('App.Contact', {
phoneNumbers: {embedded: 'always'}
});
App.Store = DS.Store.extend({
revision: 12,
adapter: App.Adapter.create()
});
https://github.com/dgeb/ember_data_example/blob/master/app/assets/javascripts/controllers/contact_edit_controller.js
App.ContactEditController = Em.ObjectController.extend({
needs: ['contact'],
startEditing: function() {
// add the contact and its associated phone numbers to a local transaction
var contact = this.get('content');
var transaction = contact.get('store').transaction();
transaction.add(contact);
contact.get('phoneNumbers').forEach(function(phoneNumber) {
transaction.add(phoneNumber);
});
this.transaction = transaction;
},
save: function() {
this.transaction.commit();
},
addPhoneNumber: function() {
this.get('content.phoneNumbers').createRecord();
},
});