DoneJS StealJS jQuery++ FuncUnit DocumentJS
6.6.1
5.33.3 4.3.0 3.14.1 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-bind
      • can-compute
      • can-debug
      • can-deep-observable
      • can-define
      • can-define/list/list
      • can-define/map/map
      • can-define-backup
      • can-define-stream
      • can-define-stream-kefir
      • can-event-queue
      • can-kefir
      • can-list
      • can-map
      • can-map-compat
      • can-map-define
      • can-observable-array
      • can-observable-object
      • can-observation
      • can-observation-recorder
      • can-observe
      • can-simple-map
      • can-simple-observable
      • can-stream
      • can-stream-kefir
      • can-value
    • Views
      • can-attribute-observable
      • can-component
      • can-observable-bindings
      • can-stache
      • can-stache-bindings
      • can-stache-converters
      • can-stache-element
      • can-stache-route-helpers
      • can-view-autorender
      • can-view-callbacks
      • can-view-import
      • can-view-live
      • can-view-model
      • can-view-parser
      • can-view-scope
      • can-view-target
      • steal-stache
    • Data Modeling
      • can-connect
      • can-connect-ndjson
      • can-connect-tag
      • can-define-realtime-rest-model
      • can-define-rest-model
      • can-fixture
      • can-fixture-socket
      • can-local-store
      • can-memory-store
      • can-ndjson-stream
      • can-query-logic
      • can-realtime-rest-model
      • can-rest-model
      • can-set-legacy
      • can-super-model
    • Routing
      • can-deparam
      • can-param
      • can-route
      • can-route-hash
      • can-route-mock
      • can-route-pushstate
    • JS Utilities
      • can-assign
      • can-define-lazy-value
      • can-diff
      • can-globals
      • can-join-uris
      • can-key
      • can-key-tree
      • can-make-map
      • can-parse-uri
      • can-queues
      • can-string
      • can-string-to-any
    • DOM Utilities
      • can-ajax
      • can-attribute-encoder
      • can-child-nodes
      • can-control
      • can-dom-data
      • can-dom-events
      • can-dom-mutate
      • can-event-dom-enter
      • can-event-dom-radiochange
      • can-fragment
    • Data Validation
      • can-type
      • can-validate
      • can-validate-interface
      • can-validate-legacy
      • can-validate-validatejs
    • Typed Data
      • can-cid
      • can-construct
      • can-construct-super
      • can-data-types
      • can-namespace
      • can-reflect
      • can-reflect-dependencies
      • 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-value can-stache-element can-realtime-rest-model can-observable-object can-observable-array can-route can-route-pushstate --save

Let’s explore each module a bit more.

can-value

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

import { value } from "can";

let name = value.with("Justin");

// read the value
console.log(name.value); //-> "Justin"

name.on((newVal, oldVal) => {
    console.log(newVal); //-> "Matthew"
    console.log(oldVal); //-> "Justin"
});

name.value = "Matthew";

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

import { ObservableObject, ObservableArray, value } from "can";

let person = new ObservableObject({ first: "Justin", last: "Meyer" }),
    hobbies = new ObservableArray(["js", "bball"]),
    age = value.with(33);

let info = value.returnedBy(function(){
    return person.first +" "+ person.last + " is " + age.value +
        "and likes " + hobbies.join(", ") + ".";
});

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

info.on((newVal) => {
    console.log(newVal); //-> "Justin Meyer is 33 and likes js."
});

hobbies.pop();

can-observable-object and can-observable-array

can-observable-object and can-observable-array allow you to create observable objects and arrays 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 TodoArray type as follows:

import { ObservableObject, ObservableArray, type } from "can";

class Todo extends ObservableObject {
  static props = {
      // A todo has a:
      // .name that’s a string
      name: String,

      complete: {                           // .complete that’s
          type: Boolean,                    //        a boolean
          default: false                    //        initialized to false
      },

      // .dueDate that’s a date
      dueDate: Date,

      get isPastDue(){                      // .pastDue that returns if the
          return new Date() > this.dueDate; //        dueDate is before now
      }
  };

  toggleComplete() {                        // .toggleComplete method that
    this.complete = !this.complete;         //        changes .complete
  }
}

class TodoArray extends ObservableArray {
  static props = {
      get completeCount(){                  // has .completeCount
          return this.filter(               //         that returns
            (todo) => todo.complete         //         # of
          )                                 //         complete todos
          .length;
      }
  };

  static items = type.convert(Todo);       // has numeric properties
                                           //         as todos
}

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

import { ObservableObject } from "can";

class Todo extends ObservableObject {
  static props = {
      // A todo has a:
      // .name that’s a string
      name: String,

      complete: {                           // .complete that’s
          type: Boolean,                    //        a boolean
          default: false                    //        initialized to false
      },

      // .dueDate that’s a date
      dueDate: Date,

      get isPastDue(){                      // .pastDue that returns if the
          return new Date() > this.dueDate; //        dueDate is before now
      }
  };

  toggleComplete() {                        // .toggleComplete method that
    this.complete = !this.complete;         //        changes .complete
  }
}
const dishes = new Todo({
    name: "do dishes",
    // due yesterday
    dueDate: new Date(new Date() - 1000 * 60 * 60 * 24)
});
console.log(dishes.name);      //-> "do dishes"
console.log(dishes.isPastDue); //-> true
console.log(dishes.complete);  //-> false
dishes.toggleComplete();
console.log(dishes.complete);  //-> true

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

import { ObservableObject, ObservableArray, type } from "can";

class Todo extends ObservableObject {
  static props = {
      // A todo has a:
      // .name that’s a string
      name: String,

      complete: {                           // .complete that’s
          type: Boolean,                    //        a boolean
          default: false                    //        initialized to false
      },

      // .dueDate that’s a date
      dueDate: Date,

      get isPastDue(){                      // .pastDue that returns if the
          return new Date() > this.dueDate; //        dueDate is before now
      }
  };

  toggleComplete() {                        // .toggleComplete method that
    this.complete = !this.complete;         //        changes .complete
  }
}

class TodoArray extends ObservableArray {
  static props = {
      get completeCount(){                  // has .completeCount
          return this.filter(               //         that returns
            (todo) => todo.complete         //         # of
          )                                 //         complete todos
          .length;
      }
  };

  static items = type.convert(Todo);       // has numeric properties
                                           //         as todos
}
const dishes = new Todo({
    name: "do dishes",
    // due yesterday
    dueDate: new Date(new Date() - 1000 * 60 * 60 * 24)
});
dishes.toggleComplete();
const todos = new TodoArray([dishes, { name: "mow lawn", dueDate: new Date() }]);
console.log(todos.length);         //-> 2
console.log(todos[0].complete);    //-> true
console.log(todos.completeCount);  //-> 1

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

can-set

[can-set] models a service layer’s behavior as a [can-set.Algebra 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:

import set from "can-set";
let 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 an ObservableObject and associated ObservableArray, to a service layer. This is often done via the can-rest-model module which bundles many common behaviors into a single api:

import { ObservableObject, ObservableArray, restModel } from "can";

class Todo extends ObservableObject {
    static props = {
        // ...
    };
}

class TodoArray extends ObservableObject {
    static props = {
        // ...
    };

    static items = Todo;
}

const connection = restModel({
    url: "/api/todos",
    ObjectType: Todo,
    ArrayType: TodoArray
});

baseMap extends the Object 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((todos) => {});
    
  • Get a single Todo
    Todo.get({ _id: 6 }).then((todo) => {});
    
  • Create a Todo
    const todo = new Todo({ name: "do dishes", complete: false });
    todo.save().then((todo) => {});
    
  • Update an already created Todo
    todo.complete = true;
    todo.save().then((todo) => {});
    
  • Delete a Todo
    todo.destroy().then((todo) => {});
    

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:

import { stache, ObservableObject, ObservableArray, restModel } from "can";

class Todo extends ObservableObject {
    static props = {
        // ...
    };
}

class TodoArray extends ObservableObject {
    static props = {
        // ...
    };

    static items = Todo;
}

const connection = restModel({
    url: "/api/todos",
    ObjectType: Todo,
    ArrayType: TodoArray
});

// Creates a template
let template = stache(`
    <ul>
        {{# if(this.todos.isPending) }}<li>Loading…</li>{{/ if }}
        {{# if(this.todos.isResolved) }}
            {{# for(todo of this.todos.value) }}
                <li class="{{# todo.complete }}complete{{/ todo.complete }}">{{ todo.name }}</li>
            {{else}}
                <li>No todos</li>
            {{/for}}
        {{/if}}
    </ul>
`);

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

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

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-stache-element

can-stache-element creates custom elements with unit-testable properties. It combines a view model created by can-observable-object with a template created by can-stache.

<todos-list></todos-list>

<script type="module">
import { StacheElement, stache, ObservableObject, ObservableArray, restModel } from "can";

class Todo extends ObservableObject {
    static props = {
        // ...
    };
}

class TodoArray extends ObservableObject {
    static props = {
        // ...
    };

    static items = Todo;
}

const connection = restModel({
    url: "/api/todos",
    ObjectType: Todo,
    ArrayType: TodoArray
});

class TodosList extends StacheElement {
    static view = `
            <ul>
                {{# if(this.todos.isPending) }}<li>Loading…</li>{{/ if }}
                {{# if(this.todos.isResolved) }}
                    {{# for(todo of this.todos.value) }}
                        <li class="{{# todo.complete }}complete{{/ todo.complete }}">{{ todo.name }}</li>
                    {{else}}
                        <li>No todos</li>
                    {{/for}}
                {{/if}}
            </ul>
    `;

    // Defines the todos-list component’s properties
    static props = {
        // An initial value that is a promise containing the
        // list of all todos.
        todos: {
            get default() {
                return Todo.getList({});
            }
        },
        // A method that toggles a todo’s complete property
        // and updates the todo on the server.
        toggleComplete(todo) {
            todo.complete = !todo.complete;
            todo.save();
        }
    };
}
customElements.define("todos-list", TodosList);
</script>

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 an ObservableObject’s properties to values in the url. Create an object type, connect it to the url, and begin routing like:

import { ObservableObject, route } from "can";

class AppViewModel extends ObservableObject {
    static props = {
        // Sets the default type to string
        todoId: String,
        todo: {
            get: function(){
                if(this.todoId) {
                    return Todo.get({_id: this.todoId})
                }
            }
        }
    };

    static items = String;
}

const appViewModel = new AppViewModel();
route.data = appViewModel;

route.start();

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.register 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.register("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. To use it, set route.urlData to an instance of RoutePushstate:

import { route, RoutePushstate } from "can";

route.urlData = new RoutePushstate();

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 API Docs 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 6.6.1.

On this page

Get help

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