DoneJS StealJS jQuery ++ FuncUnit DocumentJS
3.0.0
2.3.27

 

  • Github
  • Twitter
  • Chat
  • Forum
  • Guides
  • Core
    • can-component
    • can-compute
    • can-connect
    • can-define
    • can-define/list/list
    • can-define/map/map
    • can-route
    • can-route-pushstate
    • can-set
    • can-stache
    • can-stache/helpers/route
    • can-stache-bindings
  • Ecosystem
  • Infrastructure
  • Legacy
  • Bitovi
    • Bitovi.com
    • Blog
    • Consulting
    • Training
    • Open Source
  • Chat
  • Forum
  • Star
  • Follow @canjs
  • CanJS
  • /
  • Core
  • / On this page
    • Core

      page

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

      • source

      Use

      CanJS's core libraries are the modules most commonly used to build web applications. Each module is part of an independent package, so you should install the ones you use directly:

      npm install can-define can-set can-connect can-component can-stache can-route --save
      

      Lets explore each one 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 ($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:

      • (event)="key()" for event binding.
      • {prop}="key" for one-way binding to a child.
      • {^prop}="key" for one-way binding to a parent.
      • {(prop)}="key" for two-way binding.

      Adding $ to a binding like ($event)="key()" changes the binding from the viewModel to the element's attributes or properties.

      Event binding examples:

      <!-- calls `toggleComplete` when the li is clicked -->
      <li ($click)="toggleComplete(.)"/>
      
      <!-- calls `resetData` when cancel is dispatched on `my-modal`'s view model -->
      <my-modal (cancel)="resetData()"/>
      

      One-way to child examples:

      <!-- updates input's `checked` property with the value of complete -->
      <input type="checkbox" {$checked}="complete"/>
      
      <!-- updates `todo-lists`'s  `todos` property with the result of `getTodos`-->
      <todos-list {todos}="getTodos(complete=true)"/>
      

      One-way to parent examples:

      <!-- updates `complete` with input's `checked` property -->
      <input type="checkbox" {^$checked}="complete"/>
      
      <!-- updates `todosList` with `todo-lists`'s `todos` property -->
      <todos-list {^todos}="todosList"/>
      

      Two-way examples:

      <!-- Updates the input's `value` with `name` and vice versa -->
      <input type="text" {($value)}="name"/>
      
      <!-- Updates `date-picker`'s `date` with `dueDate` and vice versa -->
      <date-picker {(date)}="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, checkout the Guides page on how to learn CanJS. Specifically, you'll want to checkout the Chat Guide and TodoMVC Guide to learn the basics of using CanJS's core libraries. After that, checkout the 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.0.0.