[Profile 2]
這個小節會介紹如何從前端頁面去呼叫到後端寫好的服務,以及頁面上相關的設定。相關程式路徑,請參閱上個小節的程式路徑說明。
[前端相關程式]
製作一個全新的JS日曆表,不太符合我們開發的需求,且需要一段時間去開發,這次我們採用現成的JQueryUI,它本身可以顯示日曆表,設計理念是,讓使用者去選擇日曆後,自動帶入生日欄位,相關的程式碼以及文字套件,取至於官方的JQueryUI,,以下是相關程式路徑,就不說明JQueryUI實作細節,我們知道如何使用即可。
public
└ javascripts
└ jquery-ui-1.12.1
└ LICENSE.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的加解密,JQuery的Deferred延遲物件取得後端回傳的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>