can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
652 lines (492 loc) • 19.7 kB
HTML
<!--####################################################################
THIS IS A GENERATED FILE -- ANY CHANGES MADE WILL BE OVERWRITTEN
INSTEAD CHANGE:
source: [object Object]
@page guides/pmo/TheDefinePlugin
######################################################################## -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS - The Define Plugin</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="../../static/bundles/bit-docs-site/static.css">
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" href="/docs/images/canjs_favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="../../../docs/images/canjs_favicon_57x57.png">
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="../../../docs/images/canjs_favicon_57x57.png">
<link rel="apple-touch-icon" sizes="72x72" href="../../../docs/images/canjs_favicon_72x72.png">
<link rel="apple-touch-icon" sizes="114x114" href="../../../docs/images/canjs_favicon_114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="../../../docs/images/canjs_favicon_128x128.png">
<link rel="apple-touch-icon" sizes="144x144" href="../../../docs/images/canjs_favicon_144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="../../../docs/images/canjs_favicon_152x152.png">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta name="apple-mobile-web-app-status-bar-style" content="white-translucent">
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-2302003-11', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger"/>
<label for="nav-trigger">Menu</label>
<div id="everything">
<div id="left" class="column">
<div class="top-left">
<div class="brand">
<div class="logo">
<a href="../../../index.html" alt="CanJS"></a>
<div class="dropdown project-dropdown">
<a href="https://donejs.com/">DoneJS</a>
<a href="http://stealjs.com/">StealJS</a>
<a href="http://jquerypp.com/">jQuery ++</a>
<a href="https://funcunit.com/">FuncUnit</a>
<a href="http://documentjs.com/">DocumentJS</a>
</div>
</div>
<div class="version">
<div class="version-number">
3.0.0
</div>
<div class="dropdown version-dropdown">
<a href="https://v2.canjs.com">2.3.27</a>
</div>
</div>
</div>
<div class="search-bar">
<p>
</p>
</div>
</div>
<div class="bottom-left">
<div class="social-side-container">
<ul class="social-side">
<li>
<a class="header-mobile github" href="https://github.com/canjs/canjs" target="_blank"><img class="social-icon-small" src="../../../docs/images/github.png">Github</a>
</li>
<li>
<a class="header-mobile twitter" href="https://twitter.com/canjs" target="_blank"><img class="social-icon-small" src="../../../docs/images/twitter.png">Twitter</a>
</li>
</ul>
<ul class="social-side">
<li>
<a class="header-mobile" href="https://gitter.im/canjs/canjs" target="_blank">Chat</a>
</li>
<li>
<a class="header-mobile" href="http://forums.donejs.com/c/canjs" target="_blank">Forum</a>
</li>
</ul>
</div>
<ul>
<li class="
">
<a class="page"
href="Components.html"
title="">
Components
</a>
</li>
<li class="
">
<a class="page"
href="ApplicationDesign.html"
title="">
Application Design
</a>
</li>
<li class="
">
<a class="page"
href="../../pmo/Setup.html"
title="">
Setup
</a>
</li>
<li class="
">
<a class="page"
href="Constructors.html"
title="">
Constructors
</a>
</li>
<li class="current
parent
expanded">
<a class="page"
href="TheDefinePlugin.html"
title="">
The Define Plugin
</a>
</li>
<li class="
">
<a class="page"
href="StacheTemplates.html"
title="">
Stache Templates
</a>
</li>
<li class="
">
<a class="page"
href="AppStateAndRouting.html"
title="">
App State and Routing
</a>
</li>
<li class="
">
<a class="page"
href="Observables.html"
title="">
Observables
</a>
</li>
<li class="
">
<a class="page"
href="ViewModels.html"
title="">
View Models
</a>
</li>
<li class="
">
<a class="page"
href="DataModelsAndFixtures.html"
title="">
Data Models and Fixtures
</a>
</li>
<li class="
">
<a class="page"
href="LoadingStates.html"
title="">
Loading States
</a>
</li>
<li class="
">
<a class="page"
href="EventHandling.html"
title="">
Event Handling
</a>
</li>
<li class="
">
<a class="page"
href="WebServiceCommunication.html"
title="">
Web Service Communication
</a>
</li>
<li class="
">
<a class="page"
href="Recap.html"
title="">
Recap
</a>
</li>
</ul>
</div>
</div>
<div id="right" class="column">
<div class="top-right">
<div class="top-right-top">
<ul class="top-right-bitovi">
<li class="dropdown">
<a href="http://bitovi.com" class="bitovi icon-bits">Bitovi</a>
<ul class="dropdown-menu">
<li><a href="http://bitovi.com">Bitovi.com</a></li>
<li><a href="http://bitovi.com/blog/">Blog</a></li>
<li><a href="http://bitovi.com/consulting/">Consulting</a></li>
<li><a href="http://bitovi.com/training/">Training</a></li>
<li><a href="http://bitovi.com/open-source/">Open Source</a></li>
</ul>
</li>
</ul>
<div class="brand">
<div class="logo">
<a href="../../../index.html" alt="CanJS"></a>
</div>
</div>
<ul class="top-right-links">
<li>
<a href="https://gitter.im/canjs/canjs">Chat</a>
</li>
<li>
<a href="http://forums.donejs.com/c/canjs">Forum</a>
</li>
<li>
<a class="github-button nav-social" href="https://github.com/canjs/canjs" data-count-href="/canjs/canjs/stargazers" data-count-api="/repos/canjs/canjs#stargazers_count">Star</a>
</li>
<li>
<a href="https://twitter.com/canjs" class="twitter-follow-button nav-social" data-show-count="true" data-show-screen-name="false">Follow @canjs</a><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
</li>
</ul>
</div>
<div class="breadcrumb">
<li><a href="../pmo.html">Place My Order Guide</a></li> /
<li><a href="TheDefinePlugin.html">The Define Plugin</a></li>
<li class="breadcrumb-dropdown">/ <a> On this page</a>
<ul class="on-this-page"></ul>
</li>
<div class="nav-toggle" title="Back to top"></div>
</div>
</div>
<div class="bottom-right">
<article>
<section class="title">
<div class="page-type">
<h1>The Define Plugin</h1>
<div>page</div>
</div>
<section class="description">
</section>
</section>
<section class="on-this-page-table">
</section>
<section class="title-footer">
<ul class="title-links">
<!-- <li><a href="#">docco</a></li> -->
<li><a href="//github.com/canjs/canjs/tree/v3.0.0/docs/can-guides/experiment/pmo/the-define-plugin.md">source</a></li>
<!-- <li><a href="#">download</a></li> -->
<!-- <li><a href="#">tests</a></li> -->
</ul>
</section>
<section class="body">
<div class="getting-started">
<hr />
<p><strong>In this Chapter</strong></p>
<ul>
<li>The Define Plugin</li>
</ul>
<p><em>There is no code to download for this chapter</em></p>
<hr />
<p>The <code>define</code> plugin allows you to finely control the the behavior of the
attributes on a <code>can.Map</code>. For any property you declare in the <code>define</code> plugin,
you can control its:</p>
<ul>
<li><a href="#set">set</a></li>
<li><a href="#get">get</a></li>
<li><a href="#type">type</a></li>
<li><a href="#value">value</a></li>
<li><a href="#remove">remove</a></li>
<li><a href="#serialization">serialization</a></li>
</ul>
<p>Before we get into the properties of the define plugin, however, let’s look at how to set it up.
Creating a define is as simple as adding a define property to the instance properties
of the <code>can.Map</code>. This property is an object literal. Remember from our conversation on
<a href="Constructors.html"><code>can.Construct</code></a> that passing in one argument to a <code>can.Construct</code> will set
its instance properties. This is important to know should you create a <code>can.Map</code> that has both
instance and static properties, and you want to use the define plugin. Below are two examples:</p>
<pre><code>//can.Map with one argument
var Person = can.Map.extend({
define: {
//define properties go here
myProperty: {
//property attributes
}
}
});
//can.Map with two arguments
var Person = can.Map.extend(
{
//static properties go here
},
{
define: {
//define properties go here
myProperty: {
//property attributes
}
}
});
</code></pre>
<p><a name="set"></a></p>
<h3>set</h3>
<p>A <a href="../docs/can.Map.prototype.define.set.html">set</a> function defines what happens when a value is set on a <code>can.Map</code>.
It is typically used to update other attributes on the <code>can.Map</code> as a side
effect, or coerce the set value into specific format.</p>
<p>The setter function can take two optional arguments:</p>
<ul>
<li><code>newVal</code>: The type function coerced value the user intends to set on the <code>can.Map</code></li>
<li><code>setVal</code>: A callback that can set the value of the property asynchronously.</li>
</ul>
<p>When using a setter function, the final value of the attribute is determined
by the value the setter function returns. If the function returns a value,
that value is used as the value of the attribute. If <code>undefined</code> is
returned, the behavior depends on the number of arguments the setter
<em>declares</em>, as below:</p>
<pre><code>// If the setter does not specify the newValue argument,
// the attribute value is set to whatever was passed to attr.
set: function() { ... }
// If the setter specifies the newValue argument only,
// the attribute value will be removed
set: function(newValue) { ... }
// If the setter specifies both newValue and setValue, the value of
// the property will not be updated until setValue is called
set: function(newValue, setValue) { ... }
</code></pre>
<p><a name="get"></a></p>
<h3>get</h3>
<p>A <a href="../docs/can.Map.prototype.define.get.html">get</a> function defines what happens when a value is read on a <code>can.Map</code>.
It is typically used to provide properties that derive their value from other
properties of the map, as below:</p>
<pre><code>var Person = can.Map.extend({
define: {
fullName: {
get: function () {
return this.attr("first") + " " + this.attr("last");
}
}
}
});
</code></pre>
<p><code>get</code> is passed two optional arguments: <code>lastSetValue</code> and <code>resolve</code>.</p>
<p><code>lastSetValue</code> is the last value the property was set to. This, among other uses,
can be used to update a list in place instead of replacing it. The following
keeps <code>taskIds</code> updated with all of <code>tasks</code>' ids:</p>
<pre><code>var Person = can.Map.extend({
define: {
taskIds: {
Value: can.List,
get: function(initialValue){
var ids = this.attr('tasks').map(function(task){
return task.attr("id");
});
return initialValue.replace(ids);
}
}
}
});
</code></pre>
<p><code>resolve</code> can asynchronously set the retrived value of a
"bound" property. The following makes <code>person</code> update when <code>personId</code> changes.
<code>person</code> will be a <code>Person</code> instance retrieved from the server.</p>
<pre><code>var AppViewModel = can.Map.extend({
define: {
person: {
get: function(lastSetValue, resolve){
Person.findOne({id: this.attr("personId")})
.then(resolve);
}
}
}
});
</code></pre>
<p>Asynchronous getters must be bound to to behave correctly. In most apps, this happens
automatically because observables are "bound" by a template. However,
when testing, you'll need to remember to <a href="../docs/can.Map.prototype.bind.html">bind</a>
on that property before reading it. Here's how one might test the previous <code>AppViewModel</code>:</p>
<pre><code>var appVM = new AppViewModel({
personId: 5
});
appVM.bind("person", function(ev, newVal, oldVal){
// `person` will change from undefined to a
// Person instance.
ok(newVal instanceof Person)
});
// appVM is `undefined` here because
// `Person.findOne` hasn't returned yet
appVM.attr("person") //-> undefined
</code></pre>
<p><a name="type"></a></p>
<h3>type</h3>
<p>The <a href="/docs/can.Map.prototype.define.type.html">type</a> property converts a value passed to an <code>attr</code> setter function
into a specific value type. The type can be specified as either a type
function, or one of the following strings:</p>
<ul>
<li><code>string</code> - Converts the value to a string.</li>
<li><code>date</code> - Converts the value to a date or <code>null</code> if the date can not be converted.</li>
<li><code>number</code> - Passes the value through <code>parseFloat</code>.</li>
<li><code>boolean</code> - Converts falsey values (such as <code>""</code> or <code>0</code>) to <code>false</code> and everything else to <code>true</code>.</li>
<li><code>*</code> - Prevents the default coercion of Objects to can.Maps and Arrays to can.Lists.</li>
</ul>
<p>There are two ways to define the <code>type</code> property:</p>
<ul>
<li><code>Type</code></li>
<li><code>type</code></li>
</ul>
<p><code>Type</code>, uppercase, makes sure that the set value is an
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof">instanceof</a> the provided
constructor function. If not, the constructor function will be called with <code>new</code> and passed the set value as the first
argument.</p>
<p>In contrast, <code>type</code>, lowercase, is called no matter what and expected to return the value that should be set on the map.</p>
<pre><code>define: {
items: {
type: function(newValue){
return typeof newValue === "string" ? newValue.split(",") : newValue;
}
}
}
</code></pre>
<p><a name="value"></a></p>
<h3>value</h3>
<p>Sets the default value for instances of the <code>can.Map</code>. If the default
value should be an object of some type, it should be specified as the return
value of a function, so that all instances of the map don't point to the same
object. This is because JavaScript passes primitives by value, and all other
values (objects, arrays, etc.) by reference.</p>
<pre><code>define: {
prop: {
value: function(){ return []; }
}
}
</code></pre>
<p>As with <code>type</code>, above, there are two ways to define the <code>value</code> property: <code>Value</code>,
or <code>value</code>. <a href="../docs/can.Map.prototype.define.ValueConstructor.html">Value</a>, uppercase, provides a constructor function, ensuring that
a new instance of <code>Value</code> is made for each map instance. If <a href="../docs/can.Map.prototype.define.value.html">value</a> is not an function,
that value will be the default value of the attribute. If <code>value</code> is a function,
that function's return value will be used as the default value of the attribute.</p>
<p><a name="remove"></a></p>
<h3>remove</h3>
<p>The <a href="../docs/can.Map.prototype.define.remove.html">remove</a> property is called when an attribute is removed. This is often used to remove other related properties.</p>
<p><a name="serialization"></a></p>
<h3>serialization</h3>
<p>The last property we’ll talk about is <a href="https://en.wikipedia.org/wiki/Serialization">serialization</a>. The
<a href="../docs/can.Map.prototype.define.serialize.html">serialize</a> property defines how the attribute will behave when the map is
serialized. Managing this property can be useful when serializing complex types like dates,
arrays, or objects into strings. You can also control whether or not a
given property can be serialized. Returning <code>undefined</code> from a serialization
function for any property means this property will not be part of the
serialized object. Managing serialization is an important consideration in <a href="AppStateAndRouting.html">routing</a>.
We’ll see how this works when we discuss routing in a later chapter.</p>
<pre><code>define: {
locationIds: {
serialize: false
}
}
</code></pre>
<hr />
<p><span class="pull-left"><a href="Observables.html">‹ Observables</a></span>
<span class="pull-right"><a href="StacheTemplates.html">Stache Templates ›</a></span></p>
</div>
</section>
<script type="text/javascript">
var docObject = {"src":{"path":"docs/can-guides/experiment/pmo/the-define-plugin.md"},"description":"\n","name":"guides/pmo/TheDefinePlugin","title":"The Define Plugin","type":"page","parent":"guides/pmo","order":4,"disabletableofcontents":true,"comment":" ","pathToRoot":"../../.."};
</script>
</article>
<footer><p>CanJS is part of <a href="http://donejs.com" target="_blank">DoneJS</a>. Created and maintained by the core <a href="https://donejs.com/About.html#section=section_Team" target="_blank">DoneJS team</a> and <a href="http://bitovi.com" target="_blank">Bitovi</a>. <strong>Currently 3.0.0.</strong></p></footer>
</div>
</div>
</div>
<script>
steal = {
instantiated: {
"bundles/bit-docs-site/static.css!$css" : null
}
};
</script>
<script type='text/javascript' data-main="bit-docs-site/static" src="../../static/node_modules/steal/steal.production.js"></script>
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>