dijit/form/_FormMixin.js

  • Provides:

    • dijit.form._FormMixin
  • dijit.form._FormMixin

    • type
      Function
    • summary
      Mixin for containers of form widgets (i.e. widgets that represent a single value
      and can be children of a <form> node or dijit.form.Form widget)
  • dijit.form._FormMixin.state

    • summary
  • dijit.form._FormMixin.reset

    • type
      Function
    • source: [view]
         dojo.forEach(this.getDescendants(), function(widget){
          if(widget.reset){
           widget.reset();
          }
         });
    • summary
  • dijit.form._FormMixin.validate

    • type
      Function
    • source: [view]
         var didFocus = false;
         return dojo.every(dojo.map(this.getDescendants(), function(widget){
          // Need to set this so that "required" widgets get their
          // state set.
          widget._hasBeenBlurred = true;
          var valid = widget.disabled || !widget.validate || widget.validate();
          if(!valid && !didFocus){
           // Set focus of the first non-valid widget
           dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
           widget.focus();
           didFocus = true;
          }
           return valid;
          }), function(item){ return item; });
    • summary
      returns if the form is valid - same as isValid - but
      provides a few additional (ui-specific) features.
      1 - it will highlight any sub-widgets that are not
      valid
      2 - it will call focus() on the first invalid
      sub-widget
  • dijit.form._FormMixin.setValues

    • type
      Function
    • parameters:
      • val: (typeof )
    • source: [view]
         dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
         return this.set('value', val);
    • summary
  • dijit.form._FormMixin._setValueAttr

    • type
      Function
    • parameters:
      • obj: (typeof Object)
    • source: [view]
      define("dijit/form/_FormMixin", ["dojo", "dijit", "dojo/window"], function(dojo, dijit) {


      dojo.declare("dijit.form._FormMixin", null, {
       // summary:
       //  Mixin for containers of form widgets (i.e. widgets that represent a single value
       //  and can be children of a
      node or dijit.form.Form widget)
       // description:
       //  Can extract all the form widgets
       //  values and combine them into a single javascript object, or alternately
       //  take such an object and set the values for all the contained
       //  form widgets




       // value: Object
       //  Name/value hash for each child widget with a name and value.
       //  Child widgets without names are not part of the hash.
       //
       //  If there are multiple child widgets w/the same name, value is an array,
       //  unless they are radio buttons in which case value is a scalar (since only
       //  one radio button can be checked at a time).
       //
       //  If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
       //
       //  Example:
       // | { name: "John Smith", interests: ["sports", "movies"] }




       // state: [readonly] String
       //  Will be "Error" if one or more of the child widgets has an invalid value,
       //  "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
       //  which indicates that the form is ready to be submitted.
       state: "",


       // TODO:
       // * Repeater
       // * better handling for arrays. Often form elements have names with [] like
       // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
       //
       //


        reset: function(){
         dojo.forEach(this.getDescendants(), function(widget){
          if(widget.reset){
           widget.reset();
          }
         });
        },


        validate: function(){
         // summary:
         //  returns if the form is valid - same as isValid - but
         //  provides a few additional (ui-specific) features.
         //  1 - it will highlight any sub-widgets that are not
         //   valid
         //  2 - it will call focus() on the first invalid
         //   sub-widget
         var didFocus = false;
         return dojo.every(dojo.map(this.getDescendants(), function(widget){
          // Need to set this so that "required" widgets get their
          // state set.
          widget._hasBeenBlurred = true;
          var valid = widget.disabled || !widget.validate || widget.validate();
          if(!valid && !didFocus){
           // Set focus of the first non-valid widget
           dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
           widget.focus();
           didFocus = true;
          }
           return valid;
          }), function(item){ return item; });
        },


        setValues: function(val){
         dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
         return this.set('value', val);
        },
        _setValueAttr: function(/*Object*/ obj){
         // summary:
         //  Fill in form values from according to an Object (in the format returned by get('value'))


         // generate map from name --> [list of widgets with that name]
         var map = { };
         dojo.forEach(this.getDescendants(), function(widget){
          if(!widget.name){ return; }
          var entry = map[widget.name] || (map[widget.name] = [] );
          entry.push(widget);
         });


         for(var name in map){
          if(!map.hasOwnProperty(name)){
           continue;
          }
          var widgets = map[name],      // array of widgets w/this name
           values = dojo.getObject(name, false, obj); // list of values for those widgets


          if(values === undefined){
           continue;
          }
          if(!dojo.isArray(values)){
           values = [ values ];
          }
          if(typeof widgets[0].checked == 'boolean'){
           // for checkbox/radio, values is a list of which widgets should be checked
           dojo.forEach(widgets, function(w, i){
            w.set('value', dojo.indexOf(values, w.value) != -1);
           });
          }else if(widgets[0].multiple){
           // it takes an array (e.g. multi-select)
           widgets[0].set('value', values);
          }else{
           // otherwise, values is a list of values to be assigned sequentially to each widget
           dojo.forEach(widgets, function(w, i){
            w.set('value', values[i]);
           });
          }
         }


         /***
          *  TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)


         dojo.forEach(this.containerNode.elements, function(element){
          if(element.name == ''){return}; // like "continue"
          var namePath = element.name.split(".");
          var myObj=obj;
          var name=namePath[namePath.length-1];
          for(var j=1,len2=namePath.length;j     var p=namePath[j - 1];
           // repeater support block
           var nameA=p.split("[");
           if(nameA.length > 1){
            if(typeof(myObj[nameA[0]]) == "undefined"){
             myObj[nameA[0]]=[ ];
            } // if


            nameIndex=parseInt(nameA[1]);
            if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
             myObj[nameA[0]][nameIndex] = { };
            }
            myObj=myObj[nameA[0]][nameIndex];
            continue;
           } // repeater support ends


           if(typeof(myObj[p]) == "undefined"){
            myObj=undefined;
            break;
           };
           myObj=myObj[p];
          }


          if(typeof(myObj) == "undefined"){
           return;  // like "continue"
          }
          if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
           return;  // like "continue"
          }


          // TODO: widget values (just call set('value', ...) on the widget)


          // TODO: maybe should call dojo.getNodeProp() instead
          switch(element.type){
           case "checkbox":
            element.checked = (name in myObj) &&
             dojo.some(myObj[name], function(val){ return val == element.value; });
            break;
           case "radio":
            element.checked = (name in myObj) && myObj[name] == element.value;
            break;
           case "select-multiple":
            element.selectedIndex=-1;
            dojo.forEach(element.options, function(option){
             option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
            });
            break;
           case "select-one":
            element.selectedIndex="0";
            dojo.forEach(element.options, function(option){
             option.selected = option.value == myObj[name];
            });
            break;
           case "hidden":
           case "text":
           case "textarea":
           case "password":
            element.value = myObj[name] || "";
            break;
          }
          });
          */

         
         // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
         // which I am monitoring.
    • returns
      like "continue"
    • summary
  • dijit.form._FormMixin.getValues

    • type
      Function
    • source: [view]
         dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
         return this.get('value');
    • summary
  • dijit.form._FormMixin._getValueAttr

    • type
      Function
    • source: [view]
      define("dijit/form/_FormMixin", ["dojo", "dijit", "dojo/window"], function(dojo, dijit) {


      dojo.declare("dijit.form._FormMixin", null, {
       // summary:
       //  Mixin for containers of form widgets (i.e. widgets that represent a single value
       //  and can be children of a node or dijit.form.Form widget)
       // description:
       //  Can extract all the form widgets
       //  values and combine them into a single javascript object, or alternately
       //  take such an object and set the values for all the contained
       //  form widgets




       // value: Object
       //  Name/value hash for each child widget with a name and value.
       //  Child widgets without names are not part of the hash.
       //
       //  If there are multiple child widgets w/the same name, value is an array,
       //  unless they are radio buttons in which case value is a scalar (since only
       //  one radio button can be checked at a time).
       //
       //  If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
       //
       //  Example:
       // | { name: "John Smith", interests: ["sports", "movies"] }




       // state: [readonly] String
       //  Will be "Error" if one or more of the child widgets has an invalid value,
       //  "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
       //  which indicates that the form is ready to be submitted.
       state: "",


       // TODO:
       // * Repeater
       // * better handling for arrays. Often form elements have names with [] like
       // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
       //
       //


        reset: function(){
         dojo.forEach(this.getDescendants(), function(widget){
          if(widget.reset){
           widget.reset();
          }
         });
        },


        validate: function(){
         // summary:
         //  returns if the form is valid - same as isValid - but
         //  provides a few additional (ui-specific) features.
         //  1 - it will highlight any sub-widgets that are not
         //   valid
         //  2 - it will call focus() on the first invalid
         //   sub-widget
         var didFocus = false;
         return dojo.every(dojo.map(this.getDescendants(), function(widget){
          // Need to set this so that "required" widgets get their
          // state set.
          widget._hasBeenBlurred = true;
          var valid = widget.disabled || !widget.validate || widget.validate();
          if(!valid && !didFocus){
           // Set focus of the first non-valid widget
           dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
           widget.focus();
           didFocus = true;
          }
           return valid;
          }), function(item){ return item; });
        },


        setValues: function(val){
         dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
         return this.set('value', val);
        },
        _setValueAttr: function(/*Object*/ obj){
         // summary:
         //  Fill in form values from according to an Object (in the format returned by get('value'))


         // generate map from name --> [list of widgets with that name]
         var map = { };
         dojo.forEach(this.getDescendants(), function(widget){
          if(!widget.name){ return; }
          var entry = map[widget.name] || (map[widget.name] = [] );
          entry.push(widget);
         });


         for(var name in map){
          if(!map.hasOwnProperty(name)){
           continue;
          }
          var widgets = map[name],      // array of widgets w/this name
           values = dojo.getObject(name, false, obj); // list of values for those widgets


          if(values === undefined){
           continue;
          }
          if(!dojo.isArray(values)){
           values = [ values ];
          }
          if(typeof widgets[0].checked == 'boolean'){
           // for checkbox/radio, values is a list of which widgets should be checked
           dojo.forEach(widgets, function(w, i){
            w.set('value', dojo.indexOf(values, w.value) != -1);
           });
          }else if(widgets[0].multiple){
           // it takes an array (e.g. multi-select)
           widgets[0].set('value', values);
          }else{
           // otherwise, values is a list of values to be assigned sequentially to each widget
           dojo.forEach(widgets, function(w, i){
            w.set('value', values[i]);
           });
          }
         }


         /***
          *  TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)


         dojo.forEach(this.containerNode.elements, function(element){
          if(element.name == ''){return}; // like "continue"
          var namePath = element.name.split(".");
          var myObj=obj;
          var name=namePath[namePath.length-1];
          for(var j=1,len2=namePath.length;j     var p=namePath[j - 1];
           // repeater support block
           var nameA=p.split("[");
           if(nameA.length > 1){
            if(typeof(myObj[nameA[0]]) == "undefined"){
             myObj[nameA[0]]=[ ];
            } // if


            nameIndex=parseInt(nameA[1]);
            if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
             myObj[nameA[0]][nameIndex] = { };
            }
            myObj=myObj[nameA[0]][nameIndex];
            continue;
           } // repeater support ends


           if(typeof(myObj[p]) == "undefined"){
            myObj=undefined;
            break;
           };
           myObj=myObj[p];
          }


          if(typeof(myObj) == "undefined"){
           return;  // like "continue"
          }
          if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
           return;  // like "continue"
          }


          // TODO: widget values (just call set('value', ...) on the widget)


          // TODO: maybe should call dojo.getNodeProp() instead
          switch(element.type){
           case "checkbox":
            element.checked = (name in myObj) &&
             dojo.some(myObj[name], function(val){ return val == element.value; });
            break;
           case "radio":
            element.checked = (name in myObj) && myObj[name] == element.value;
            break;
           case "select-multiple":
            element.selectedIndex=-1;
            dojo.forEach(element.options, function(option){
             option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
            });
            break;
           case "select-one":
            element.selectedIndex="0";
            dojo.forEach(element.options, function(option){
             option.selected = option.value == myObj[name];
            });
            break;
           case "hidden":
           case "text":
           case "textarea":
           case "password":
            element.value = myObj[name] || "";
            break;
          }
          });
          */

         
         // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
         // which I am monitoring.
        },


        getValues: function(){
         dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
         return this.get('value');
        },
        _getValueAttr: function(){
         // summary:
         //   Returns Object representing form values. See description of `value` for details.
         // description:


         // The value is updated into this.value every time a child has an onChange event,
         // so in the common case this function could just return this.value. However,
         // that wouldn't work when:
         //
         // 1. User presses return key to submit a form. That doesn't fire an onchange event,
         // and even if it did it would come too late due to the setTimout(..., 0) in _handleOnChange()
         //
         // 2. app for some reason calls this.get("value") while the user is typing into a
         // form field. Not sure if that case needs to be supported or not.


         // get widget values
         var obj = { };
         dojo.forEach(this.getDescendants(), function(widget){
          var name = widget.name;
          if(!name || widget.disabled){ return; }


          // Single value widget (checkbox, radio, or plain type widget)
          var value = widget.get('value');


          // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
          if(typeof widget.checked == 'boolean'){
           if(/Radio/.test(widget.declaredClass)){
            // radio button
            if(value !== false){
             dojo.setObject(name, value, obj);
            }else{
             // give radio widgets a default of null
             value = dojo.getObject(name, false, obj);
             if(value === undefined){
              dojo.setObject(name, null, obj);
             }
            }
           }else{
            // checkbox/toggle button
            var ary=dojo.getObject(name, false, obj);
            if(!ary){
             ary=[];
             dojo.setObject(name, ary, obj);
            }
            if(value !== false){
             ary.push(value);
            }
           }
          }else{
           var prev=dojo.getObject(name, false, obj);
           if(typeof prev != "undefined"){
            if(dojo.isArray(prev)){
             prev.push(value);
            }else{
             dojo.setObject(name, [prev, value], obj);
            }
           }else{
            // unique name
            dojo.setObject(name, value, obj);
           }
          }
         });


         /***
          * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
          * but it doesn't understand [] notation, presumably)
         var obj = { };
         dojo.forEach(this.containerNode.elements, function(elm){
          if(!elm.name) {
           return;  // like "continue"
          }
          var namePath = elm.name.split(".");
          var myObj=obj;
          var name=namePath[namePath.length-1];
          for(var j=1,len2=namePath.length;j     var nameIndex = null;
           var p=namePath[j - 1];
           var nameA=p.split("[");
           if(nameA.length > 1){
            if(typeof(myObj[nameA[0]]) == "undefined"){
             myObj[nameA[0]]=[ ];
            } // if
            nameIndex=parseInt(nameA[1]);
            if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
             myObj[nameA[0]][nameIndex] = { };
            }
           } else if(typeof(myObj[nameA[0]]) == "undefined"){
            myObj[nameA[0]] = { }
           } // if


           if(nameA.length == 1){
            myObj=myObj[nameA[0]];
           } else{
            myObj=myObj[nameA[0]][nameIndex];
           } // if
          } // for


          if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
           if(name == name.split("[")[0]){
            myObj[name]=elm.value;
           } else{
            // can not set value when there is no name
           }
          } else if(elm.type == "checkbox" && elm.checked){
           if(typeof(myObj[name]) == 'undefined'){
            myObj[name]=[ ];
           }
           myObj[name].push(elm.value);
          } else if(elm.type == "select-multiple"){
           if(typeof(myObj[name]) == 'undefined'){
            myObj[name]=[ ];
           }
           for(var jdx=0,len3=elm.options.length; jdx      if(elm.options[jdx].selected){
             myObj[name].push(elm.options[jdx].value);
            }
           }
          } // if
          name=undefined;
         }); // forEach
         ***/
         return obj;
    • summary
      Returns Object representing form values.   See description of `value` for details.
    • returns
      like "continue"
  • dijit.form._FormMixin.isValid

    • type
      Function
    • source: [view]
         return this.state == "";
    • summary
      Returns true if all of the widgets are valid.
      Deprecated, will be removed in 2.0.  Use get("state") instead.
  • dijit.form._FormMixin.onValidStateChange

    • type
      Function
    • parameters:
      • isValid: (typeof )
    • source: [view]
         // summary:
         //  Stub function to connect to if you want to do something
         //  (like disable/enable a submit button) when the valid
         //  state changes on the form as a whole.
         //
         //  Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
    • summary
      Stub function to connect to if you want to do something
      (like disable/enable a submit button) when the valid
      state changes on the form as a whole.
      
      Deprecated.  Will be removed in 2.0.  Use watch("state", ...) instead.
  • dijit.form._FormMixin._getState

    • type
      Function
    • source: [view]
         var states = dojo.map(this._descendants, function(w){
          return w.get("state") || "";
         });


         return dojo.indexOf(states, "Error") >= 0 ? "Error" :
          dojo.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
    • summary
      Compute what this.state should be based on state of children
  • dijit.form._FormMixin.disconnectChildren

    • type
      Function
    • source: [view]
         dojo.forEach(this._childConnections || [], dojo.hitch(this, "disconnect"));
         dojo.forEach(this._childWatches || [], function(w){ w.unwatch(); });
    • summary
      Remove connections to monitor changes to children's value, error state, and disabled state,
      in order to update Form.value and Form.state.
  • dijit.form._FormMixin.connectChildren

    • type
      Function
    • parameters:
      • inStartup: (typeof Boolean)
    • source: [view]
         var _this = this;


         // Remove old connections, if any
         this.disconnectChildren();


         this._descendants = this.getDescendants();


         // (Re)set this.value and this.state. Send watch() notifications but not on startup.
         var set = inStartup ? function(name, val){ _this[name] = val; } : dojo.hitch(this, "_set");
         set("value", this.get("value"));
         set("state", this._getState());


         // Monitor changes to error state and disabled state in order to update
         // Form.state
         var conns = (this._childConnections = []),
          watches = (this._childWatches = []);
         dojo.forEach(dojo.filter(this._descendants,
          function(item){ return item.validate; }
         ),
         function(widget){
          // We are interested in whenever the widget changes validity state - or
          // whenever the disabled attribute on that widget is changed.
          dojo.forEach(["state", "disabled"], function(attr){
           watches.push(widget.watch(attr, function(attr, oldVal, newVal){
            _this.set("state", _this._getState());
           }));
          });
         });


         // And monitor calls to child.onChange so we can update this.value
         var onChange = function(){
          // summary:
          //  Called when child's value or disabled state changes

          
          // Use setTimeout() to collapse value changes in multiple children into a single
          // update to my value. Multiple updates will occur on:
          // 1. Form.set()
          // 2. Form.reset()
          // 3. user selecting a radio button (which will de-select another radio button,
          //   causing two onChange events)
          if(_this._onChangeDelayTimer){
           clearTimeout(_this._onChangeDelayTimer);
          }
          _this._onChangeDelayTimer = setTimeout(function(){
           delete _this._onChangeDelayTimer;
           _this._set("value", _this.get("value"));
          }, 10);
         };
         dojo.forEach(
          dojo.filter(this._descendants, function(item){ return item.onChange; } ),
          function(widget){
           // When a child widget's value changes,
           // the efficient thing to do is to just update that one attribute in this.value,
           // but that gets a little complicated when a checkbox is checked/unchecked
           // since this.value["checkboxName"] contains an array of all the checkboxes w/the same name.
           // Doing simple thing for now.
           conns.push(_this.connect(widget, "onChange", onChange));


           // Disabling/enabling a child widget should remove it's value from this.value.
           // Again, this code could be more efficient, doing simple thing for now.
           watches.push(widget.watch("disabled", onChange));
          }
         );
    • summary
      Setup connections to monitor changes to children's value, error state, and disabled state,
      in order to update Form.value and Form.state.
      
      You can call this function directly, ex. in the event that you
      programmatically add a widget to the form *after* the form has been
      initialized.
  • dijit.form._FormMixin.startup

    • type
      Function
    • source: [view]
         this.inherited(arguments);


         // Initialize value and valid/invalid state tracking. Needs to be done in startup()
         // so that children are initialized.
         this.connectChildren(true);


         // Make state change call onValidStateChange(), will be removed in 2.0
         this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
    • summary
  • dijit.form._FormMixin.destroy

    • type
      Function
    • source: [view]
         this.disconnectChildren();
         this.inherited(arguments);
    • summary
  • dijit.form._FormMixin._descendants

    • summary
  • dijit.form._FormMixin._childConnections

    • summary
  • dijit.form._FormMixin._childWatches

    • summary
  • dijit.form

    • type
      Object
    • summary
  • dijit

    • type
      Object
    • summary