[Profile 2]

這個小節會介紹如何從前端頁面去呼叫到後端寫好的服務,以及頁面上相關的設定。相關程式路徑,請參閱上個小節的程式路徑說明。


[前端相關程式]

製作一個全新的JS日曆表,不太符合我們開發的需求,且需要一段時間去開發,這次我們採用現成的JQueryUI,它本身可以顯示日曆表,設計理念是,讓使用者去選擇日曆後,自動帶入生日欄位,相關的程式碼以及文字套件,取至於官方的JQueryUI,,以下是相關程式路徑,就不說明JQueryUI實作細節,我們知道如何使用即可。

public
 └ javascripts
   └ jquery-ui-1.12.1LICENSE.txt                   <--- jqeury ui說明
        datepicker-zh-TW.js           <--- jqeury Ui繁體中文js
        jquery-1.12.4.js              <--- jquery ui需要的js
        jquery-ui.css                 <--- jquery ui主要樣式表
        jquery-ui.js                  <--- jquery ui主要js
        package.json                  <--- jquery ui主要的檔案相依性
        images
         └ calendar.gif               <--- 日期選擇器的icon圖 
           ui-icons_444444_256x240.png <--- 日期選擇器相關圖
           ui-icons_555555_256x240.png 
           ui-icons_777620_256x240.png 
           ui-icons_777777_256x240.png 
           ui-icons_cc0000_256x240.png 
           ui-icons_ffffff_256x240.png

public.javascripts.webUtils.js

這個JS用途是撰寫一些公用URL混合BASE64的加解密,JQueryDeferred延遲物件取得後端回傳的JSON資料,以及日曆表的一些初始設定,都集中寫在這個JS裡,若往後有其他頁面要使用時,直接引用這個JS就可以了。

// 傳入字串,會進行URL與Base64 encode
function urlBase64Encode(rawStr){
    return btoa(encodeURIComponent(rawStr));
}

// 傳入加密的Base64+URL encode字串,進行解密動作
function urlBase64Decode(encodeStr){
    return decodeURIComponent(atob(encodeStr));
}

// 傳入url 並使用 Deferred延遲物件,等候伺服器回傳JSON資料
function getCheckResultData(checkUrl){
    var result = $.Deferred();
    $.getJSON(checkUrl).done(function(data){
        result.resolve(data);
    });
    return result;
}

// 初始化頁面日期選擇元件
function initDatePicker(datepicker , imgUrl ){

    $(datepicker).datepicker({
          showOn: "button",
          buttonImage: imgUrl,
          buttonImageOnly: true,
          buttonText: "Select date",
          changeMonth: true,
          changeYear: true ,  
          onSelect: function () {
              // 去觸發生日驗證
              $(datepicker).trigger("blur");
          }
    });
    // 設定中文顯示
    $.datepicker.setDefaults( $.datepicker.regional[ "zh-TW" ] );
}

public.javascripts.profileUtils.js

這個JS主要用途有三個,第一個是會員進來後讀取會員資料。第二個是針對欄位輸入完畢之後,ajax呼叫我們後端寫好的服務,檢驗欄位是否可以通過。第三個是使用者填寫後,送出要修改的會員明細資料,進行更新member_main更新member_detail更新或寫入。

// init 讀取會員明細資料
function initLoadMemberProfile(headerPicLink , headerPicPreview , datepicker , username , nickname , cellphone , systemMessage , url){
    var result = getCheckResultData(url);
    result.promise().then(initEven);

    function initEven(data) {
        $(systemMessage).html('');
        if (data.editable === false) {
            $(systemMessage).css("color", "red");
            $(systemMessage).append(data.systemMessage);
            return ;
        } 
        if(data.headerPicLink != ''){
              $(headerPicPreview).attr("src" , data.headerPicLink);
            $(headerPicPreview).fadeIn();
        }
        $(headerPicLink).val(data.headerPicLink);
        $(datepicker).val(data.birthday)
        $(username).val(data.username);
        $(nickname).val(data.nickname);
        $(cellphone).val(data.cellphone);
    }
}

// 傳入欄位Id , 驗證資訊Id與檢查的url,進行資料檢查
function inputBlurHandler(inputName , verifyMessage, ajaxUrl){
    $(inputName).blur("change paste keyup", function() {
        var checkUrl = ajaxUrl + urlBase64Encode($(inputName).val());
        var result = getCheckResultData(checkUrl);
        result.promise().then(ajaxMessageEven);
    });

    var selectorname = $(verifyMessage);

    function ajaxMessageEven(data) {
        selectorname.html('');
        selectorname.append(data.statusDesc);
        if (data.pass === false) {
            selectorname.css("color", "red");
        } else {
            selectorname.css("color", "green");
        }
    }
}

// 傳入欄位Id,預覽圖片Id,驗證資訊Id與檢查的url,進行資料檢查
function imgInputBlurHandler(inputName , preImgName , verifyMessage, ajaxUrl){
    $(inputName).blur("change paste keyup", function() {
        var checkUrl = ajaxUrl + urlBase64Encode($(inputName).val());
        var result = getCheckResultData(checkUrl);
        result.promise().then(ajaxMessageEven);
    });

    var selectorname = $(verifyMessage);

    function ajaxMessageEven(data) {
        selectorname.html('');
        selectorname.append(data.statusDesc);
        // 若失敗,預覽圖不會顯示
        if (data.pass === false) {
            selectorname.css("color", "red");
            $(preImgName).hide();
        } else {
            // 圖片網址正確,會顯示預覽圖
            selectorname.css("color", "green");
            if($(inputName).val()!=''){
                  $(preImgName).attr("src" , $(inputName).val());
                  $(preImgName).show();
            } else {
                $(preImgName).hide();
            }

        }
    }
}


// 修改表單驗證與更新
function profileHandler(editProfile , systemMessage , headerPicPreview , VerifyMessage){
  var frm = $(editProfile);
  frm.submit(function (ev) {
      $.ajax({
          type: frm.attr('method'),
          url : frm.attr('action'),
          data: frm.serialize(),
          success: function (data) {

              // 系統訊息
              formSystemMessageEven( $(systemMessage) , data);

              // 欄位檢查結果
              var keys = Object.keys(data.verificResults);
              for(var i = 0 ; i < keys.length ; i++){
                  var info = data.verificResults[keys[i]];
                  var selectorname = $('#'+info.inputName + VerifyMessage);
                  if('headerPicLink' === info.inputName){
                      formImgMessageEven('#'+info.inputName , headerPicPreview , selectorname , info);
                  } else {
                      formMessageEven(selectorname, info);
                  }
              }
          } , 
            error : function(){
              $(systemMessage).html('系統忙碌中,請稍候再嘗試,謝謝。'); 
              $(systemMessage).css("color", "red");
            }
      });

      ev.preventDefault();
  });
}

// 表單系統訊息提示
function formSystemMessageEven(selectorname , data){
    selectorname.html('');
    selectorname.append(data.statusDesc)
    if (data.update === false) {
        selectorname.css("color", "red");
    } else {
        selectorname.css("color", "green");
    }
}

// 表單欄位驗證提示
function formMessageEven( selectorname , data) {
    selectorname.html('');
    if (data.pass === false) {
        selectorname.css("color", "red");
        selectorname.append(data.statusDesc);
    } else {
        selectorname.css("color", "green");
    }
}

// 表單欄位預覽圖圖示
function formImgMessageEven(imgInputName , preImgName , selectorname , data) {
    selectorname.html('');
    // 若失敗,預覽圖不會顯示
    if (data.pass === false) {
        selectorname.css("color", "red");
        selectorname.append(data.statusDesc);
        $(preImgName).hide();
    } else {
        // 圖片網址正確,會顯示預覽圖
        selectorname.css("color", "green");
        if($(imgInputName).val()!=''){
              $(preImgName).attr("src" , $(imgInputName).val());
              $(preImgName).show();
        } else {
            $(preImgName).hide();
        }

    }
}

app.views.web.headerJqueryuiLibs.scala.html

這個網頁是用來引用JQueryUI的內容,獨立成一個頁面去使用。

<!-- Jquery ui lib-->
<link rel="stylesheet" href='@routes.Assets.versioned("javascripts/jquery-ui-1.12.1/jquery-ui.css")'>
<script src='@routes.Assets.versioned("javascripts/jquery-ui-1.12.1/jquery-1.12.4.js")'></script>
<script src='@routes.Assets.versioned("javascripts/jquery-ui-1.12.1/jquery-ui.js")'></script>
<script src='@routes.Assets.versioned("javascripts/jquery-ui-1.12.1/datepicker-zh-TW.js")'></script>

app.views.web.headerNav.scala.html

我們新增了編輯會員個人資料功能,我們需要調整原本menu,新增功能連結。

...
    <ul>
        <li><a href="@controllers.routes.WebController.login.url">登入</a></li>
        @if(utilsSession.isClinetHaveCookie(request)){
            <li><a href="@controllers.routes.WebController.editPassword.url">修改密碼</a></li>
            <li><a href="@controllers.routes.WebController.editEmail.url">修改信箱</a></li>
            <li><a href="@controllers.routes.WebController.editProfile.url">編輯個人資料</a></li>
        }
        <li><a href="@controllers.routes.WebController.logout.url">登出</a></li>
    </ul>
...

app.views.web.loginSignup.editProfile.scala.html

前面的前端程式都已經準備完畢,接下來就是設計編輯會員主要頁面,裡面會有五個欄位資訊需要填寫。前面特別可以注意到我特別寫成document ready去引用相關JS,這個寫法是說明等候網頁的基本element載入完畢之後,才會去引用相關JS,去偵測使用者的輸入與送出,以下就是我們頁面的程式。

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>編輯個人資料</title>

    @views.html.web.headerLibs()

    @views.html.web.loginSignup.loginSignupLibs()

    @views.html.web.headerJqueryuiLibs()

    <style>    
    #headerPicPreview {
       max-height: 300px;
          width: 250px;
       width: 50%;
       height: 50%;
    }
    </style>


    <script type ="application/javascript">
        $( document ).ready(function() {
            // 清除表單
            document.getElementById("editProfile").reset();

            // 一開始不顯示頭像
            $('#headerPicPreview').css('display','none');

            // 讀取登入與註冊JS
            $.getScript('@routes.Assets.versioned("javascripts/loginSignup.js")');

            // 讀取webUtils.js
            $.getScript('@routes.Assets.versioned("javascripts/webUtils.js")', function(){
                // 日曆表初始化
                initDatePicker('#birthday' , '@routes.Assets.versioned("javascripts/jquery-ui-1.12.1/images/calendar.gif")');

                // 讀取profileUtils.js
                $.getScript('@routes.Assets.versioned("javascripts/profileUtils.js")', function(){

                    initLoadMemberProfile("#headerPicLink" , "#headerPicPreview" , "#birthday" , '#username' , '#nickname' , '#cellphone' , '#systemMessage',
                                          "http://@request.host" + "@controllers.routes.WebController.ajaxLoadMemberProfile.url");

                    // 圖片觸發事件
                    imgInputBlurHandler("#headerPicLink" , "#headerPicPreview" , "#headerPicLinkVerifyMessage" , 
                                        "http://@request.host" + "@controllers.routes.WebController.ajaxCheckHeaderPicLink.url?headerPicLink="); 

                    // 生日觸發事件
                    inputBlurHandler("#birthday" , "#birthdayVerifyMessage" , 
                                      "http://@request.host" + "@controllers.routes.WebController.ajaxCheckBirthday.url?birthday="); 

                    // 使用者名稱觸發事件
                    inputBlurHandler("#username" , "#usernameVerifyMessage" , 
                                     "http://@request.host" + "@controllers.routes.WebController.ajaxCheckUsername.url?username="); 

                    // 匿稱觸發事件
                    inputBlurHandler("#nickname" , "#nicknameVerifyMessage" , 
                                     "http://@request.host" + "@controllers.routes.WebController.ajaxCheckNickname.url?nickname="); 

                    // 手機號碼觸發事件
                    inputBlurHandler("#cellphone" , "#cellphoneVerifyMessage" , 
                                     "http://@request.host" + "@controllers.routes.WebController.ajaxCheckCellphone.url?cellphone="); 

                    // 表單驗證
                    profileHandler('#editProfile','#systemMessage','#headerPicPreview','VerifyMessage');

                });

            });    

        });        

    </script>

  </head>

  <body>
    <div id="page-wrapper">
        <div id="select_nav_user">@views.html.web.headerNav()</div>
    </div>
    <div class="form">

      <ul class="tab-group">
        <li class="tab active"><a>編輯個人資料</a></li>
      </ul>
      <div class="tab-content">
        <div id="Form" > 
          <form action="@controllers.routes.WebController.doEditProfile.url" method="post" id="editProfile">
            <div class="field-wrap">
                <img id="headerPicPreview"/>
                <label class="lable-field-wrap">頭像照片網址</label>
                <input type="text" autocomplete="off" id="headerPicLink" name="headerPicLink"/>
                <span id="headerPicLinkVerifyMessage"></span>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">生日</label>
                <input type="text"  autocomplete="off" name="birthday" id="birthday"/>
                <span id="birthdayVerifyMessage"></span>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">使用者名稱</label>
                <input type="text" required autocomplete="off" name="username" id="username"/>
                <span id="usernameVerifyMessage"></span>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">暱稱</label>
                <input type="text" autocomplete="off" name="nickname" id="nickname"/>
                <span id="nicknameVerifyMessage"></span>
            </div>
            <div class="field-wrap">
                <label class="lable-field-wrap">手機號碼</label>
                <input type="text" autocomplete="off" name="cellphone" id="cellphone"/>
                <span id="cellphoneVerifyMessage"></span>
            </div>
            <span id="systemMessage"></span>
            <a>
                <input type="submit" value="SUBMIT" class="button button-block">
            </a>
          </form>
        </div>
        </div><!-- tab-content -->   
    </div> <!-- /form -->
    <div id="load"></div>
    <div id="titleBar"></div>
  </body>
</html>

results matching ""

    No results matching ""