Build dynamic user interfaces quickly and easily! Use the forestay generator to scaffold complete CRUD interfaces using just your SailsJS model attributes.
Data First. Sometimes building great user interfaces requires a full understanding of how data are structured and how users interact with them. Forestay is a way to quickly build data models with a sensible, basic user interface.
Data manipulation. When you get your data, a lot of times you want to display it in a different way. That's why the forestay object is completely mutable throughout the request process.
Forestay isn't meant to be your final UI.
It's meant to be your first UI.
Instant gratification
$ npm install sails-generate-forestay --saveYou may need to merge following into your .sailsrc file. :
{
"modules": {
"forestay": "sails-generate-forestay"
}
}$ sails generate forestay (modelname)You will then be shown the routing code to place into your routes.js file. This will route actions through the Forestay controllers and give you a complete CRUD interface.
Please visit the wiki article on using existing models
Note that this is an early release of this generator;
- Model
string- Text inputs - Model
number- Integers though inputnumberattribute - Model
boolean- truthy/falsey represented by HTML select - Model
text- HTML Text Area - Model
enumwill show as a<select>list forstringandnumbertypes
instrumentType: {
type:"string",
enum: ["stringed","woodwind","keys","electronic","brass"]
},- Association type
collection- association of many records. Note thatpopulateByis required for the UI to know which field to use.
musicians: {
collection:"musician",
via: "instruments",
meta: {
forestay:{
populateBy: "name"
}
}
}- Collections as multi-select list (Thanks @ReversedK)
users:{
collection:"User",
via:"usergroups",
meta:{
forestay:{
displayAs:'select',
populateBy: "name"
}
}
}- Association type
model- association of a single record.populateByis also required here.
make: {
model: 'make',
meta:{
forestay: {
populateBy: "name"
}
}
}-
model.attributes.required- Supported byrequiredinput attribute -
model.attributes[key].meta.forestay.hideInIndex === trueHide this field in the forestay index -
model.attributes[key].meta.forestay.mutable === false- Field can be edited on create, but not after create. -
model.attributes[key].meta.forestay.hideInForm === trueHide this field in all forms (may cause problems if the field is required!) -
model.attributes[key].meta.forestay.showDescriptionInIndexWill show a description for this attribute on the index as a popover -
model.attributes[key].meta.forestay.uiType = “textarea”Show a text area for string types -
model.attributes[key].meta.forestay.replaceIndexHtml- When in the index, replace with given HTML template. (TODO: Can be a EJS template!) - Warning, this will evaluate all HTML, EJS and Javascript and could be an entry to your system. If you make this user editable, you are potentially introducing a security risk if you are not sanitizing your inputs -
model.forestay.actionsThese actions create UI buttons for your model index or individual records -
model.forestay.index.hideAddButtonRemoves the "Add" record button from index. You can alternately create an action button to replace it. -
model.forestay.index.defaultSortDefault sort column for index. Default method is "ASC" for ascending, can also use "DESC" for descending
{
attribute: "title",
method: "ASC"
},model.attributes[key].descriptionshows in CREATE/Update viewsmodel.attributes[key].meta.forestay.prefillable === trueAllow values to be prefilled from the URL query when this is set to true. For example a query parm of?pet=12will prefill thepetfield with the value of12on the create form.model.forestay.beforeCreate- we opted to use this instead of Sails beforeCreate callback, because we have the req and res objects available. theforestay.saveobject will contain the values that will be saved
beforeCreate: function(req, res, forestay, next){
/* ... */
return next(null, forestay)
}model.forestay.afterCreate- This gets run after a new record is created.
afterCreate: function(req,res,forestay,next){
console.log("AFTER CREATE!", forestay.saved)
return next(null, forestay)
},model.forestay.afterUpdate- This gets run after a record is updated.
afterUpdate: function(req,res,forestay,next){
console.log("AFTER CREATE!", forestay.saved)
return next(null, forestay)
},model.forestay.beforeUpdate- We use this instead of the Sails beforeUpdate callback, because we want access to REQ,RES and saved ids. The saved values are contained inforestay.saveand the original ID isforestay.idif you need to look up pre-saved values in the database
beforeUpdate: function(req, res, forestay, next){
/* ... */
return next(null, forestay)
}model.forestay.index.beforeRendercallback, gets fired before the index page is rendered
beforeRender: function (req, res, forestay, next) {
/* ... modify and return forestay .. */
var errors
if (errors) return next(errors)
return next(null,forestay)
},model.forestay.createUpdateReturnUrlReturn URL after create/update. Can use standard express-style routing parameters.
createUpdateReturnUrl: "/user/:id/view",model.forestay.createUpdate.beforeUpdateCreateViewcallback, gets fired before the create/update page is rendered.
beforeUpdateCreateView: function (req, res, forestay, next){
/* ... */
return next(null,forestay)
}- Filtering via
model.attributes[key].meta.forstay.filterable&model.attributes[key].meta.forstay.hideFilter
actions:{
"/get-cpu/":{
type:"index",
label: "Get CPU Value"
},
"/instrument/play/:id":{
type:"record",
label: "Play Instrument"
}
},foo:{
type: 'collection',
via :'bar',
Meta:{
Forestay:{
filterCriteria: function(req,res, forestay, cb){
return {
where:{
owner: req.user.id
}
}
}
}
}
}model.forestay.actions.[actionKey].buttonClassCustom classes for your action buttonsmodel.forestay.actions.[actionKey].imageUrlCustom image for your action buttonsroutes.jsrendered menu in Forestay layouts. Setforestay.hideFromMenu = trueto hide a route from the menu. AnyGETitems will otherwise end up in here. Also useforestay.linkNameto specify display friendly names andforestay.modelso forestay understands what model the router is going to use.model.attributes[key].meta.forestay.updateCreateFilterByfor Models Create Filter By - Only show models with specified property value. for example, only models related to a specific record by id- Replace row data with
forestay.config.index.replaceIndexRowHtml. Note that this is rendered as actual HTML. TODO: EJS templating
replaceIndexRowHtml: function (req, res, forestay, row, cb) {
row.forestay_replace = {
user: 'foobar'
}
cb(null, forestay, row);
},beforeRouteBefore each route, call this callback
beforeRoute: function(req, res, forestay, next){
console.log("cool!")
return next(null, forestay)
},model.forestay.index.aboveTableCustomHtmlCustom html that gets displayed above the tablemodel.forestay.index.hideEditButtonhide edit button in indexesmodel.forestay.index.hideDeleteButtonhide delete button in indexesmodel.forestay.index.filterLogicalOperator === "or"set to "or" and filters will user "or" logical oporator. Default is "and"model.forestay.createdAtLabel- Label for the "Created At" label. For example, if you wanted to change "Created At to "Connected" in the index andbooleantype replacement text
privacy: {
type: "boolean",
meta: {
forestay: {
label:"Privacy",
booleanLabels: {
true: "Private",
false: "Public"
}
}
}
},forestay.config.forestay.index.filterOverrideoverride filters with sailsJS filter syntax https://sailsjs.com/documentation/concepts/models-and-orm/query-language- custom headers/footers: Properties exposed in model.forestay (global), model.forestay.index (index only), model.forestay.create (create only), model.forestay.update (update only), model.forestay.createUpdate (create and update)
footerHtml: “<h1>This is the footer</h1>”,
headerHtml: “<h1>This is the header</h1>”,model.attributes[key].meta.forestay.createUpdateUi:'tagging'- A collection UI for tagging on create/update. New tags are created ifallowAddition===true- Pagination: Pagination is built in. Just set
forestay.config.forestay.itemsPerPageto an integer. forestay.args- You have access to the URL arguments throughforestay.args. You can use this throughout the request process.
model.attributes[key].meta.forestay.sortable- For string attributes, allow index sortingforestay.index['searchable'] = true- for string and model attributes, enable searching.
defaultLayout- use an alternate local layout instead of the default Forestay layoutbackground- the URL location of a background image like/images/bg.jpg
Built and tested as of SailsJS version 1.01
This forestay generator is available under the MIT license.
This is a generator for the Sails framework.




