AngularJS 做為一個以建立 Single Page App 為目標的 framework,當然是不希望你在 web app 中碰到需要與後端做資料溝通時會需要 refresh 整個網頁,因此它提供了完整的 HTML <form> 解決方案,諸如:
- 透過 form directive 攔截預設的 form submission
- 多樣化而且可以自訂的 validation 功能
- 提供 ng-submit 以及 [type=“submit”] 上的 ng-click 作為真正送出 form 時候的程式動作
問題
然而,我們不時的還是會碰到真正需要 page refresh 的 form submission
- 該功能還沒有做 API 的時候(容易發生在從 backend-based 轉移成為 SPA 的時候)
- 介接第三方網站功能的時候(例如金流業者)
貼心如 AngularJS,當然也理解你的困難,於是
Angular prevents the default action (form submission to the server) unless the <form> element has an action attribute specified.
可惜天下的事情通常都不會如此順利(蛤)。
問題在於,有加上 action 屬性的 <form> 元素只有在直接被使用者操作的情況下才能夠成功送出。
於是我們還是會碰到 AngularJS natively 提供的 solution 無法解決的問題:
- 需要先與後端確認資料正確性之後再送出 - 無直接操作送出的動作
- 需要送出隱藏的 <form> - 無法透過使用者操作
過往我們怎麼做的?在 AngularJS 裡我們又該何去何從呢?
// jquery way
$('form')[0].submit()
// angular way?
// $document injectable
$document.find('form')[0].submit();
// $element injectable
$element.find('form')[0].submit();
這樣當然不是好的做法!
- Controller 裡面不要做 DOM 操作
- 如果純靠 jQLite 的 selector 能力(即不使用 jQuery),頁面上有多個 <form> 的時候使用起來是相對難以選擇到真正的目標(因為 jqLite 的 selector 只支援 tag)
directive: submit-it
/* file: submitIt.js */
angular.module('submitIt', [])
/**
* @ngdoc directive
* @description form submission trigger
*
* @param {boolean} submit-it submit form on `true`
* @param {string} si-action submit action(optional)
* @param {string} si-method submit method(optional)
*/
.directive('submitIt', [
'$timeout',
function ($timeout) {
return {
restrict: 'A',
link: function postLink(scope, iElm, iAttrs) {
var _submitWatch = scope.$watch(iAttrs.submitIt, _onSubmitWatch),
_savedAction, _savedMethod;
iAttrs.$observe('siAction', function (val) {
if (val) {
_savedAction = val;
}
});
iAttrs.$observe('siMethod', function (val) {
if (val) {
_savedMethod = val;
}
});
function _onSubmitWatch(submit) {
if (submit && iElm[0].tagName === 'FORM') {
if (_savedAction) {
iElm.attr('action', _savedAction);
}
if (_savedMethod) {
iElm.attr('method', _savedMethod);
}
$timeout(function () {
iElm[0].submit();
});
_submitWatch();
}
}
}
};
}]);
用法
submit-it
本身做為「送出 <form>」的觸發器,其值為true
時即會送出 <form>- 附加的
si-action
、si-method
則是提供了於送出 <form> 前的瞬間設定 <form> 的 action 及 method 的功能
Demo
在這個 demo 中可以看到該按鈕其實沒有擺在 <form> 裡面,並且點擊後直接對 http://www.google.com 發出了 GET
request,又由於 target
為 _top
,整頁都將被導至 http://www.google.com。