<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      wexin

      進(jìn)一步豐富和簡化表單管理的組件:form.js

      上文《簡潔易用的表單數(shù)據(jù)設(shè)置和收集管理組件》介紹了我自己的表單管理的核心內(nèi)容,本文在上文的基礎(chǔ)上繼續(xù)介紹自己關(guān)于表單初始值獲取和設(shè)置以及表單數(shù)據(jù)提交等內(nèi)容方面的做法,上文的組件粒度很小,都是跟單個(gè)表單元素相關(guān)的某種特定類型的組件,所以內(nèi)容很多;本文要介紹的內(nèi)容集中于整個(gè)表單組件本身,有點(diǎn)像上文介紹的formMap.js組件,但不同的是在我自己的項(xiàng)目中form.js用的更多,formMap幾乎不用,因?yàn)樵趂orm的內(nèi)部就有用到formMap組件的實(shí)例來管理表單的數(shù)據(jù),之所以這么做,也是為了讓各個(gè)組件的功能更加單一,方便今后的維護(hù)和重用。form.js的代碼不多,只有200多行,該組件以及我提供的demo頁面的js內(nèi)都有比較詳細(xì)的注釋,方便有興趣的朋友閱讀參考。

      form.js的代碼地址:

      https://github.com/liuyunzhuge/blog/blob/master/form/src/js/mod/form.js

      demo地址:

      新增模式:

      http://liuyunzhuge.github.io/blog/form/dist/html/demo2.html?mode=1

      編輯模式:

      http://liuyunzhuge.github.io/blog/form/dist/html/demo2.html?mode=2&id=1

      form.js解決的問題

      在我自己以前開發(fā)項(xiàng)目的經(jīng)驗(yàn)中,在開發(fā)一個(gè)表單的時(shí)候會(huì)遇到下列的一些問題:

      1)表單各個(gè)字段的初始值如何設(shè)置?
      雖然上文的內(nèi)容已經(jīng)解決了如何區(qū)分新增模式和編輯模式的初始值,但是還存在的問題是如果某個(gè)字段的初始值需要從后臺返回該如何處理?傳統(tǒng)項(xiàng)目中我們可以用后臺模板來解決,但是假如是一個(gè)純前后端分離的項(xiàng)目呢,那就沒有后臺模板可以利用了;還有,從后臺返回的話,如果是獲取編輯模式的初始值,意味著要后臺從數(shù)據(jù)庫查詢相應(yīng)的數(shù)據(jù),這個(gè)時(shí)候如何規(guī)范傳遞給后臺查詢初始值的接口參數(shù)?

      2)假如通過接口來獲取初始值,編輯模式一定需要ajax,但是新增模式就不一定需要,意味著同樣的一個(gè)功能,有的時(shí)候可能是異步的,有的時(shí)候可能是同步的,這種情況該如何統(tǒng)一?

      3)如果新增模式和編輯模式要使用不同的接口來保存該如何處理?

      4)一般表單在保存之后都會(huì)根據(jù)后臺返回的響應(yīng)添加一些交互邏輯,但是每個(gè)保存功能的交互邏輯都是不固定的,更別說項(xiàng)目之間的區(qū)別了,如何才能讓表單的保存功能更加單一,不受其它功能的影響?

      5)提交到后臺的數(shù)據(jù)一般都是按照querystring的格式提交,但有時(shí)候?yàn)榱朔奖悖趐hp里面會(huì)將querystring的參數(shù)名都封裝成數(shù)組索引的形式,比如有一個(gè)參數(shù)id=1,就會(huì)變成某個(gè)model名稱加參數(shù)名稱的形式如 User[id]=1,這么做是為了配合后臺的數(shù)據(jù)解析功能;而在java里面,更喜歡直接把整個(gè)表單的所有參數(shù)合并成一個(gè)參數(shù),把所有數(shù)據(jù)通過json字符串來傳遞,java后臺也有好用的工具將數(shù)據(jù)直接解析成model,要考慮兼容這樣的問題,表單組件在提交數(shù)據(jù)的時(shí)候該如何管理?

      這些問題,我考慮的解決方法是:

      1)使用以下幾個(gè)option來管理通過接口初始值的獲取和設(shè)置的功能:

      queryUrl: '',//編輯模式時(shí)查詢初始值的url
      key: '',//編輯模式時(shí)使用它作為主鍵的值,跟在queryUrl后面?zhèn)鬟f到后臺查詢數(shù)據(jù)
      keyName: '',//編輯模式時(shí)使用它作為主鍵的名稱,跟在queryUrl后面?zhèn)鬟f到后臺查詢數(shù)據(jù)
      defaultData: {},//新增模式時(shí)的默認(rèn)值,可以是一個(gè)object,也可以是一個(gè)字符串,是字符串的時(shí)候表示一個(gè)后臺查詢的接口地址

      看這部分的代碼就能明白它們的實(shí)際作用了:

      //獲取表單初始化數(shù)據(jù)
      getInitData: function () {
          var opts = this.options,
              mode = this.mode;
      
          //這個(gè)函數(shù)返回的格式,包含三個(gè)參數(shù),各個(gè)參數(shù)的含義如下:
          //valid: true表示有效,false表示無效
          //ajax: true表示data返回的是jquery創(chuàng)建的ajax對象,false表示不是
          //data: 當(dāng)ajax為true的時(shí)候返回jquery創(chuàng)建的ajax對象,否則直接返回一個(gè)object實(shí)例表示初始化數(shù)據(jù)
      
          //新增模式,通過defaultData來獲取初始值
          if (mode == 1) {
              var defaultData = opts.defaultData;
              //如果defaultData是一個(gè)字符串,表示它需要從后臺加載
              if (typeof(defaultData) == 'string') {
                  return {
                      valid: true,
                      ajax: true,
                      data: Ajax.get(defaultData)
                  };
              } else {
                  return {
                      valid: true,
                      ajax: false,
                      data: defaultData
                  };
              }
          } else {
              //非新增模式,通過queryUrl來獲取初始值
              //此模式下必須跟后臺傳遞keyName跟key值
              //否則后臺無法查詢到要編輯的數(shù)據(jù)給前端返回
              var url = $.trim(opts.queryUrl),
                  keyName = $.trim(opts.keyName),
                  key = $.trim(opts.key);
      
              if (!url || !key || !keyName) {
                  //url key keyName 為falsy值時(shí)均返回valid:false,表示初始值無效
                  return {
                      valid: false
                  };
              } else {
                  var params = {};
                  params[keyName] = key;
                  return {
                      valid: true,
                      ajax: true,
                      data: Ajax.get(url, params)
                  }
              }
          }
      }

      2) 利用$.Deffered將同步的功能變成異步的功能,但是由于異步的功能里面調(diào)用異步回調(diào)傳遞的參數(shù)是$.ajax返回的對象,所以調(diào)用resolve方法時(shí)也必須傳遞同樣的格式,才能保證功能的健壯:

      //設(shè)置初始值
      //方法名起的不好...
      //因?yàn)檫@是一個(gè)帶返回值的設(shè)置型函數(shù)
      //而且為了將初始化數(shù)據(jù)的設(shè)置統(tǒng)一成異步任務(wù)
      //用到了$.Deferred()
      setInitData: function () {
          var $defer = $.Deferred(),
              initData = this.getInitData(),
              opts = this.options;
      
          if (!initData.valid) {
              setTimeout(function () {
                  $defer.resolve({});
              }, 0);
          } else if (initData.ajax) {
              initData.data.done(function (res) {
                  var data = opts.parseInitResponse(res);
                  $defer.resolve(data);
              }).fail(function () {
                  $defer.resolve({});
              });
          } else {
              setTimeout(function () {
                  $defer.resolve(initData.data);
              }, 0);
          }
      
          return $.when($defer);
      }

      3)將表單保存的接口分成兩個(gè),一個(gè)postUrl用于新增模式新增,一個(gè)putUrl用于編輯模式新增,結(jié)合mode參數(shù),通過下面的接口來返回實(shí)際發(fā)起ajax請求時(shí)的地址:

      //獲取表單保存時(shí)調(diào)用的接口地址
      getSaveUrl: function () {
          var url = '',
              opts = this.options,
              mode = this.mode;
      
          //mode=1時(shí)用postUrl
          if (opts.postUrl && mode == 1) {
              url = opts.postUrl;
          }
      
          //mode!=1時(shí)用putUrl
          if (opts.putUrl && mode != 1) {
              url = opts.putUrl;
          }
      
          return url;
      }

      4)保存的方法返回的時(shí)候直接返回$.ajax創(chuàng)建的對象,不考慮對外部提供任何的保存后的回調(diào),目的就是為了讓保存方法更加簡單和單一:

      //表單保存邏輯
      save: function () {
          if (this.mode > 2) return false;
      
          var opts = this.options,
              formData = this.getData(),
              event;
      
          //觸發(fā)beforeSave事件
          this.trigger((event = $.Event('beforeSave')), formData);
      
          //方便外部對formData進(jìn)行一些額外的處理
          formData = opts.parseSubmitData(formData);
      
          //如果beforeSave事件默認(rèn)行為被阻止,則直接返回
          if (event.isDefaultPrevented()) {
              return false;
          }
      
          var url = this.getSaveUrl();
      
          //發(fā)ajax請求保存,同時(shí)把Ajax組件創(chuàng)建的實(shí)例返回
          //方便外部根據(jù)實(shí)際情況添加自己的回調(diào)
          return Ajax[opts.ajaxMethod](url, formData);
      }

      5)通過parseSubmitData回調(diào)來解決以何種結(jié)構(gòu)傳遞數(shù)據(jù)到后臺的問題,在上面的save方法的代碼中,發(fā)起ajax請求前有一個(gè)調(diào)用parseSubmitData的代碼,這個(gè)回調(diào)需返回一個(gè)有效的對象作為實(shí)際要傳遞的數(shù)據(jù)。formData在調(diào)用這個(gè)回調(diào)前是一個(gè)object實(shí)例,假如后臺是php,我們可以把parseSubmitData定義成:

      function (data) {
          var hasOwn = Object.prototype.hasOwnProperty;
      
          var ret = {};
          for (var i  in data) {
              if (hasOwn.call(data, i)) {
                  ret['User[' + i + ']'] = data[i];
              }
          }
      
          return ret;
      }

      假如formData默認(rèn)傳遞時(shí)是這樣的結(jié)構(gòu):

      image

      調(diào)用parseSubmitData后將會(huì)是這樣的結(jié)構(gòu):

      image

      從我自身的經(jīng)驗(yàn)來說,一個(gè)表單管理的整體組件能夠把以上問題解決,基本上功能就夠了,因?yàn)楸韱喂芾淼墓δ鼙旧硎潜容^單一的,當(dāng)我想要往這個(gè)組件添加功能的時(shí)候,我總是想兩個(gè)問題:

      1. 是否違背單一原則,加?xùn)|西會(huì)不會(huì)讓這個(gè)組件今后的改動(dòng)更不穩(wěn)定

      2. 是否能夠添加新的組件來解決要添加的功能。

      比如說,我原來想把表單校驗(yàn)的功能集成到form.js里面,后面我就發(fā)現(xiàn)這是個(gè)明顯地違背單一原則的決定,最后將表單校驗(yàn)的功能單獨(dú)出來形成了一個(gè)新的組件,這樣做最大的好處就是兩邊的功能沒有任何關(guān)聯(lián)影響;而且分開之后,從代碼印象上都感覺代碼質(zhì)量跟原來明顯不同。

      form.js的整體功能

      首先它定義的option如下:

      var DEFAULTS = {
          mode: 1, //同F(xiàn)ormFieldBase的mod
          postUrl: '',//編輯時(shí)保存的url
          putUrl: '',//新增時(shí)保存的url
          queryUrl: '',//編輯模式時(shí)查詢初始值的url
          key: '',//編輯模式時(shí)使用它作為主鍵的值,跟在queryUrl后面?zhèn)鬟f到后臺查詢數(shù)據(jù)
          keyName: '',//編輯模式時(shí)使用它作為主鍵的名稱,跟在queryUrl后面?zhèn)鬟f到后臺查詢數(shù)據(jù)
          defaultData: {},//新增模式時(shí)的默認(rèn)值,可以是一個(gè)object,也可以是一個(gè)字符串,是字符串的時(shí)候表示一個(gè)后臺查詢的接口地址
          ajaxMethod: 'post',//發(fā)ajax請求的時(shí)候用的方法
          fieldOptions: {},//各個(gè)字段的選項(xiàng)
          parseData: $.noop,//獲取初始化數(shù)據(jù)時(shí),通過這個(gè)回調(diào)來解析初始化數(shù)據(jù)
          parseSubmitData: function (data) {
              //保存提交數(shù)據(jù)到后臺之前,可以通過這個(gè)回調(diào)對要提交的數(shù)據(jù)做些額外的處理
              return data;
          },
          parseInitResponse: function (res) {
              //使用這個(gè)回調(diào)來解析獲取初始化數(shù)據(jù)時(shí)ajax返回的響應(yīng)
              if (res.code == 200) {
                  return res.data;
              } else {
                  return {};
              }
          },
          onInit: $.noop,//表單初始化完成后的事件回調(diào)
          onBeforeSave: $.noop//表單保存接口調(diào)用前觸發(fā)的回調(diào)
      };

      要說明的是form.js內(nèi)部使用了formMap組件來管理表單元素的實(shí)例,所以以上option中的mode跟fieldOptions的用法跟上文中的一模一樣。

      form.js提供了以下api方法在實(shí)際工作中可以經(jīng)常用到:

      getMode(): 返回表單的模式:1 2 3

      getData(): 獲取表單數(shù)據(jù)

      reset(): 表單重置

      save(): 保存。

      在源碼中有一部分可能還需要解釋一下:

      //設(shè)置初始值
      this.setInitData().always(function (data) {
          opts.parseData(data);
      
          var fields = {};
          for (var i in data) {
              if (hasOwn.call(data, i)) {
                  fields[i] = '';
              }
          }
      
          for (var i in opts.fieldOptions) {
              if (hasOwn.call(opts.fieldOptions, i)) {
                  fields[i] = '';
              }
          }
      
          //解析字段的初始值
          var fieldOptions = {};
          for (var i in fields) {
              if (hasOwn.call(fields, i)) {
                  fieldOptions[i] = (i in opts.fieldOptions) && opts.fieldOptions[i] || {};
                  (i in data ) && (fieldOptions[i][that.mode == 1 ? 'defaultValue' : 'value'] = data[i]);
              }
          }
      
          //初始值可能是異步獲取的,所以必須在初始數(shù)據(jù)獲取完畢之后再初始化formMap組件
          that.formMap = new FormMap($element, {
              mode: that.mode,
              fieldOptions: fieldOptions
          });
      
          //告訴外部初始化完成
          that.trigger('formInit');
      });

      1)注意always這個(gè)方法使用,跟前面介紹的setInitData()的返回值有關(guān)系;

      2)以上代碼中的三個(gè)循環(huán),前面2個(gè)是為了找出fieldOptions和initData中所有的字段,第三個(gè)是為了將字段的option跟initData中的值合并起來,以便最后實(shí)例化formMap的時(shí)候,直接把fieldOptions傳遞進(jìn)去,這樣里面的每個(gè)表單元素組件在實(shí)例化的時(shí)候就能得到外部表單組件獲取的初始值。

      3)還有一種做法:不在表單獲取完initData后再去初始化formMap,而是之前就初始化好,然后當(dāng)initData獲取完以后再通過formMap的setData方法來設(shè)置初始值,這樣有兩個(gè)問題:
      a. formMap提前初始化,各個(gè)表單元素組件的初始值都是空的,當(dāng)form調(diào)用reset的時(shí)候,不會(huì)重置成form獲取的值,而是reset成空值;
      b. setData方法如果管理不好,會(huì)導(dǎo)致在初始化調(diào)用的時(shí)候觸發(fā)各個(gè)表單元素實(shí)例的change事件,這對于初始化過程來說,是不應(yīng)該的,因?yàn)槟莻€(gè)時(shí)候的change事件不符合語義。

      form.js的注意事項(xiàng)

      form.js的使用方式可參考demo中的demo2.js。

      http://liuyunzhuge.github.io/blog/form/dist/js/app/demo2.js

      由于formMap的初始化以及form的init事件觸發(fā)都是異步的,所以如果外部有些邏輯依賴formMap的話,要考慮把那些邏輯放到form的onInit事件回調(diào)里面去做,否則即使不報(bào)undefined錯(cuò)誤,也達(dá)不到想要的功能。

      本文小結(jié)

      本文提供了一個(gè)代碼跟功能都很簡單的表單組件,它跟上文的那些組件一起,構(gòu)成了我自己在工作中做表單開發(fā)的全部內(nèi)容,由于它們跟我自身的開發(fā)經(jīng)驗(yàn)有很大的關(guān)系,所以我也不敢保證這些東西對每個(gè)人來說都一定是好用的,但是至少啟發(fā)作用還是有的,我寫這些東西就是受曾經(jīng)公司開發(fā)平臺的啟發(fā)以及后來的項(xiàng)目實(shí)際情況的影響,也許有人看到了這些,會(huì)寫出更符合自己使用習(xí)慣的另一套組件出來,那樣的話,對自己或者對工作,都會(huì)有很大的價(jià)值。

      下一篇介紹如何自定義jquery.validation,來實(shí)現(xiàn)好看的帶tooltip的表單校驗(yàn),敬請關(guān)注:)

      posted @ 2016-05-16 10:35  流云諸葛  閱讀(3094)  評論(3)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲精品色哟哟一区二区| 亚洲性av网站| 亚洲欧美日本久久网站| 国产蜜臀精品一区二区三区| 蜜桃av一区二区高潮久久精品| 亚洲综合色成在线播放| 国产成人综合久久亚洲av| 大陆精大陆国产国语精品| 欧美日激情日韩精品嗯| 国产高颜值不卡一区二区| 精品无码国产污污污免费| 国产亚洲色婷婷久久99精品| 老色鬼永久精品网站| 亚洲av日韩av永久无码电影| 亚洲精品中文字幕无码蜜桃| 亚洲综合一区无码精品| av永久免费网站在线观看| 性色在线视频精品| 91福利一区福利二区| 乱人伦中文字幕成人网站在线| 国产精品无码无卡在线观看久| 亚洲另类激情专区小说婷婷久| 久久热这里只有精品国产| 亚洲熟女乱色一区二区三区| 午夜片神马影院福利| 国产精品国产三级国产试看| 国产精品中文字幕免费| 131mm少妇做爰视频| 久久无码中文字幕免费影院蜜桃| 国产成人无码A区在线观| 亚洲精品欧美综合二区| 无码人妻出轨黑人中文字幕| 中文字幕日韩有码第一页| 电影在线观看+伦理片| 日韩乱码人妻无码中文字幕视频| 久久se精品一区二区三区| 777奇米四色成人影视色区| 日本丰满护士bbw| 九九热视频精品在线播放| 婷婷久久香蕉五月综合加勒比| 国产黄大片在线观看画质优化|