Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working With Forms Guide #3708

justinbmeyer opened this Issue Nov 2, 2017 · 8 comments


None yet
4 participants
Copy link

commented Nov 2, 2017

tldr; We propose a guide that will teach people how to work with forms, including:

  • The different types of form elements, including custom elements.
  • Using types and setters on ViewModels.
  • Converters
  • Validation
  • Backup (or clone) and restore
  • Custom events and attributes

This was discussed on a recent live stream (30:02)

The Problem

It's difficult to discover all the form-related tools that CanJS provides. It is even more difficult to know how to combine them to achieve some desired functionality.

For example, the following are some useful form related tools:

  • Custom Events
  • Custom Attributes
    • values Used to work better with <select multiple>. Allows reading and setting the values in the <select> as an array.
    • focused - an attribute that indicates if the element is focused or not.
  • can-stache-bindings basic binding syntaxes: on:event, key:from, key:to, and key:bind.
  • can-stache-bindings "advanced" binding syntaxes: on:input:value:to
  • type: "number" on DefineMap to ensure strings are converted to numbers
  • stringToAny utility that converts a string to what we judge is its best type.
  • can-stache-converters that translate between two computes, including the string-to-any converter.
  • can-define-validate-validatejs - validations on a DefineMap
  • can-connect-cloneable - Cloneable maps, whose clones can be changed, and the changes .save()d to the server and used to update the source map.

The solution

I propose a guide that walks through the following use cases (or links to examples elsewhere in CanJS):

  • Connecting a viewModel to all commonly implemented form types (reading and writing).
    • Using converters and DefineMap types to keep your DOM as strings, but your VM as "nice" types.
    • Using custom attributes to simplify difficult to read form values.
  • The different ways of connecting to those form types:
    • Two-way binding pattern: value:bind="key"
    • Discrete event-up / value-down pattern: on:input="updateKey(scope.element.value)" value:from="key"
      • Q: What to do if user types, but you want to set <input> value "back" to old key value, but key hasn't changed?
    • Hybrid and shortcut patterns: on:input:value:to
  • Performing Validations
  • Creating new data
  • Updating data

This comment has been minimized.

Copy link
Contributor Author

commented Nov 2, 2017

Q: What to do if user types, but you want to set value "back" to old key value, but key hasn't changed?

This, imo, is really only currently solved well by streams which can produce the same value over and over again. I think this problem is worth an issue and investigation of its own.

One imperative solution:

<input on:input:value:bind="inputValue">

VM = {
  inputValue: "string",
  value: "string",
  reset: function(){
   this.inputValue = this.value;
  save: function(){
    this.value = this.inputValue;

Another example from @phillipskevin for illegal characters:

var VM = can.DefineMap.extend({
  key: { type: 'string', value: '123' },
  updateKey(val) {
    if (/* val is illegal */) {
      var oldVal = this.key;
      this.key = '';
      this.key = oldVal;
    } else {
      this.key = val;      

var view = can.stache(`
  <input on:input="updateKey(%element.value)" value:from="key"/>

@justinbmeyer justinbmeyer referenced this issue Nov 2, 2017


Epoch 1 Survey Questions #77

24 of 24 tasks complete

This comment has been minimized.

Copy link

commented Nov 4, 2017

It would be great if the guide will have how to handle models relations, example Post map has many Category maps (Category.List) than how to handle categoryIDs <=>Category.List?
I'm assuming that Post and Category are models with can-connect:

var Category = DefineMap.extend({
  id: 'any',
  name :'string'

var Post = DefineMap.extend({
  id: 'any',
  title: 'string',
  categories: {
    Tye: Category.List,
    Value: Category.List

var EditPostVM = DefineMap.extend({
  post: {
    Type: Post,
    Value: Post
  categoryIds: {
    Type: DefineList
   * for categories list
  categoriesPromise: {
    value: function() {
      return Category.getList({})
  addRemoveCategoryId:function(ev) {
  savePromise: function() {
    // ....

{{#if categoriesPromise.isResolved}}
 {{#each categoriesPromise.value}}
      <input type="checkbox" value:from="id"  on:change="addRemoveCategoryId(scope.event)">

This comment has been minimized.

Copy link

commented Nov 11, 2017

It would be more then great if we show handling of file uploads i mean the special file propertys. how they get binded best.


This comment has been minimized.

Copy link

commented Feb 14, 2018

Here is the outline for the first draft of the guide that I am working on now:

## Form Elements

### <input type="text">
* binding to events
  - on:change
  - list of standard events that can be bound to
* binding to attributes
  - value:to
  - value:from
  - value=“{{foo}}”
  - list of standard attributes that can be bound to
* binding to events and attributes
  - value:bind
  - on:input:value:to

### <input type="checkbox">
* checked:from
* {{#if checked}}checked{{/if}}
* checked:bind=“boolean-to-inList(key, list)”
* checked:bind=“either-or(…)” 

### <input type="radio">
* can-event-dom-radiochange
* checked:bind=“equal(key, value)” 

### <input type="number">
* type: "number"

### <input type="submit">
* can-event-dom-enter

### <input type="file">

### <select>

### <select multiple>
* value:bind

### <textarea>

## Fancy bindings

### Non-standard attributes
* focused
* disabled

### Non-standard events
* Reasoning
  - Keep DOM-related functionality out of your ViewModel (ev.type === 13, etc)
* enter
  - can-event-dom-enter
* radiochange
  - can-event-dom-radiochange

### events up, data down
* on:change=“scope.set(“key”, scope.element.value)” value:from=“key”

### Resetting <input> value back
* explain that two-way binding normally sets <input> value back when a “change” event occurs on the ViewModel property
* if a setter or resolver does not change the value (because it is illegal), there is no change event
* with :bind, can-stache-bindings will handle this for you

## Validation
* can-define-validate-validatejs

A few things that I haven't added to the outline yet (so I don't forget about them):

  • can-connect-cloneable
  • creating new data
  • updating data

This comment has been minimized.

Copy link

commented Feb 21, 2018

I'm leaving out the can-event-dom-radiochange for now as I don't see a use-case for using this directly (not through can-stache-bindings and the checked attribute).


This comment has been minimized.

Copy link

commented Feb 22, 2018

@phillipskevin can-connect-cloneable isn't that replacing can-define-map-backup ? stuff?


This comment has been minimized.

Copy link

commented Feb 22, 2018

I think can-define-backup and can-connect-cloneable have slightly different goals, but they are related. I don't think one is replacing the other.


This comment has been minimized.

Copy link

commented Feb 27, 2018

Remaining changes:

  • Revamp overview section so that it gives people a better understanding of what they're getting into (take a look at Tech Overview guide)
  • Make Custom Attributes and Custom Events more obvious
  • Add a "working with related data" example
    • make, model, year example with can-ajax
    • how to load data
    • how to use promises in forms
    • the string-to-any utility
    • link to issue on "creating new data" / "updating data"
  • Remove use of <label> where they prevent you from clicking outside <input> to produce "change" events
  • Remove use of scope.set(...) in "data down, actions up" section
  • Expand "data down, actions up" section to show an example with multiple components
  • Use Components in all the examples
  • explain when on:click will be called when hitting the Enter key inside an <input> in a form
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.