Skip to content
xzer edited this page Oct 27, 2015 · 2 revisions

As being suggested in the Basic Usage, we can declare "_register_dom_change" to register DOM change observing, which is the standard way to declare 2-way binding.

Since most cases of 2-way binding are to observe traditional html form elements, a "_form" extension can be used to declare form element related binding and there is also a high level api "Aj.form()" to help developers to declare _form binding easier.

_form

The raw _form format is as following:

$scope.snippet("body")
      .bind($scope.data, {
        age : {
          _form : {
            _name : "age",
            _default_change_event : "change",
            _extra_change_events : ["keyup", "blur", "click"]
          }
        }
      });
  • _name

    Assign a css selector "[name=xxx]" to "_selector". if absent, the current property name will be treated as name.

  • _default_change_event

    A string value that suggests which event the framework should bind to DOM to handle the user input change. If absent, "click" is as default for "input[type=checkbox]" and "input[type=radio]", "change" is as default for others.

  • _extra_change_events

    A array value that suggests extra events that the framework should bind to DOM and will be treated as empty if absent.

_option and Aj.optionBind()

There are 3 types of form elements ("select", "input[type=checkbox]" and "input[type=radio]") requiring option data.

The following sample shows how to bind $scope.dataOption.bloodTypes as option of "select[name=bloodType]":

$scope.dataOption = {
  "bloodTypes":["A", "B", "AB", "O"]
};

$scope.snippet("body")
      .bind($scope.data, {
        bloodType : {
          _form : {
            _option: {
              _var_ref : $scope.dataOption,
              bloodTypes : {}
            }
          }
        }
      });

The _var_ref is the observable reference root just as the binding root $scope.data, then we also says that bind the "bloodTypes" as option, here we follow the default binding by a empty object definination.

For checkbox and radio, things become a little bit complicated, the following sample shows how to bind $scope.dataOption.genders as option of "input[type=checkbox][name=bloodType]":

$scope.dataOption = {
  "genders":[
    {
        "value": 0,
        "text": "female"
    },
    {
        "value": 1,
        "text": "male"
    }
  ]
}

$scope.snippet("body")
      .bind($scope.data, {
        sex : {
          _form: {
            _option:{
              _var_ref : $scope.dataOption,
              genders: {
                _duplicator: ".x-sex-group"
              }
            }
          }
        }
      });

We need to declare a "_duplicator" to tell the framework how to generate the duplicated "input[type=checkbox]" elements which is usually contains a pair of input and label element. In this sample, the option data $scope.dataOption.genders is an array of object rather than primitive value, Asta4j will treat the value field as the value(of the input element) and text field as the display text(the inner text of label).

More complicated value/text retrieving can be performed by customized "_value"/"_text" function, the following sample shows how to bind $scope.dataOption.languages as option of "input[type=radio][name=language]" and also retrieve the value/text in customization:

$scope.dataOption = {
  "languages": [
    {
      code: "en",
      name: "English"
    }, 
    {
      code: "ja",
      name: "Japanese"
    }
    {
      code: "zh", 
      name:"Chinese"
    }
  ]
}

$scope.snippet("body")
      .bind($scope.data, {
        language : {
          _form : {
            _option: {
              _var_ref : $scope.dataOption,
              languages:{
                _duplicator: ".x-lang-group"
                _item: {
                  _value: function(v){
                    return v.code;
                  },
                  _text: function(v){
                    return v.name;
                  }
                }
              }// end languages
            }//end _option
          }//end _form
        }
      });

Finally, there is a helper api "Aj.optionBind" which can make the above binding a little bit simpler:

$scope.snippet("body")
      .bind($scope.data, {
        bloodType : {
          _form : {
            _option: Aj.optionBind($scope.dataOption, "bloodTypes")
          }
        }
        sex : {
          _form: {
            _option: Aj.optionBind($scope.dataOption, "genders", ".x-sex-group")
          }
        },
        language : {
          _form : {
            _option: Aj.optionBind($scope.dataOption, {
              languages:{
                _duplicator: ".x-lang-group"
                _item: {
                  _value: function(v){
                    return v.code;
                  },
                  _text: function(v){
                    return v.name;
                  }
                }
              }// end languages
            })//end _option
          }//end _form
        }
      });

Aj.form()

Aj.form accepts up to three arguments.

  • ()

    If no argument is specified, a meta object of an empty "_form" will be returned.

    $scope.snippet("body")
          .bind($scope.data, {
            age: Aj.form()
          });

    is as the same as:

    $scope.snippet("body")
          .bind($scope.data, {
            age: {
              _form: {}
            }
          });
  • (target)

    There are two possible types of target:

    • string

      String value will be treated as name or selector, thus the framework will set the "_selector" as "[name=xxx], xxx"

    • object

      The properties of "selector" and "name" will be retrieved to set value for "_selector" and "_form._name", "selector" is in priority.

  • (target, event)

    The target follows the above rule, the event has two possible types:

    • string

      A string value will be treated as "_default_change_event".

    • array

      An array value will be treated as "_extra_change_events".

  • (target, defaultChangeEvent, extraChangeEvent)

    As the arguments name suggest.

Further, the Aj.form() returns a meta with "_form" definition which allows futher customization by some exposed api:

  • asSingleCheck()

    There is a case that an "input[type=checkbox/radio]" may be a boolean flag without any extra option list, the raw meta can be as following:

    $scope.snippet("body")
          .bind($scope.data, {
            private: {
              _form: {
                _single_check: true
              }
            }
          });

    Then Aj.form() version is as following:

    $scope.snippet("body")
          .bind($scope.data, {
            private: Aj.form().asSingleCheck()
          });
  • withOption(...)

    The "withOption()" is compeletely as the same as the "Aj.optionBind()" as a convinence in Aj.form():

    $scope.snippet("body")
          .bind($scope.data, {
            sex : Aj.form().withOption($scope.dataOption, "genders", ".x-sex-group")
          });
  • transform(transform)

    Add the accepted object to "_transform".

    $scope.snippet("body")
          .bind($scope.data, {
              sex : Aj.form()
                      .withOption($scope.dataOption, "genders", ".x-sex-group")
                      .transform({
                        _get_value: function(v){...},
                        _set_value: function(v){...},
                      })
          });

    There are several built-in transformers can be used too:

    • asInt(radix)

      $scope.snippet("body")
            .bind($scope.data, {
                sex : Aj.form()
                        .withOption($scope.dataOption, "genders", ".x-sex-group")
                        .asInt(10)
            });
    • asFloat()

      The same as asInt, just without radix required.

    • asDatetime(option)

      In HTML5, the value format of "input[type=datetime-local]" is ISO8601 without time zone, but a ISO8601 string will be treated as UTC time by javascript's Date.parse(). This api is useful to help developer to combat with this conflict. To work with "input[type=datetime-local]", the option keeps undefined is OK.

      $scope.snippet("body")
            .bind($scope.data, {
                time : Aj.form()
                        .withOption($scope.dataOption, "genders", ".x-sex-group")
                        .asDatetime()
            });
  • fileOption(...)

    For "input[type=file]", since we cannot set the value to it, we can only perform 1-way binding on it, from DOM to model. By default, Asta4js will handle the value of "input[type=file]" as the new file api's File type.

    If the property "multiple" is specified, the value will be an array of File, or a single File reference if "multiple" is absent.

    Also, Asta4js will add the preloaded file content to the returned Fiel reference with named property "content". For this default action, there are two options that need to be configured.

    The raw format of file option is as following:

    $scope.snippet("body")
          .bind($scope.data, {
              file : {
                _file_preload_limit : 100,
                _file_preload_format : "dataurl" 
              }
          });

    Asta4js will only preload the files which size are less than the given limit, if absent, the default value is 10M.

    Available format string is:

    • ArrayBuffer
    • BinaryString
    • DataURL
    • Text
    • base64

    Case is not matter and the default value is "base64".

    Then, the above can be simplified by Aj.form() as following:

    $scope.snippet("body")
          .bind($scope.data, {
              file : Aj.form().fileOption(100, "dataurl")
          });

    The order of limit and format in arguments is not matter, the following arguments are acceptable:

    • (limit):

      A number.

    • (format)

      A string.

    • (limit, format)

      A number and a string.

    • (format, limit)

      A string and a number.

    • ({limit: ?, format: ?})

    • ({_file_preload_limit: ?, _file_preload_format: ?})

      The latter one is in priority.

  • override(obj)

    The extending out of support of Aj.form() can be performed by override the generated meta by Aj.form().

    $scope.snippet("body")
          .bind($scope.data, {
              age : Aj.form().override({
                _selector: "#somewhat",
                _form: {
                  _extra_change_events: ["blur"]
                }
              })
          });