Apis
DoneJS is comprised of many projects that are documented separately. This page contains overviews of each project and links to their official APIs.
Application Infrastructure
The blue boxes in the following architecture diagram represent modules provided by DoneJS.
- StealJS - Module loader and build system. api.
- CanJS - Views, ViewModels, modeling part of Models, custom elements, routing. api
- can-connect - Data connection part of Models, real-time, fall-through cache. api
- can-set - Create set algebras used to compare AJAX parameters. api
- jQuery - DOM utilities. api
- jQuery++ - Even more DOM utilities. api
- done-ssr - Server-side rendering for NodeJS. api
- done-autorender - Processes templates so they can be server-side rendered. api
- can-simple-dom - A lightweight virtual DOM. api
Tooling
DoneJS provides many aspects of JavaScript application tooling, shown in the diagram below.
- donejs-cli - The commands available to the donejs command line interface. api
- generator-donejs - Default generators are bundled with DoneJS. api
- QUnit - Default test assertion library. api
- FuncUnit - Functional test utilities. api
- Testee - Browser launcher and test reporter. api
- DocumentJS - Documentation engine. api
Application flow overview
Lets talk about how the typical behavior of a DoneJS application works. We'll use the chat application as an example in development. We'll cover what happens when:
https://chat.donejs.com/
https://chat.donejs.com/
to anotherhttps://chat.donejs.com/chat
.First page load
An http request for
https://chat.donejs.com/
is sent to a node server. The node server is configured, in this case with express, to use done-ssr-middleware to render a DoneJS application:done-ssr uses steal to load the application's main module which results in loading the entire application. Loading the application only happens once for all page requests.
A DoneJS's main module is specified where all configuration of a DoneJS application happens, its
package.json
. The main module is usually a can-stache template processed with the done-autorender plugin. The module name is specified like:index.stache!done-autorender
.index.stache
might look like:The done-autorender plugin, in NodeJS, exports this template so it can be rendered. It also exports any modules it imports with
<can-import>
that are labeled withexport-as="EXPORT_NAME"
. Exporting the viewModel is important for done-ssrOnce done-ssr has the done-autorender's
template
andviewModel
export it:viewModel
into thevirtual DOM
instance.done-autorender templates waits for all promises to complete before providing a final result. Once the template is finished rendering, done-ssr converts it to a string and sends it back to the browser.
The browser downloads the page's HTML, which includes a
<script>
tag that points to steal.In development, this loads
steal.js
which then loadsindex.stache
and processes it with thedone-autorender
.In the browser,
done-autorender
:viewModel
into a document fragment.Pushstate change
A pushstate is triggered by user action, usually by clicking a link. can-route's routing rules determines the properties set on the application viewModel.
done-autorender previously bound the AppViewModel to can-route which causes any change in the route to be reflected in the AppMap instance.
Live binding causes the initial template to reflect in the change in route. If the new route is
/chat
it will cause thepage
to be chat:can-import will progressively load the component for the new page with a Promise as its view model. When the promise resolves the can-component will be inserted.
CLI and Generators
After installing DoneJS globally with
npm install donejs -g
you will have thedonejs
command available on the command line. It lets you initialize a new application and - when navigating within a DoneJS project - run scripts provided locally by your application. Within your application folder thedonejs
command is a convenience wrapper for the functionality described below and you can also get a list of all commands by runningnpm scripts
npm scripts are defined in the
scripts
section of your applicationspackage.json
. There are some standard scripts that every Node application uses (likenpm start
ornpm test
- both of which are already set up for you) and you can add your own which is what DoneJS does with commands likenpm run develop
ornpm run build
. Thedonejs
command makes running those commands easier by allowing you to run them likedonejs start
,donejs develop
ordonejs build
Generators
donejs add
lets you run the Yeoman generators provided by generator-donejs. Currently the following generators are available:donejs add app [folder]
which will initialize a new application (optionally within the given folder)donejs add component <modulename> <tagname>
to create a new can-componentdonejs add supermodel <modulename>
to generate a new modeldonejs add plugin [folder]
which will initialize a new plugin projectdonejs add generator [folder]
which will initialize a new generator projectThird-party generators
If
donejs add
can’t find a built-in generator, e.g. when runningdonejs add mygenerator
, DoneJS will try to install thedonejs-mygenerator
package from npm and run the Yeoman generators it provides. This is how we can enable a desktop application build of the application by simply running:Which will install the donejs-electron package and then run its generator, which initializes everything you need. This also works for adding a mobile application build using donejs-cordova like this:
This way you can use DoneJS’s growing list of plugins and generators without having to add anything to your application that you don't use.
StealJS
The base of any good JavaScript application is its dependency management system.
DoneJS uses StealJS which itself is split into two sub-projects:
steal
- loads CommonJS, ES6, and AMD modules. It can also load styles, templates and more.steal-tools
- builds your application's modules for production and also provides hot-module-swapping.steal
To use steal, simply add a script tag to
steal.js
in an HTML page or in a done-autorendertemplate
and point themain
attribute to a module to load like:Using the default DoneJS system.directories.lib configuration, this will load
my-app/src/my-module.js
. From there, use CommonJS, ES6, or AMD to load your modules:If an
import
,require
ordefine
module reference ends with"/"
, is a shorthand for importing a module in the modlet format. The moduleName imported is the same as the module reference, but with the last folder name added again.Some examples:
Configure steal's behavior in your
package.json
in thesteal
object like:steal-tools
In DoneJS applications, steal-tools is primarily used to:
It can also be used to export your modules to different formats.
DoneJS comes with a
build.js
script that call's steal-tools' build:This is already configured to run with:
But you could also run it with:
Hot module swapping is done with live-reload which is bundled within steal-tools.
By default
donejs develop
starts the live-reload server. However, you could start one yourself with:CanJS
CanJS provides:
Observables act as the
ViewModel
and part of theModel
.One-way and two-way binding templates act as the
View
.can-component is used to combine
View
andViewModel
into easy to instantiate and assemble custom elements.Checkout the following quick examples of their use:
observables:
one and two-way binding templates:
custom elements:
can-construct
can-construct allows you to define constructor functions that are easy to inherit from. It's used by can-define and can-component.
To create your own constructor function, extend
can-construct
with prototype methods like:Then you can create instances of
Todo
like:You can extend
Todo
with extend too:can-define/map/map
can-define is used to create observable JavaScript Object-like objects. Create an instance of the base can-define/map/map like:
Read or write a
map
's properties:Bind to changes in a person's properties with .on:
Extend a
DefineMap
to create a new constructor function. This is very useful for creating Models and View Models:The can-define allows you to control the behavior of attributes. You can define default values, getters, setters, and type converters.
You can even describe asynchronous behavior which is critical for working with service data:
can-define/list/list
can-define/list/list is used to create observable JavaScript Array-like objects. Create an instance of the base
DefineList
like:Read and write items from the list or to read the length:
Use array methods like .push, .pop, and .splice to modify the array:
Use .forEach, .map, or .filter to loop through the array. All these methods return a
DefineList
Listen to when a list changes by binding on
add
orremove
orlength
events.By default, if you initialize a list with plain JavaScript objects, those objects are converted to a
DefineMap
:You can create your own custom
DefineList
constructor functions by extendingDefineList
:When extending
DefineList
you can specify the defaultMap
type that's created when plain JS objects are added to the list:can-compute
can-compute isn't used directly much anymore. However, it's used heavily in can-define getters and live binding so it's worth understanding the basics.
can-compute
allows you to define single observable values like:or derived values like:
Read a compute by calling it like a function:
Listen to a compute by binding on its
change
event:Internally,
on
runs the compute function, identifying what observable values it reads, and listening to them. It caches the return result so that reading the compute again likeinfo()
just returns the cached result.When any of the read observables change, it updates the cached value, and calls back any event handlers:
can-stache
can-stache is a Handlebars and Mustache compliant live-binding templating language.
Create a template programmatically with
can-stache
like:template
is a renderer function that, when called with observable data, returns a DocumentFragment that is updated when the observable data changes.Add those fragments to the page to see the result:
In a DoneJS application, templates are used primarily as part of a can-component or as the done-autorendered main template.
When used in a can-component, the templates are often put in their own file. For example, a
person_edit.js
component file might have aperson_edit.stache
file like:This template's renderer function is imported in
person_edit.js
like:can-stache
template behavior is controlled by what's within magic tags like{{ }}
. There are different tag types, lots of helper functions, and different ways to call methods and functions.There's too much to cover so we will highlight the important APIs.
The different tag types:
{{key}} - inserts an escaped value.
{{{key}}} - inserts an unescaped value.
{{#key}} ... {{/key}} - renders a subsection depending on the value of the key.
The subsection is rendered with the
key
value as the top of the scope:{{^key}} ... {{/key}} - opposite of
{{#key}}
.The following are stache's most commonly used helpers:
{{#if expr}} .. {{/if}} - renders the subsection if the expr is truthy.
{{#is expr1 expr2}} ... {{/is}} - compares two expressions and renders a subsection depending on the result.
{{#each key}} ... {{/each}} - renders a subsection for each item in a key's value.
If the value of a key is a DefineList only the minimum amount of DOM updates occur when the list changes.
{{routeUrl hashes}} - generates a url using can-route for the provided hashes.
Call methods in your scope like:
{{method(value)}}
can-stache-bindings
can-stache-bindings
allows you to bind to viewModel or DOM events and create one-way or two-way bindings on element's properties/attributes, can-component viewModels andcan-stache
's scope.Create a one-way binding from the parent scope to a child's properties/attributes or viewModel:
name
in the scope touserName
property on the viewModel or thevalue
attribute on theinput
element.Create a one-way binding from the child's properties/attributes or viewModel to the parent scope:
childProp:to="name" - One-way bind the value of
userName
property on the viewModel or thevalue
attribute on theinput
element to thename
property in the parent scope.Create two-way bindings between the parent scope and the child's viewModel or property/attributes:
propName:bind="value" - Two-way bind the value of
userName
property in the viewModel orvalue
attribute on theinput
element to thename
in the parent scope.Create bindings to viewModel or DOM events:
EVENT
and usehandler
as the event handler.Listen to the
click
event emitted by a DOM element:Listen to the
show
event emitted by the viewModel,vm
:can-component
can-component lets you create widgets with well-defined View Models and are instantiated with custom elements.
Define a
can-component
by extending one with atag
name, can-defineviewModel
and can-stache template like:To instantiate this component so it says
Hello World!
, add a<hello-world>
element to the page like:Use can-stache-bindings to send a value from the
can-stache
scope like:can-component
s are usually built as modlets, meaning their template and styles are another file and imported:Some components are so small, they they don't require three separate files. For these, you can use a
.component
file:can-route
can-route provides powerful 2-way, nested, routing to your application, supporting both hash and pushstate.
Configure routing rules to define property values on your application's View Model when a url is matched.
The following sets the application ViewModel's
page
property to"chat"
when the url looks like/chat
:You can define defaults that get set when
{page}
is empty. The following sets the defaultpage
property to"home"
.You can specify multiple properties to set for a given url:
Update the url by changing
can-route
:Or change
route
by modifying the url:In a DoneJS application can.route is bound to the application View Model, but you can connect
can-route
to other maps:Which will cause any changes in the route to reflect in the View Model instance, and any changes in the View Model instance to reflect in the route.
Data Layer APIs
can-connect
can-connect is used to connect typed data to backend services. In a DoneJS application, that typed data is a can-define/map/map and can-define/list/list type.
To make a simple connection to a restful interface:
This adds a getList, .get, .save and .destroy methods to
Todo
allowing you to CRUDTodo
s andTodoList
s from the service layer like:can-connect
comes with a wide variety of behaviors that can be mixed into a connection. Examples include:can.List
s updated with changes.To make the process of creating
can.Map
based connections easier, DoneJS comes with a supermodel generator creates a super-map.A super-map is just a connection with a bunch of the mostly commonly used behaviors. Create one with the
superMap
function like:can-set
can-set is used to compare set objects that are represented by the parameters commonly passed to service requests.
For example, if you want all todos for user
5
that are complete, you might call:{userId: 5, complete: true}
represents a set. Usingcan-set
we can compare it to other sets. The following returnstrue
because{userId: 5, complete: true}
represents a subset of{userId: 5}
.can-set
can perform more complex logic with custom set Algebras.The following creates a set-algebra that is able to combine ranges:
In a DoneJS application, you create custom algebras to pass to can-connect connections. The connection's behaviors use that algebra to their optimizations.
For example, if the
Todo
type in the can-connect section has the following property behaviors:complete
can be true or falsetype
can be one of "dev", "design", or "QA"... and the service layer supports queries like:
You'd want to create an algebra for the
superMap
as follows:This allows a
superMap
to combine requests like:And know that if
Todo.getList({type: ["dev","design"]})
has already been retrieved, there's no need to make a request forTodo.getList({type: ["dev"]})
.Testing APIs
QUnit
QUnit is DoneJS's default JavaScript unit testing framework. It is provided for DoneJS by the steal-qunit project. A basic unit test for a can.Component view-model looks like this:
While the generators create QUnit tests by default you can switch your own tests easily to Jasmine or Mocha. To use Mocha instead for the previous view-model example we just need to install the wrapper with
And then change the test file to:
FuncUnit
FuncUnit enhances QUnit, Mocha or Jasmine and enables them to simulate user actions, easily test asynchronous behavior, and support black box testing. It uses a jQuery-like syntax to write functional or unit tests. When generating an application, DoneJS already includes a basic FuncUnit smoke-test which runs alongside the other tests. It looks like this:
This will open the main application (
development.html
is the HTML file that loads our DoneJS app without server-side-rendering) and ensures that the<title>
is set to the name (which is the default in a newly generated application). To learn more about the user interactions and assertions available, follow up in the FuncUnit API documentation.Testee
Testee is a JavaScript test runner that can run your QUnit, Mocha and Jasmine tests from the command line. The command executed when running
donejs test
(which is the same as runningnpm test
) is located in thepackage.json
scripts
section and already set up to run the main test suite in Firefox like this:To change the browsers that our tests run on we can update the list of browsers, for example to add Safari and Google Chrome Canary by changing the test script to:
Testee supports all Mocha command line reporters. For example, running the tests in the default browser PhantomJS (DoneJS only works with PhantomJS >= 2.0) on a Jenkins CI server that uses XUnit output from a
testresults.xml
can be accomplished like this:For more configuration options follow up in the Testee documentation.
DocumentJS
When working on large applications keeping updated documentation is critical. DocumentJS generates API documentation for your application supporting JSDoc syntax that can be multi-versioned.
Configuration
DocumentJS is configured with a docConfig specified in a documentjs.json file within your project:
This specifies to look in JavaScript and Markdown files for jsdoc tags. When ran the documentation will be written to the docs folder.
Documenting
DocumentJS includes most tags you need to document a web application and includes an API to create your own.
Here's how you would document a can-component View Model:
Generating
DoneJS preconfigures your app to be documented with:
Or you can run the documentjs command directly with:
DOM APIs
jQuery
jQuery is the ubiquitous DOM manipulation library. While you don't often need to write jQuery directly, CanJS is built making it safe to use jQuery when needed.
For example, you can make your own custom elements that call jQuery plugins:
can.-stache-bindings lets you listen to jQuery special events like:
can-component's events object also supports this:
CanJS adds special inserted, removed, and attributes events. This allows you to teardown any behavior when the DOM is modified:
CanJS's live-binding also hooks into these same events. So if you remove an element with jQuery, CanJS will also teardown its bindings. This means that if you were to call:
jQuery++
jQuery++ adds a bunch of special events and other DOM utilties to jQuery.
jQuery.animate
to use CSS3 animations if possible.Server Side Rendering APIs
done-ssr
done-ssr enables DoneJS applications to be server-side rendered. Paired with done-autorender it allows you to render the entire document from a single template.
The render function is called with a string url to render and returns a response object that contains the html string that was rendered. Use any Node-based http framework with done-ssr.
For convenience we have published an Express middleware:
Additionally DoneJS has done-serve which acts as a rendering front-end for your application. It will host static content, render your application, and proxy requests to another back-end server.
done-autorender
done-autorender is a Steal plugin that enables using a can.stache template as your application's entry point. Create a template like:
done-autorender will insert this template on page load. The
can-import
tag with theexport-as="viewModel"
attribute is a can-define/map/map that acts as the View Model for the application.If you have live-reload enabled done-autorender will additionally use those APIs to re-render the application when any modules are reloaded.
done-autorender handles requests when running in Node for server-side rendering and will wait for all asynchronous events to complete.
can-simple-dom
can-simple-dom is a minimal virtual DOM implementation used for server-side and worker thread rendering. It contains enough of the DOM APIs to get basic jQuery usage to work, as well as what is typical of CanJS applications.
If you are working on an advanced plugin you might use can-simple-dom directly, in which case you would import it:
From here document has the normal DOM apis such as
document.createElement
.