Class

Model

kiss.data.Model(config)

Represents a Model.

The models are the central elements to structure the data of your applications. The jargon can be slightly different depending on the frameworks, therefore, let's ensure we're talking the same language when it comes to Model, Field, Record, and Collection.

Model:

  • a model is a representation of a real-world entity: a user, a car, an invoice, a task, a project, a dream, a quark
  • a model defines the properties of the entity, but is not that entity: a model defines what is a car, but is not a car
  • a model must define the properties of the entity. Ex: firstName, lastName, email
  • a model can have custom methods. Ex: invite(), sendEmail(), ...

Field:

  • a field is a single property of a model. Ex: firstName
  • a field has an id. Ex: firstName, lastName, bwxJF4yz
  • a field has a label. Ex: First name, Last name
  • a field has a type. Ex: text, number, date, select

Record:

  • a record is an entity created thanks to the model definition
  • a record has data. Ex: firstName: Bob, lastName: Wilson, email: bob@wilson.com
  • a record inherits the model's methods. Ex: myUser.invite(), myUser.sendMail()

Collection:

  • a collection is an array of multiple records
  • a collection acts as a proxy for model's data, with specific projection, filtering, sorting, grouping, paging...
  • it means multiple collections can be bound to the same model
  • a collection is useful to cache a specific set of records and represent them into the UI
  • in KissJS, each record of a collection is not just a JSON object, but it's a Record's instance, so you can directly use its methods

In KissJS:

  • a model needs basic properties like id, name, items
  • a model needs to know its singular and its plural name: man / men, child / children, box / boxes, spy / spies
  • a model can have custom methods: sendEmail(), archive(), processTask()
  • to be able to represent the model visually in KissJS applications, it has an icon and a color property
  • to classify the models semantically, KissJS can have meta-data like tags and domains

You define a model passing its names (singular and plural) and its items:

let spyModel = new kiss.data.Model({
 name: "spy",
 namePlural: "spies",
 items: [
     {label: "Spy code number"},
     {label: "Spy real name"}
 ]
})

Important note about KissJS conventions:

  • we define model items instead of fields
  • fields have a label instead of a name

This is because KissJS shares a single convention for both the model and its direct UI representation: the form:

  • forms have items (which can be fields, panels, buttons, images...)
  • panels contain fields or other items (like buttons)
  • fields have a label
  • panels have a title

It means you can also define a model like this:

let spyModel = new kiss.data.Model({
 name: "spy",
 namePlural: "spies",
 items: [
     {
         type: "panel",
         title: "General informations",
         items: [
             {label: "Spy code number"},
             {label: "Spy fake name"}
          ]
     },
     {
         type: "panel",
         title: "Secret informations",
         items: [
             {label: "Spy real name"},
             {label: "Last mission date"}
         ]
     }
 ]
})

This model will be automatically represented by a form with 2 sections.

If you just give a label to a field, its id will be randomly generated. To keep control over your field ids, just add them to the field definition:

let newModel = new kiss.data.Model({
 name: "spy",
 namePlural: "spies",
 items: [
     {id: "code", label: "Spy code number"},
     {id: "name", label: "Spy real name"}
 ]
})

The default type is "text", but you can of course define the field's type and validation rules:

  • using validation property and a regex
  • using validationType property for pre-defined rules (alpha, alphanumeric, email, url, ip)
let newModel = new kiss.data.Model({
 name: "spy",
 namePlural: "spies",
 items: [
     {id: "code", label: "Spy code number", type: "text", validation: /^\d{3}$/}, // Force 3 digits, like "007"
     {id: "name", label: "Spy real name", type: "text"},
     {id: "missions", label: "Number of missions", type: "number"},
     {id: "lastMission", label: "Last mission date", type: "date"},
     {id: "secretEmail", label: "Spy secret mailbox", type: "text", validationType: "email"}, // Predefined validation type
     {id: "isActive", label: "Is this spy still active?", type: "checkbox"}
 ]
})

The field type is used to define how the field is rendered into the UI, and its data type is implicit. In the previous example, the checkbox type has implicitly a boolean data type.

Once a model has been defined, you can create model instances. In KissJS, a model instance is called a Record:

let userModel = kiss.app.models.user
let userRecord = userModel.create({firstName: "Bob", lastName: "Wilson"})

Fields

To define a field, the minimum is to have a field label:

{label: "First name"}

If you just set a label, KissJS will automatically:

If you prefer having control over your field ids, do:

{id: "firstName", label: "First name"}

Field types

Because KissJS is primarily a UI library, it is designed to make it easier to display the model in the user interface. It's a very opiniated architecture choice. The point here is to keep the focus on the UI and not the underlying structure. Doing that way allows us to just throw a model definition to KissJS and it will automagically generate the UI as a form.

Basic field types:

Field type Data type Displayed as
text text input field
textarea text textarea field
number number number field
date ISO 8601 extended. Example: 2021-04-01T23:20:15Z date picker
checkbox boolean checkbox
select string[] single or multi-value dropdown list
rating number a rating field with stars (or other icons)
slider number a slider field
icon string a single icon field
iconPicker string an icon picker (using Font Awesome icons at the moment)
color string a single color field
colorPicker string a color picker
attachment object file upload control

Special field types (non mandatory extensions):

Field type Data type Displayed as
aiTextarea text textarea field with AI suggestions
aiImage object file upload control with AI image generation
directory string[] dropdown list to select users and/or groups
link * list to show one or multiple records and create relationships between tables
lookup * computed field that lookup a value from a single foreign linked record
summary * computed field that makes a summary of multiple foreign linked records (a sum, a percentage, a concatenation...)

Roadmap for news field types:

  • automatic number
  • address (with search / completion and map)
  • checkbox group (= just another UI for Select field with "multiple: true" option)
  • radio group (= just another UI for Select field with "multiple: false" option)
  • image (= just another UI for the attachment field)

Methods:

By default, every instanciated record will have default methods:

  • save
  • read
  • update
  • delete

But you can also define custom methods as well (just ensure their name doesn't conflict with default CRUD methods):

let contactModel = new kiss.data.Model({
 name: "contact",
 namePlural: "contacts",
 items: [
     {id: "name", label: "Contact name", type: "text"},
     {id: "email", label: "Email", type: "text", validationType: "email"}
 ],
 methods: {
     sendEmail: function (subject, message) {
         yourSmtpService.sendMessage(this.email, subject, message)
     }
 }
})

// Instanciate a new record
let newContact = contactModel.create({name: "Bob", email: "bob@wilson.com"})

// Using its custom method
newContact.sendEmail("Urgent", "Do that")
Constructor

# new Model(config)

Parameters:
Name Type Attributes Description
config object

model configuration

mode string <optional>

"memory" | "offline" | "online"

id string <optional>
templateId string <optional>

id of the original template model (used to keep track of the source model)

name string <optional>

Name of the model: Lead

namePlural string <optional>

Plural name: Leads

items Array.<object>

Array for field definitions

acl object

model's acl (Access Control List)

methods object

model's methods

features object <optional>

model's features (workflow, comments, ...)

icon string <optional>

The Font Awesome icon class. Example: "fas fa-check"

color string <optional>

Hexa color. Ex: "#00aaee"

tags Array.<string> <optional>

Ex: ["Leads", "Sales", "CRM", "HRM"]

domains Array.<string> <optional>

Ex: ["banking", "insurance"]

View Source common/dataModel.js, line 309

Example
// Register a new model
let leadModel = new kiss.data.Model({
 name: "lead",
 namePlural: "leads",

 icon: "fas fa-user",
 color: "#00aaee",

 // Define model fiels
 items: [
     {
         label: "Name",
         id: "name",
         type: "text"
     },
     {
         primary: true, // Primary key field
         label: "Email",
         id: "email",
         type: "text",
         validationType: "email"
     },
     {
         label: "Category",
         id: "category",
         type: "select",
         options: [
             {
                 label: "National"
                 value: "NAT",
                 color: "#00aaee"
             },
             {
                 label: "International",
                 value: "INT",
                 color: "#aa00ee"
             }
         ]
     }
 ],

 // Define model methods
 methods: {
     // Get all the pending deals for this lead
     getPendingDeals: async function() {
         return await kiss.app.collections["deal"].find({
             filter: {
                 $and: [
                     {leadId: this.id},
                     {status: "pending"}
                 ]
             }
         })
     }
 }
})

// Your can create a new instance like this
let myLead = leadModel.create({name: "Bob Wilson", email: "bob@wilson.com", category: "INT"})

// Creating a new instance happens in memory. You have to save it manually with
await myLead.save()

// Updating an instance using default CRUD methods
await myLead.update({name: "Bob Wilson Junior"})

// Calling a custom method
let pendingDeals = await myLead.getPendingDeals()

// Deleting an instance
await myLead.delete()

Methods

# _initACLFields()

Init the model's ACL fields

This is only used server-side to define the fields that holds ACL entries (users and/or groups). When a user or group is deleted, the ACL entries in the model's fields are updated accordingly. This is done by kiss.directory.cleanupAllUserReferences

View Source common/dataModel.js, line 397

this

# async addField(config, sectionIdopt) → {boolean}

Create a new field configuration. This method also updates the views that are connected to the model.

Parameters:
Name Type Attributes Description
config object

New field config

sectionId string <optional>

Optional section id. If provided, adds the field at the end this section

View Source common/dataModel.js, line 1281

true in case of success

boolean

# checkFormula(fieldId) → {Array.<string>}

Check if the field labels used in the formula are still valid. If not, returns the list of invalid field labels.

Parameters:
Name Type Description
fieldId string

View Source common/dataModel.js, line 2039

Array with the wrong field labels (empty if OK)

Array.<string>

# async checkPermission(action) → {boolean}

Check the permission (client-side) to perform an action on the model.

Parameters:
Name Type Description
action string

"update" | "delete"

View Source common/dataModel.js, line 535

true if the permission is granted

boolean

# async connectToModel(foreignModelId, fieldSetup) → {object}

Connect the model to a foreign model using a field.

To connect the 2 models, a symmetric field is created in the foreign model.

Parameters:
Name Type Description
foreignModelId string

id of the foreign model to connect

fieldSetup object

Setup of the field in the local model

View Source common/dataModel.js, line 1198

The generated foreign field

object

# create(recordDataopt, inheritopt) → {object}

Create a new Record from this model

This does not save the record automatically: to save the record into the database, use the save() method of the created record.

Parameters:
Name Type Attributes Description
recordData object <optional>

The new record's data

inherit boolean <optional>

If true, create a blank record then assign recordData to it

View Source common/dataModel.js, line 503

The new Record object

object
Example
userModel = kiss.app.models["user"]
let Bob = userModel.create({firstName: "Bob", lastName: "Wilson"})
await Bob.save()

# createFromLabels(record)

Create a record using field labels as keys

Parameters:
Name Type Description
record object

View Source common/dataModel.js, line 518

The record

Example
userModel = kiss.app.models["user"]
let Bob = userModel.createFromLabels({"First name": "Bob", "Last name": "Wilson"})
await Bob.save()

# async deleteField(fieldId) → {boolean}

Delete a field configuration and update all the views that are connected to this model. A primary field can't (and must not) be deleted.

Parameters:
Name Type Description
fieldId string

View Source common/dataModel.js, line 1411

false if the field couldn't be deleted (primary field)

boolean

# async deleteLinksToModel(foreignModelId) → {integer}

Delete all the links that were auto-generated for a given model.

Parameters:
Name Type Description
foreignModelId string

View Source common/dataModel.js, line 1172

The number of deleted links

integer

# exportAsJSON()

Export the model definition as JSON.

This is useful to be able to import/export pre-built application templates.

View Source common/dataModel.js, line 1827

# async generateLinksToModel(config) → {Array.<object>}

Generate link records between 2 models when their 2 given fields are equal.

Note: it does not save the links into the database. It's up to the caller function to decide what to do with the links (create them or cancel)

Parameters:
Name Type Description
config object
foreignModelId object
sourceLinkFieldId object
sourceFieldId object
foreignLinkFieldId object
foreignFieldId object

View Source common/dataModel.js, line 1132

link records

Array.<object>

# getActiveFields() → {Array.<object>}

Get visible fields (= non deleted)

View Source common/dataModel.js, line 1017

Array of field definitions

Array.<object>

# getBatchableFields() → {Array.<object>}

Get the fields which can be used for batch operations.

View Source common/dataModel.js, line 1100

The list of fields

Array.<object>

# getFeatureFields() → {Array.<object>}

Get the fields brought by the model's active plugins

View Source common/dataModel.js, line 935

Array of field definitions

Array.<object>

# getField(fieldId) → {object}

Get a field by id

Note: if the field is not found, the method tries to find the field by its label

Parameters:
Name Type Description
fieldId string

View Source common/dataModel.js, line 735

The field definition

object
Example
let myField = myModel.getField("xD12z4ml00z")

// Returns...
{
     id: "yearlyIncome",
     label: "Yearly income",
     type: "number",
     precision: 2,
     formula: "{{Monthly income}} * 12",
}

# getFieldByLabel(fieldLabel) → {object}

Get the first field matching a label.

Note:

  • if the field label is not found, it defaults to searching the field id
  • deleted field are not taken into consideration
Parameters:
Name Type Description
fieldLabel string

View Source common/dataModel.js, line 766

The field definition

object
Example
let myField = myModel.getFieldByLabel("Project name")

# getFields() → {Array.<object>}

Get the model's fields

In KissJS, the model can be directly defined by a complex form with multiple sections and sub items. This method explores the tree and returns only the items which are fields.

Important: deleted fields are also returned, with a flag deleted = true

View Source common/dataModel.js, line 786

Array of field definitions

Array.<object>

# getFieldsByType(types) → {Array.<object>}

Get only the fields of specific types

Parameters:
Name Type Description
types string | Array.<string>

Single type or array of types. Ex: "text", "textarea", "number", or ["date", "checkbox", "select"]

View Source common/dataModel.js, line 1027

The fields of the required type, or []

Array.<object>

# getFilterableFields() → {Array.<object>}

Get the fields which can be used for filtering

View Source common/dataModel.js, line 1060

The list of filterable fields

Array.<object>

# getFormulaFields() → {Array.<object>}

Get the fields which can be used inside a formula

View Source common/dataModel.js, line 1068

The list of formula fields

Array.<object>

# getGroupableFields() → {Array.<object>}

Get the fields which can be used for grouping

View Source common/dataModel.js, line 1050

The list of groupable fields

Array.<object>

# getLinkField(foreignModelId) → {object}

Search inside a model which field links to a foreign model

Parameters:
Name Type Description
foreignModelId string

Foreign model id

View Source common/dataModel.js, line 1085

The field that links to the foreign model

object

# getPrimaryKeyField() → {object}

Get the primary field of this model

View Source common/dataModel.js, line 746

The primary field, or the model's 1st field if it wasn't found

object

# getSection(fieldId) → {object}

Get a section by id

Note: if the section is not found, the method tries to find the section by its title

Parameters:
Name Type Description
fieldId string

View Source common/dataModel.js, line 1648

The section definition

object
Example
let mySection = myModel.getSection("General informations")

// Returns...
{
     id: "aE7x450",
     title: "General informations",
     items: [
         // ... Section items
     ]
}

# getSectionByTitle(sectionTitle) → {object}

Get the first section matching a title.

Note: if the section title is not found, it defaults to searching the section id

Parameters:
Name Type Description
sectionTitle string

View Source common/dataModel.js, line 1665

The section definition

object
Example
let mySection = myModel.getSectionByTitle("General informations")

# getSections() → {Array.<object>}

Get the model's sections

In KissJS, the model can be directly defined by a complex form with multiple sections and sub items. This method explores the tree and returns only the items which are sections.

View Source common/dataModel.js, line 812

Array of sections definitions

Array.<object>

# getSortableFields() → {Array.<object>}

Get the fields which can be used for sorting

View Source common/dataModel.js, line 1041

The list of sortable fields

Array.<object>

# getSystemFields() → {Array.<object>}

Initialize the system fields

View Source common/dataModel.js, line 854

Array of system fields

Array.<object>

# getViews() → {Array.<Record>}

Get all the views that are connected to this model

View Source common/dataModel.js, line 1678

Array of records containing the view configurations

Array.<Record>

# getViewsByUser(userId) → {Array.<object>}

Get the views a user is allowed to access.

A user can see a view if:

  • he is the account owner
  • he is the view creator
  • the view read access is allowed to all authenticated users
  • he is mentionned in the field "accessRead"
Parameters:
Name Type Description
userId string

View Source common/dataModel.js, line 1699

The list of authorized views

Array.<object>

# hasSections() → {boolean}

Check if the model has sections.

View Source common/dataModel.js, line 1337

boolean

# isFirstItemInSection(itemId) → {boolean}

Check if an item is the first in its section. (used to perform checks in the form builder)

Parameters:
Name Type Description
itemId string

View Source common/dataModel.js, line 1525

boolean

# async saveItems()

Save the model's items

View Source common/dataModel.js, line 708

# async updateField(fieldId, config, shouldUpdateFormula) → {boolean}

Update a field configuration. This method also updates the views that are connected to the model.

Parameters:
Name Type Description
fieldId string
config object

New field config

shouldUpdateFormula boolean

If true, re-compute the field value on every record of the collection

View Source common/dataModel.js, line 1352

true in case of success

boolean

# async updateFieldFormula()

Recompute the computed field value on every record of the collection.

View Source common/dataModel.js, line 1388

# async updateSection(sectionId, newSectionConfig)

Update a section configuration

Parameters:
Name Type Description
sectionId string
newSectionConfig object

View Source common/dataModel.js, line 1550