PropDefinition
Defines the type, initial value, and get, set, and serialize behavior for an
observable property. These behaviors can be specified with as an Object, String,
Constructor function, Array, a getter expression, or setter expression.
Object
Defines multiple behaviors for a single property.
{
propertyName: {
default: function() { /* ... */ },
Default: Constructor,
type: function() { /* ... */ },
Type: Constructor,
get: function() { /* ... */ },
value: function() { /* ... */ },
set: function() { /* ... */ },
serialize: function() { /* ... */ },
identity: Boolean
}
}
Options
-
default
{default()}:Specifies the initial value of the property or a function that returns the initial value.
import {DefineMap} from "can"; // A default age of `0`: const Person = DefineMap.extend( { age: { default: 0 }, address: { default: function() { return { city: "Chicago", state: "IL" }; } } } ); const person = new Person(); console.log( person.age ); //-> 0 -
Default
{Default()}:Specifies a function that will be called with
newwhose result is set as the initial value of the attribute.import {DefineMap, DefineList} from "can"; // A default empty DefineList of hobbies: const Person = DefineMap.extend( { hobbies: { Default: DefineList } } ); const person = new Person(); console.log( person.hobbies instanceof DefineList ); //-> true -
type
{type()}:Specifies the type of the property. The type can be specified as either a function that returns the type coerced value or one of the types names.
import {DefineMap} from "can"; const Person = DefineMap.extend( { age: { type: "number" }, hobbies: { type: function( newValue ) { if ( typeof newValue === "string" ) { return newValue.split( "," ); } else if ( Array.isArray( newValue ) ) { return newValue; } } } } ); const person = new Person({ age: "20", hobbies: "basketball,billiards,dancing" }); console.log( person.age, person.hobbies ); //-> 20, ["basketball", "billiards", "dancing"] -
Type
{Type()}:A constructor function that takes the assigned property value as the first argument and called with new. For example, the following will call
new Address(newValue)with whatever non null, undefined, or address type is set as aPerson's address property.import {DefineMap} from "can"; const Address = DefineMap.extend( { street: "string", state: "string" } ); const Person = DefineMap.extend( { address: { Type: Address } } ); const person = new Person({ address: { street: "Example Ave.", state: "IL" } }); console.log( person.address.serialize() ); //-> {state: "IL", street: "Example Ave."} -
get
{get(lastSetValue)}:A function that specifies how the value is retrieved. The get function is converted to an async compute. It should derive its value from other values on the object. The following defines a
pagegetter that reads from a map's offset and limit:import {DefineMap} from "can"; const Book = DefineMap.extend( { offset: "number", limit: "number", page: { get: function( newVal ) { return Math.floor( this.offset / this.limit ) + 1; } } } ); const book = new Book( {offset: 10, limit: 5} ); console.log( book.page ) //-> 3A
getdefinition makes the property computed which means it will not be enumerable by default. -
value
{value()}:A function that listens to events and resolves the value of the property. This should be used when get is unable to model the right behavior. The following counts the number of times the
pageproperty changes:import {DefineMap} from "can"; const Book = DefineMap.extend( { page: "number", pageChangeCount: { value( prop ) { let count = 0; // When page changes, update the count. prop.listenTo( "page", function() { prop.resolve( ++count ); } ); // Set initial count. prop.resolve( count ); } } } ); const book = new Book(); book.on("pageChangeCount", () => {}); book.page = 1; book.page += 1; console.log( book.pageChangeCount ); //-> 2A
valuedefinition makes the property computed which means it will not be enumerable by default. -
set
{set(newVal, resolve)}:A set function that specifies what should happen when a property is set.
setis called with the result oftypeorType. The following defines apagesetter that updates the map's offset:import {DefineMap} from "can"; const Book = DefineMap.extend( { offset: "number", limit: "number", page: { set: function( newVal ) { this.offset = ( parseInt( newVal ) - 1 ) * this.limit; } } } ); const book = new Book({ limit: 5 }); book.page = 10; console.log( book.offset ); //-> 45 -
serialize
{serialize()}:Specifies the behavior of the property when serialize is called.
By default, serialize does not include computed values. Properties with a
getdefinition are computed and therefore are not added to the result. Non-computed properties values are serialized if possible and added to the result.import {DefineMap} from "can"; const Todo = DefineMap.extend( { date: { type: "date", serialize: function( value ) { return value.getTime(); } } } ); const todo = new Todo( {date: Date.now()} ); console.log( todo.serialize() ); //-> {date: 1535751516915} -
identity
{identity}:Specifies the property that uniquely identifies instances of the type.
import {DefineMap, Reflect} from "can"; const Grade = DefineMap.extend( "Grade", { classId: {type: "number", identity: true}, studentId: {type: "number", identity: true}, grade: "string" } ); const myGrade = new Grade( {classId: 12345, studentId: 54321, grade: "A+"} ) console.log(Reflect.getIdentity(myGrade)); //-> "{'classId':12345,'studentId':54321}"
function()
Either creates a method or Defines a Type setting with a constructor function. Constructor functions are identified with isConstructorLike.
{
propertyName: Constructor
}
OR
{
propertyName: function() {}
}
For example:
{
subMap: DefineMap // <- sets Type to DefineMap
}
OR
{
increment: function() {
++this.count;
} // <- sets method prop
}
Array
Defines an inline can-define/list/list Type setting. This is used as a shorthand for creating a property that is an can-define/list/list of another type.
{
propertyName: [Constructor | propDefinitions]
}
For example:
import {DefineMap} from "can";
const User = DefineMap.extend( {username: "string", password: "string"} );
const TodoList = DefineMap.extend( {
users: [ User ],
todos: [ { complete: "boolean", name: "string" } ]
} );
const user1 = new User( {username: "JMeyers", password: "12345"} );
const user2 = new User( {username: "PStrozak", password: "54321"} );
const myList = new TodoList( {
users: [ user1, user2 ],
todos: [ {complete: true, name: "Write this example"} ]
} );
console.log( myList.serialize() );
GETTER
Defines a property's get behavior with the syntax.
import {DefineMap} from "can";
const Example = DefineMap.extend( {
get propertyName() { return true; }
} );
const e = new Example();
console.log( e.propertyName ); //-> true
For example:
import {DefineMap} from "can";
const Person = DefineMap.extend( {
first: "string",
last: "string",
get fullName() {
return this.first + " " + this.last;
}
} );
const person = new Person( {first: "Justin", last: "Meyer"} );
console.log( person.fullName ); //-> "Justin Meyer"
This is a shorthand for providing an object with a get property like:
import {DefineMap} from "can";
const Person = DefineMap.extend( {
first: "string",
last: "string",
fullName: {
get: function() {
return this.first + " " + this.last;
}
}
} );
const person = new Person( {first: "Justin", last: "Meyer"} );
console.log( person.fullName ); //-> "Justin Meyer"
You must use an object with a get property if you want your get to take the lastSetValue or resolve arguments.
SETTER
Defines a property's set behavior with the set syntax.
{
set propertyName( newValue ) { /* ... */ }
}
For example:
import {DefineMap} from "can";
const Person = DefineMap.extend( {
fullName: {
set(newValue) {
const parts = newValue.split(" ");
this.first = parts[0];
this.last = parts[1];
}
}
} );
const person = new Person( {fullName: "Justin Meyer"} );
console.log( person.first ); //-> "Justin"
console.log( person.last ); //-> "Meyer"
This is a shorthand for providing an object with a set property like:
import {DefineMap} from "can";
const Person = DefineMap.extend( {
fullName: {
set: function(newValue) {
const parts = newValue.split(" ");
this.first = parts[0];
this.last = parts[1];
}
}
} );
const person = new Person( {fullName: "Justin Meyer"} );
console.log( person.first ); //-> "Justin"
console.log( person.last ); //-> "Meyer"
You must use an object with a set property if you want your set to take the resolve argument.
Use
A property definition can be defined in several ways. The Object form is the most literal
and directly represents a PropDefinition object. The other forms
get converted to a PropDefinition as follows:
DefineMap.extend( {
propertyA: Object, // -> PropertyDefinition
propertyB: String, // -> {type: String}
propertyC: Constructor, // -> {Type: Constructor}
propertyD: [ PropDefs ], // -> {Type: DefineList.extend({"#": PropDefs})>}
get propertyE() { /* ... */ }, // -> {get: propertyE(){ /* ... */ }}
set propertyF( value ) { /* ... */ }, // -> {set: propertyF(value){ /* ... */ }},
method: Function
} );
Within a property definition, the available properties and their signatures look like:
DefineMap.extend({
property: {
get: function(lastSetValue, resolve){...},
set: function(newValue, resolve){...},
type: function(newValue, prop){...}| Array<PropertyDefinition> | PropertyDefinition,
Type: Constructor | Array<PropertyDefinition> | PropertyDefinition,
default: function(){...},
Default: Constructor,
serialize: Boolean | function(){...}
}
})
For example:
import {DefineMap} from "can";
const Address = DefineMap.extend( "Address", {
street: "string",
state: "string"
} );
const Person = DefineMap.extend( "Person", {
// a `DefineList` of `Address`
addresses: [ Address ],
// A `DefineMap` with a `first` and `last` property
name: { type: { first: "string", last: "string" } },
// A `DefineList of a ``DefineMap` with a `make` and `year` property.
cars: { Type: [ { make: "string", year: "number" } ] }
} );
const person = new Person( {
addresses: [ { street: "1134 Pinetree" } ],
name: { first: "Kath", last: "Iann" },
cars: [ { make: "Nissan", year: 2010 } ]
} );
console.log(person.addresses[0].street); //-> "1134 Pinetree"
console.log(person.name.first); //-> "Kath"
console.log(person.cars[0].make); //-> "Nissan"