# 10.彈窗專題
  Layui沒有像Bootstrap那樣的直接寫在頁面中的模態彈窗,Layui的彈窗是通過layer模塊用js去彈出窗口,
這就意味著Layui的彈窗功能更豐富、操作更靈活,當然靈活也就意味著對于初學者來說使用更為復雜,下面介紹了實現表單彈窗的幾種不同方式。
## 10.1.第一種 頁面層彈窗 :id=type1
頁面層彈窗就是彈窗頁面和列表頁面在一個文件中,彈窗類型type=1:
```html
<button id="btnAddUser" class="layui-btn">添加</button>
<table id="tableUser" lay-filter="tableUser"></table>
<!-- 表單彈窗 -->
<script type="text/html" id="modelUser">
<form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form">
<input name="userId" type="hidden"/>
<div class="layui-form-item">
<label class="layui-form-label">用戶名</label>
<div class="layui-input-block">
<input name="nickName" placeholder="請輸入用戶名" type="text" class="layui-input" maxlength="20"
lay-verType="tips" lay-verify="required" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">性別</label>
<div class="layui-input-block">
<input type="radio" name="sex" value="男" title="男" checked/>
<input type="radio" name="sex" value="女" title="女"/>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
<button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button>
</div>
</form>
</script>
<!-- 表格操作列 -->
<script type="text/html" id="tableBarUser">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
</script>
<!-- js部分 -->
<script>
layui.use(['layer', 'form', 'table', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var admin = layui.admin;
// 渲染表格
var insTb = table.render({
elem: '#tableUser',
url: '../../json/user.json',
cols: [[
{type: 'numbers', title: '#'},
{field: 'nickName', sort: true, title: '用戶名'},
{field: 'sex', sort: true, title: '性別'},
{align: 'center', toolbar: '#tableBarUser', title: '操作', minWidth: 200}
]]
});
// 添加
$('#btnAddUser').click(function () {
showEditModel();
});
// 工具條點擊事件
table.on('tool(tableUser)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') { // 刪除
layer.msg('點擊了刪除', {icon: 2});
}
});
// 顯示表單彈窗
function showEditModel(mUser) {
admin.open({
type: 1,
title: (mUser ? '修改' : '添加') + '用戶',
content: $('#modelUser').html(),
success: function (layero, dIndex) {
var url = mUser ? '/updateUser' : '/addUser';
// 回顯數據
form.val('modelUserForm', mUser);
// 表單提交事件
form.on('submit(modelSubmitUser)', function (data) {
layer.load(2);
$.post(url, data.field, function (res) {
layer.closeAll('loading');
if (res.code == 200) {
layer.close(dIndex);
layer.msg(res.msg, {icon: 1});
insTb.reload();
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'json');
return false;
});
}
});
}
});
</script>
```
 彈窗的參數`type: 1`表示彈窗類型是頁面層類型而不是iframe層類型,`content: $("#modelUser").html()`是指定彈窗的內容,
表單使用`<script type="text/html">`而不是使用div,這樣表單頁面會默認隱藏,需要注意的是,操作表單的代碼,
包括表單的事件監聽都要寫在彈窗的`success`回調里面,因為layer彈窗是把表單的html**復制一份**動態插入到頁面上,而不是把那一段代碼變成模態窗,
所以事件綁定必須寫在success里面。
> 這種方式表單和表格在一個頁面上面,數據傳遞、頁面相互操作比較方便,不涉及到iframe父子頁面傳值的問題,推薦初學者使用這種方式
!> **切記:** 對于type=1的彈窗,所有操作彈窗內元素的代碼都要放在彈窗的success里面。
<br/>
## 10.2.第二種 iframe層彈窗 :id=type2
使用iframe類型的彈窗可以讓表單頁面和表格頁面分開,邏輯更清晰。
彈窗頁面,userForm.html:
```html
<html>
<head>
<link rel="stylesheet" href="../../assets/libs/layui/css/layui.css"/>
<link rel="stylesheet" href="../../assets/module/admin.css"/>
</head>
<body>
<form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form">
<input name="userId" type="hidden"/>
<div class="layui-form-item">
<label class="layui-form-label">用戶名</label>
<div class="layui-input-block">
<input name="nickName" placeholder="請輸入用戶名" type="text" class="layui-input" maxlength="20"
lay-verType="tips" lay-verify="required" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">性別</label>
<div class="layui-input-block">
<input type="radio" name="sex" value="男" title="男" checked/>
<input type="radio" name="sex" value="女" title="女"/>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" type="button" ew-event="closeIframeDialog">取消</button>
<button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button>
</div>
</form>
<!-- js部分 -->
<script type="text/javascript" src="../../assets/libs/layui/layui.js"></script>
<script type="text/javascript" src="../../assets/js/common.js"></script>
<script>
layui.use(['layer', 'form', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var mUser = admin.getLayerData(); // 獲取列表頁面傳遞的數據
// 回顯數據
form.val('modelUserForm', mUser);
// 表單提交事件
form.on('submit(modelSubmitUser)', function (data) {
layer.load(2);
var url = mUser ? '/updateUser' : '/addUser';
$.post(url, data.field, function (res) {
layer.closeAll('loading');
if (res.code == 200) {
layer.msg(res.msg, {icon: 1});
admin.putLayerData('formOk', true); // 設置操作成功的標識
admin.closeThisDialog(); // 關閉當前iframe彈窗
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'json');
return false;
});
});
</script>
</body>
</html>
```
表格頁面,list.html:
```html
<button id="btnAddUser" class="layui-btn">添加</button>
<table id="tableUser" lay-filter="tableUser"></table>
<!-- 表格操作列 -->
<script type="text/html" id="tableBarUser">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
</script>
<!-- js部分 -->
<script>
layui.use(['layer', 'table', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var table = layui.table;
var admin = layui.admin;
// 渲染表格
var insTb = table.render({
elem: '#tableUser',
url: '../../json/user.json',
cols: [[
{type: 'numbers', title: '#'},
{field: 'nickName', sort: true, title: '用戶名'},
{field: 'sex', sort: true, title: '性別'},
{align: 'center', toolbar: '#tableBarUser', title: '操作', minWidth: 200}
]]
});
// 添加
$('#btnAddUser').click(function () {
showEditModel();
});
// 工具條點擊事件
table.on('tool(tableUser)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') { // 刪除
layer.msg('點擊了刪除', {icon: 2});
}
});
// 顯示表單彈窗
function showEditModel(mUser) {
var layIndex = admin.open({
type: 2,
title: (mUser ? '修改' : '添加') + '用戶',
content: 'userForm.html',
data: mUser, // 使用data參數傳值給彈窗頁面
end: function () {
if (admin.getLayerData(layIndex, 'formOk')) { // 判斷表單操作成功標識
insTb.reload(); // 成功刷新表格
}
}
});
}
});
</script>
```
 彈窗參數`type: 2`就表示是一個iframe類型的彈窗,content參數寫表單的頁面url,
然后在end回調里面判斷操作成功的標識然后刷新表格,注意是end而不是success,這樣做的好處就是,
彈窗頁面跟表格獨立開了,大大減少了每個頁面的代碼量,將業務邏輯分開,壞處就是,
如果頁面有下拉框、日期選擇控件等元素,它們的范圍不能超出彈窗的范圍,會導致彈窗出現滾動條,甚至不顯示出來。
 那么有沒有辦法既能讓表單頁面獨立,又能讓表單里面的下拉框、日期等組件能夠超出彈窗的范圍呢,請看第三種方式。
<br/>
## 10.3.第三種 url方式彈窗 :id=type3
url方式彈窗就是使用url參數將彈窗頁面獨立出來:
彈窗頁面,userForm.html:
```html
<form id="modelUserForm" lay-filter="modelUserForm" class="layui-form model-form">
<input name="userId" type="hidden"/>
<div class="layui-form-item">
<label class="layui-form-label">用戶名</label>
<div class="layui-input-block">
<input name="nickName" placeholder="請輸入用戶名" type="text" class="layui-input" maxlength="20"
lay-verType="tips" lay-verify="required" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">性別</label>
<div class="layui-input-block">
<input type="radio" name="sex" value="男" title="男" checked/>
<input type="radio" name="sex" value="女" title="女"/>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
<button class="layui-btn" lay-filter="modelSubmitUser" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
var mUser = admin.getLayerData('#modelUserForm'); // 列表頁面傳遞的數據,#modelUserForm這個只要寫彈窗內任意一個元素的id即可
// 回顯數據
form.val('modelUserForm', mUser);
// 表單提交事件
form.on('submit(modelSubmitUser)', function (data) {
layer.load(2);
var url = mUser ? '/updateUser' : '/addUser';
$.post(url, data.field, function (res) {
layer.closeAll('loading');
if (res.code == 200) {
layer.msg(res.msg, {icon: 1});
admin.putLayerData('formOk', true, '#modelUserForm'); // 設置操作成功的標識,#modelUserForm這個只要寫彈窗內任意一個元素的id即可
admin.closeDialog('#modelUserForm'); // 關閉頁面層彈窗
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'json');
return false;
});
});
</script>
```
 注意這里,頁面不需要寫`<html><body>`這些東西,它是一個html片段,不是完整的html頁面。
表格頁面,list.html:
```html
<button id="btnAddUser" class="layui-btn">添加</button>
<table id="tableUser" lay-filter="tableUser"></table>
<!-- 表格操作列 -->
<script type="text/html" id="tableBarUser">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">修改</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
</script>
<!-- js部分 -->
<script>
layui.use(['layer', table', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var table = layui.table;
var admin = layui.admin;
// 渲染表格
var insTb = table.render({
elem: '#tableUser',
url: '../../json/user.json',
cols: [[
{type: 'numbers', title: '#'},
{field: 'nickName', sort: true, title: '用戶名'},
{field: 'sex', sort: true, title: '性別'},
{align: 'center', toolbar: '#tableBarUser', title: '操作', minWidth: 200}
]]
});
// 添加
$('#btnAddUser').click(function () {
showEditModel();
});
// 工具條點擊事件
table.on('tool(tableUser)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') { // 刪除
layer.msg('點擊了刪除', {icon: 2});
}
});
// 顯示表單彈窗
function showEditModel(mUser) {
var layIndex = admin.open({
title: (mUser ? '修改' : '添加') + '用戶',
url: 'userForm.html',
data: mUser, // 傳遞數據到表單頁面
end: function () {
if (admin.getLayerData(layIndex, 'formOk')) { // 判斷表單操作成功標識
insTb.reload(); // 成功刷新表格
}
},
success: function (layero, dIndex) {
// 彈窗超出范圍不出現滾動條
$(layero).children('.layui-layer-content').css('overflow', 'visible');
}
});
}
});
</script>
```
 這里與iframe彈窗的區別是,使用`url`參數指定表單的頁面鏈接,而不是使用content,url這個參數是admin.open擴展的,layer不具備,
這樣做會把表單頁面的html內容使用ajax加載到彈窗中,而不是iframe嵌入,這樣表單里面的下拉框、日期等組件就可以超出彈窗的范圍,不會導致彈窗出現滾動條。
!> 注意:表單頁面是html片段,不是完整的html,這點不要忘記了,另外表單頁面和表格頁面不要出現重復的id,因為最終是在一個頁面上
## 10.4.四種方式選擇指南 :id=choose
方式 | 推薦 | 理由
:--- | :--- | :---
第一、四種 | 建議初學者使用這種 | 不涉及兩個頁面傳值問題
第二種 | 推薦表單跟表格無交互使用,比如詳情 | 下拉框、日期不能超出彈窗
第三種 | 表單彈窗、頁面有交互建議使用 | 頁面傳值方便,不存在問題
<br/>
## 10.5.admin.modelForm方法 :id=model_form
此方法是把layer彈窗自帶的確定按鈕綁定成表單的提交按鈕。
```html
<!-- 表單彈窗 -->
<script type="text/html" id="modelUser">
<div class="layui-form-item">
<label class="layui-form-label">用戶名</label>
<div class="layui-input-block">
<input name="nickName" placeholder="請輸入用戶名" type="text" class="layui-input" maxlength="20"
lay-verType="tips" lay-verify="required" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">性別</label>
<div class="layui-input-block">
<input type="radio" name="sex" value="男" title="男" checked/>
<input type="radio" name="sex" value="女" title="女"/>
</div>
</div>
</script>
<!-- js部分 -->
<script>
layui.use(['layer', 'form', admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var admin = layui.admin;
admin.open({
type: 1,
title: '添加用戶',
btn: ['確定', '取消'],
content: $('#modelUser').html(),
success: function (layero, dIndex) {
// 把確定按鈕綁定表單提交,參數二是給按鈕起一個lay-filter,參數三是給表單起一個lay-filter
admin.modelForm(layero, 'demoFormSubmit', 'demoForm');
// 給表單賦值
form.val('demoForm', {nickName: '張三', sex: '男'});
// 監聽表單提交
form.on('submit(demoFormSubmit)', function (data) {
layer.msg(JSON.stringify(data.field));
return false;
});
},
yes: function () {
// 確定按鈕方法什么都不要操作
}
});
});
</script>
```
 admin.modelForm()這個方法會把彈窗外面包一個form,然后把確定按鈕加lay-submit,所以你的表單頁面不需要寫form和確定、關閉按鈕,只需要寫表單項。
 這個方法的使用場景為你想要表單的按鈕固定,只滾動表單內容部分,可以用這個操作,
當然那種自己寫確定和取消按鈕的表單彈窗的寫法也是支持固定按鈕的,
前面css組件樣式中有介紹。
## 10.6.參數傳遞方法詳解 :id=model_param
**參數傳遞:**
```javascript
admin.open({
type: 2,
content: 'userForm.html',
data: {
name: '李白',
sex: '男'
}
});
```
通過data屬性進行參數傳遞,data同樣是admin.open擴展的,layer不具備。
**獲取參數:**
方法 | 說明 | 參數
:--- | :--- | :---
admin.getLayerData(index) | 獲取某彈窗的全部參數 | layer的index
admin.getLayerData(index, key) | 參數某彈窗參數的某個字段 | index,字段
admin.getLayerData() | iframe彈窗子頁面獲取參數 | 無任何參數
admin.getLayerData('#xxForm') | url方式彈窗子頁面獲取參數 | 彈窗內任意元素id
 如果是在iframe彈窗的子頁面中可以使用admin.getLayerData()直接獲取父頁面傳遞的全部參數,
如果是在url方式打開的彈窗中可以使用admin.getLayerData('#xx')直接獲取父頁面的全部參數,'#xx'是彈窗內任意元素的id。
**增加參數:**
方法 | 說明 | 參數
:--- | :--- | :---
admin.putLayerData(key, value, index) | 增加參數 | 字段名,值,index
admin.putLayerData(key, value) | iframe彈窗子頁面增加參數 | 字段名,值
admin.putLayerData(key, value, '#xx') | url方式彈窗子頁面增加參數 | 彈窗內任意元素id
!> **注意:**data參數必須是對象的形式,data:1、data:'aa'這種寫法會導致無法put新參數。
<br/>
## 10.7.第四種 捕獲層彈窗
 第一種頁面層彈窗由于所有關于彈窗內元素的操作代碼都要寫在彈窗的success里面,
大部分人不適應這種方式,所以介紹第四種捕獲層彈窗:
```html
<!-- 表單彈窗 -->
<form id="modelRoleForm" lay-filter="modelRoleForm" class="layui-form model-form" style="display: none;">
<input name="roleId" type="hidden"/>
<div class="layui-form-item">
<label class="layui-form-label">角色名</label>
<div class="layui-input-block">
<input name="roleName" placeholder="請輸入角色名" type="text" class="layui-input"
lay-verType="tips" lay-verify="required" required/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">備注</label>
<div class="layui-input-block">
<textarea name="comments" placeholder="請輸入內容" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
<button class="layui-btn" lay-filter="modelSubmitRole" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'table', 'admin'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var admin = layui.admin;
var formUrl;
// 渲染表格
var insTb = table.render({...});
// 添加
$('#btnAddRole').click(function () {
showEditModel();
});
// 表格工具條點擊事件
table.on('tool(tableRole)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
}
});
// 顯示編輯彈窗
function showEditModel(mRole) {
// 如果是單標簽版本可以使用下面代碼把表單移到body中
/* if ($('#modelRoleForm').parent('body').length == 0) {
$('body').append($('#modelRoleForm'));
} */
$('#modelRoleForm')[0].reset(); // 重置表單
form.val('modelRoleForm', mRole); // 回顯數據
formUrl = mRole ? 'role/update' : 'role/add';
admin.open({
type: 1,
title: (mRole ? '修改' : '添加') + '角色',
content: $('#modelRoleForm')
});
}
// 表單提交事件
form.on('submit(modelSubmitRole)', function (data) {
layer.load(2);
$.post(formUrl, data.field, function (res) {
layer.closeAll('loading');
if (res.code == 200) {
admin.closeDialog('#modelRoleForm');
layer.msg(res.msg, {icon: 1});
insTb.reload();
} else {
layer.msg(res.msg, {icon: 2});
}
}, 'json');
return false;
});
});
</script>
```
 與第一種的區別是form不用`<script>`包裹,加`style="display:none"`隱藏,
admin.open的content是$('#roleForm')而不是$('#roleForm').html(),
表單的提交事件可以直接寫在外面,而不用寫在彈窗的success里面。
!> 捕獲層的弊端就是彈窗的頁面代碼最好是寫在body下面,不然樣式會被其他影響,所以spa版本不建議使用這種方式,單標簽版、iframe版本可以使用。