DoneJS StealJS jQuery++ FuncUnit DocumentJS
3.14.1
5.0.0 4.3.0 2.3.35
  • About
  • Guides
  • API Docs
  • Community
  • Contributing
  • Bitovi
    • Bitovi.com
    • Blog
    • Design
    • Development
    • Training
    • Open Source
    • About
    • Contact Us
  • About
  • Guides
  • API Docs
    • Observables
      • can-compute
      • can-define
      • can-define/list/list
      • can-define/map/map
      • can-define-stream
      • can-define-stream-kefir
      • can-event
      • can-event/async/async
      • can-event/batch/batch
      • can-event/lifecycle/lifecycle
      • can-kefir
      • can-list
      • can-map
      • can-map-backup
      • can-map-define
      • can-observation
      • can-observe
      • can-simple-map
      • can-simple-observable
      • can-stream
      • can-stream-kefir
    • Data Modeling
      • can-connect
      • can-connect-cloneable
      • can-connect-feathers
      • can-connect-ndjson
      • can-connect-signalr
      • can-fixture
      • can-fixture-socket
      • can-ndjson-stream
      • can-set
    • Views
      • can-component
      • can-ejs
      • can-element
      • can-react-component
      • can-stache
      • can-stache/helpers/route
      • can-stache-bindings
      • can-stache-converters
      • can-view-autorender
      • can-view-callbacks
      • can-view-href
      • can-view-import
      • can-view-live
      • can-view-model
      • can-view-nodelist
      • can-view-parser
      • can-view-scope
      • can-view-target
      • react-view-model
      • react-view-model/component
      • steal-stache
    • Routing
      • can-deparam
      • can-param
      • can-route
      • can-route-pushstate
    • JS Utilities
      • can-assign
      • can-define-lazy-value
      • can-globals
      • can-key-tree
      • can-make-map
      • can-parse-uri
      • can-string
      • can-string-to-any
      • can-util
      • can-zone
      • can-zone-storage
    • DOM Utilities
      • can-ajax
      • can-attribute-encoder
      • can-control
      • can-dom-events
      • can-event-dom-enter
      • can-event-dom-radiochange
      • can-jquery
    • Data Validation
      • can-define-validate-validatejs
      • can-validate
      • can-validate-interface
      • can-validate-legacy
      • can-validate-validatejs
    • Typed Data
      • can-cid
      • can-construct
      • can-construct-super
      • can-namespace
      • can-reflect
      • can-reflect-promise
      • can-types
    • Polyfills
      • can-symbol
      • can-vdom
    • Core
    • Infrastructure
      • can-global
      • can-test-helpers
    • Ecosystem
    • Legacy
  • Community
  • Contributing
  • GitHub
  • Twitter
  • Chat
  • Forum
  • News
Bitovi

Core

  • Edit on GitHub

The best, most hardened and generally useful libraries in CanJS.

Use

CanJS’s core libraries are the best, most hardened and generally useful modules.
Each module is part of an independent package, so you should install the ones you use directly:

npm install can-component can-compute can-connect can-define can-route can-route-pushstate can-set can-stache can-stache-bindings --save

Let’s explore each module a bit more.

can-compute

can-computes represent an observable value. A compute can contain its own value and notify listeners of changes like:

var compute = require("can-compute");

var name = compute("Justin");

// read the value
name() //-> "Justin"

name.on("change", function(ev, newVal, oldVal){
    newVal //-> "Matthew"
    oldVal //-> "Justin"
});

name("Matthew");

More commonly, a compute derives its value from other observables. The following info compute derives its value from a person map, hobbies list, and age compute:

var DefineMap = require("can-define/map/map"),
    DefineList = require("can-define/list/list"),
    compute = require("can-compute");

var person = new DefineMap({first: "Justin", last: "Meyer"}),
    hobbies = new DefineList(["js","bball"]),
    age = compute(33);

var info = compute(function(){
    return person.first +" "+ person.last+ " is "+age()+
        "and like "+hobbies.join(", ")+".";
});

info() //-> "Justin Meyer is 33 and likes js, bball."

info.on("change", function(ev, newVal){
    newVal //-> "Justin Meyer is 33 and likes js."
});

hobbies.pop();

can-define

can-define/map/map and can-define/list/list allow you to create observable maps and lists with well-defined properties. You can define a property’s type initial value, enumerability, getter-setters and much more. For example, you can define the behavior of a Todo type and a TodoList type as follows:

var DefineMap = require("can-define/map/map");
var DefineList = require("can-define/list/list");

var Todo = DefineMap.extend({           // A todo has a:
  name: "string",                       // .name that’s a string
  complete: {                           // .complete that’s
    type: "boolean",                    //        a boolean
    value: false                        //        initialized to false
  },                                    
  dueDate: "date",                      // .dueDate that’s a date
  get isPastDue(){                      // .pastDue that returns if the
    return new Date() > this.dueDate;   //        dueDate is before now
  },
  toggleComplete: function(){           // .toggleComplete method that
    this.complete = !this.complete;     //        changes .complete
  }
});

var TodoList = DefineList.extend({      // A list of todos:     
  "#": Todo,                            // has numeric properties
                                        //         as todos

  get completeCount(){                  // has .completeCount that
    return this.filter("complete")      //         returns # of
               .length;                 //         complete todos
  }
});

This allows you to create a Todo, read its properties, and call back its methods like:

var dishes = new Todo({
    name: "do dishes",
    // due yesterday
    dueDate: new Date() - 1000 * 60 * 60 * 24
});
dishes.name      //-> "do dishes"
dishes.isPastDue //-> true
dishes.complete  //-> false
dishes.toggleComplete()  
dishes.complete  //-> true

And it allows you to create a TodoList, access its items and properties like:

var todos = new TodoList( dishes, {name: "mow lawn", dueDate: new Date()});
todos.length         //-> 2
todos[0].complete    //-> true
todos.completeCount //-> 1

These observables provide the foundation for data connection (models), view-models and even routing in your application.

can-set

can-set models a service layer’s behavior as a set.Algebra. Once modeled, other libraries such as can-connect or can-fixture can add a host of functionality like: real-time behavior, performance optimizations, and simulated service layers.

A todosAlgebra set algebra for a GET /api/todos service might look like:

var set = require("can-set");
var todosAlgebra = new set.Algebra(
    // specify the unique identifier property on data
    set.prop.id("_id"),  
    // specify that completed can be true, false or undefined
    set.prop.boolean("complete"),
    // specify the property that controls sorting
    set.prop.sort("orderBy")
)

This assumes that the service:

  • Returns data where the unique property name is _id:
    GET /api/todos
    -> [{_id: 1, name: "mow lawn", complete: true},
        {_id: 2, name: "do dishes", complete: false}, ...]
    
  • Can filter by a complete property:
    GET /api/todos?complete=false
    -> [{_id: 2, name: "do dishes", complete: false}, ...]
    
  • Sorts by an orderBy property:
    GET /api/todos?orderBy=name
    -> [{_id: 2, name: "do dishes", complete: false},
        {_id: 1, name: "mow lawn", complete: true}]
    

In the next section will use todoAlgebra to build a model with can-connect.

can-connect

can-connect connects a data type, typically a DefineMap and its DefineList, to a service layer. This is often done via the can-connect/can/base-map/base-map module which bundles many common behaviors into a single api:

var baseMap = require("can-connect/can/base-map/base-map"),
    DefineMap = require("can-define/map/map"),
    DefineList = require("can-define/list/list"),
    set = require("can-set");

var Todo = DefineMap.extend({
    ...
});
var TodosList = DefineMap.extend({
    "#": Todo,
    ...
});
var todosAlgebra = new set.Algebra({
    ...
});

var connection = baseMap({
    url: "/api/todos",
    Map: Todo,
    List: TodoList,
    algebra: todosAlgebra,
    name: "todo"
});

baseMap extends the Map type, in this case, Todo, with the ability to make requests to the service layer.

  • Get a list of Todos
    Todo.getList({complete: true}).then(function(todos){})
    
  • Get a single Todo
    Todo.get({_id: 6}).then(function(todo){})
    
  • Create a Todo
    var todo = new Todo({name: "do dishes", complete: false})
    todo.save().then(function(todo){})
    
  • Update an already created Todo
    todo.complete = true;
    todo.save().then(function(todo){})
    
  • Delete a Todo
    todo.destroy().then(function(todo){})
    

can-connect is also middleware, so custom connections can be assembled too:

var base = require("can-connect/base/base");
var dataUrl = require("can-connect/data-url/data-url");
var constructor = require("can-connect/constructor/constructor");
var map = require("can-connect/can/map/map");

var options = {
    url: "/api/todos",
    Map: Todo,
    List: TodoList,
    algebra: todosAlgebra,
    name: "todo"
}
var connection = map(constructor(dataUrl(base(options))));

can-stache

can-stache provides live binding mustache and handlebars syntax. While templates should typically be loaded with a module loader like steal-stache, you can create a template programmatically that lists out todos within a promise loaded from Todo.getList like:

var stache = require("can-stache");

// Creates a template
var template = stache(
    "<ul>"+
        "{{#if(todos.isPending)}}<li>Loading…</li>{{/if}}"+
        "{{#if(todos.isResolved)}}"+
            "{{#each(todos.value)}}"+
                "<li class='{{#complete}}complete{{/complete}}'>{{name}}</li>"+
            "{{else}}"+
                "<li>No todos</li>"+
            "{{/each}}"+
        "{{/if}}"+
    "</ul>");

// Calls the template with some data
var frag = template({
    todos: Todo.getList({})
});

// Inserts the result into the page
document.body.appendChild(frag);

can-stache templates use magic tags like {{}} to control what content is rendered. The most common forms of those magic tags are:

  • {{key}} - Insert the value at key in the page. If key is a function or helper, run it and insert the result.
  • {{#key}}...{{/key}} - Render the content between magic tags based on some criteria.

can-stache templates return document fragments that update whenever their source data changes.

can-component

can-component creates custom elements with unit-testable view models. It combines a view model created by can-define/map/map with a template created by can-stache.

var Component = require("can-component");
var DefineMap = require("can-define/map/map");
var stache = require("can-stache");

// Defines the todos-list view model
var TodosListVM = DefineMap.extend({
    // An initial value that is a promise containing the
    // list of all todos.
    todos: {
        value: function(){
            return Todo.getList({});
        }
    },
    // A method that toggles a todo’s complete property
    // and updates the todo on the server.
    toggleComplete: function(todo){
        todo.complete = !todo.complete;
        todo.save();
    }
});

Component.extend({
    tag: "todos-list",
    ViewModel: TodosVM,
    view: stache(
        "<ul>"+
            "{{#if(todos.isPending)}}<li>Loading…</li>{{/if}}"+
            "{{#if(todos.isResolved)}}"+
                "{{#each(todos.value)}}"+
                    "<li on:click='toggleComplete(.)'"+
                         "class='{{#complete}}complete{{/complete}}'>{{name}}</li>"+
                "{{else}}"+
                    "<li>No todos</li>"+
                "{{/each}}"+
            "{{/if}}"+
        "</ul>");
});

can-stache-bindings

can-stache-bindings provides custom attributes for can-stache event and data bindings.

Bindings look like:

  • on:event="key()" for event binding.
  • prop:from="key" for one-way binding to a child.
  • prop:to="key" for one-way binding to a parent.
  • prop:bind="key" for two-way binding.

Event binding examples:

<!-- calls `toggleComplete` when the li is clicked -->
<li on:click="toggleComplete(.)"/>

<!-- calls `resetData` when cancel is dispatched on `my-modal`’s view model -->
<my-modal on:cancel="resetData()"/>

One-way to child examples:

<!-- updates input’s `checked` property with the value of complete -->
<input type="checkbox" checked:from="complete"/>

<!-- updates `todo-lists`’s  `todos` property with the result of `getTodos`-->
<todos-list todos:from="getTodos(complete=true)"/>

One-way to parent examples:

<!-- updates `complete` with input’s `checked` property -->
<input type="checkbox" checked:to="complete"/>

<!-- updates `todosList` with `todo-lists`’s `todos` property -->
<todos-list todos:to="todosList"/>

Two-way examples:

<!-- Updates the input’s `value` with `name` and vice versa -->
<input type="text" value:bind="name"/>

<!-- Updates `date-picker`’s `date` with `dueDate` and vice versa -->
<date-picker date:bind="dueDate"/>

can-route and can-route-pushstate

can-route connects a DefineMap’s properties to values in the url. Create a map type, [canjs/doc/can-route.map connect it to the url], and begin routing like:

var route = require("can-route");
var DefineMap = require("can-define/map/map");

var AppViewModel = DefineMap.extend({
    seal: false
},{
    // Sets the default type to string
    "#": "string",
    todoId: "string",
    todo: {
        get: function(){
            if(this.todoId) {
                return Todo.get({_id: this.todoId})
            }
        }
    }
});

var appViewModel = new AppViewModel();
route.map(appViewModel);

route.ready();

When the url changes, to something like #!&todoId=5, so will the appViewModel’s todoId and todo property:

appViewModel.todoId //-> "5"
appViewModel.todo   //-> Promise<Todo>

Similarly, if appViewModel’s todoId is set like:

appViewModel.todoId = 6;

The hash will be updated:

window.location.hash //-> "#!&todoId=6"

The route function can be used to specify pretty routing rules that translate property changes to a url and a url to property changes. For example,

// a route like:
route("todo/{todoId}");

// and a hash like:
window.location.hash = "#!todo/7";

// produces an appViewModel like:
appViewModel.serialize() //-> {route: "todo/{todoId}", todoId: "7"}

can-route-pushstate adds pushstate support. It mixes in this behavior so you just need to import the module:

var route = require("can-route");
require("can-route-pushstate");

Want to learn more?

If you haven’t already, check out the Guides page on how to learn CanJS. Specifically, you’ll want to check out the Chat Guide and TodoMVC Guide to learn the basics of using CanJS’s core libraries. After that, check out the Reading the Docs (API Guide) on how to use and learn from these API docs.

CanJS is part of DoneJS. Created and maintained by the core DoneJS team and Bitovi. Currently 3.14.1.

On this page

Get help

  • Chat with us
  • File an issue
  • Ask questions
  • Read latest news