modal是什么概念呢这并不是一个很高端的词。熟悉bootstrap操作的人都明白modal仅仅是一种弹窗。你可以点击这里在bootstrap官网体验各种modal的效果。当然啦如果你有兴趣而且又有能力完全可以自己集成各种弹窗。
我们知道yii2本身就很好的支持bootstrap。对于大部分人来说同bootstrap打交道那也是家常便饭。
既然如此那yii2自然也是很友好的支持modal咯答案是必然的。
下面我们以文章的栏目管理为例来看下modal的使用。开始之前我们先预览下最终的效果演示。
首先我们先创建一个简版的栏目表category包括栏目id和栏目名1
2
3
4
5CREATE TABLE `category` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '栏目ID',
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '栏目名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='栏目表';
下面我们要用gii生成model以及curd了对吧但是不乏有人访问 index.php?r=gii 会报403错误很明显这是你没给自己分配权限。有403 for bidden问题的先去分配下权限吧。记得分配完权限再添加一个菜单哦避免操作不便。
假设现在你可以正常访问 index.php?r=category/index 页面了当然我们已经经过一些不重要的整理了这一点没必要纠结你可以完全忽略保证你可以正常访问页面即可。
在我们平时开发中为了实现弹窗想一些都有哪些必要的元素呢
基本的弹窗的效果可以点击的按钮点击按钮之后弹窗内渲染的表单内容表单项如何校验值的有效性巴拉巴拉…差不多就这么些东西。
我们说了yii2目前是支持bootstrap的modal是bootstrap内置的一部分功能。所以弹窗的效果自然就不需要我们考虑了。我们需要的就是要会引用弹窗对不对
有同学要说了引用也很简单呀我从官网了解到其实就是在视图层加一段固定class的div然后让按钮绑定这个class点击的时候就有效果了。
没错是这个道理。
那就是说我们需要整一份modal的div吗官方有整理好的类通过这个类可以直接输出这段div内容到页面上不过是display:none形式的。我们看下如何输出弹窗的这段div。
在视图文件 category/index.php 内我们在页面的底部添加一段下面的代码1
2
3
4
5
6
7
8<?php
use yii\bootstrap\Modal;
Modal::begin([
'id' => 'operate-modal',
'header' => '<h4 class="modal-title"></h4>',
]);
Modal::end();
?>
我们看到这里指定了这段div的id。刷新页面右键查看源码你会发现真的有一个id=operate-modal的div。注意这个div是隐藏display:none了的哦
下面我们想办法创建一个按钮点击按钮让这个弹窗显示对不对
有人要说了既然是点击显示弹窗是不是得加一段js代码呢目前来看不需要如果你熟悉bootstrap的话你就应该明白通过给标签元素绑定一些属性也能实现点击的效果。
前面我教你们怎么创建a标签还记得吧所以接下来我们在视图文件 category/index.php 内创建一个按钮。为了调起modal这个按钮需要设置一个固定属性data-toggle=modal一个data-target属性data-target属性指向刚刚创建的modal的id “operate-modal”。1
2
3
4
5
6
7
8<?=
Html::a('创建栏目', ['create'], [
'class' => 'btn btn-success',
'id' => 'create', // 按钮的id随意
'data-toggle' => 'modal', // 固定写法
'data-target' => '#operate-modal', // 等于modal begin中设定的参数id值
])
?>
上面这段代码你可以放在GridView上面的p标签内。
现在点击“创建栏目”你能看到什么是不是一个空白的弹窗没错引用调起就是这么简单。
但是我们希望在点击创建的时候弹窗的主体内容能够展示form表单内容。
所以下面我们需要为这个按钮增加一个点击事件点击后我们通过异步去渲染表单并追加到modal的主体内。
这段js是非常简单的但是在yii2的视图界面怎么写js呢直接写一段script明显是不可以的因为jquery.js是默认加载在页面底部所以我们要想办法在页面底部jquery.js后写一段js。
通常情况下我们可以在视图文件内这样写一段js脚本来我们先写然后再解释。1
2
3
4$js = <<<JS
console.log(window);
JS;
$this->registerJs($js);
首先如果你有点php基础应该能看懂<<<的含义。
这段代码的含义就是要把$js这个变量的内容即一段script脚本注册到页面底部也就是说让这段js脚本在页面底部加载。当然这是registerJs默认的操作你也可以把js注册到页面顶部等等。
既然如此那下面我们就在视图文件 category/index.php 内写一段js脚本让“创建栏目”这个按钮去响应一个异步请求把我们的表单内容追加到modal内吧。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
use yii\helpers\Url;
// 异步请求的地址
$requestCreateUrl = Url::toRoute('create');
$js = <<<JS
// 创建操作
$('#create').on('click', function () {
$('.modal-title').html('创建栏目');
$.get('{$requestCreateUrl}',
function (data) {
// 弹窗的主题渲染页面
$('.modal-body').html(data);
}
);
});
JS;
$this->registerJs($js);
?>
以上代码写在视图文件index.php内任意部分即可。
现在让我们再次点击“创建栏目”按钮看一下modal渲染的内容。
注意我们这里异步请求的内容是 CategoryController 的 create操作输出的内容我们找到 CategoryController 的create方法看到这里使用render方法渲染的表单页面所以modal内加载的表单页面包含了布局这看起来有点别扭我们把render方法改为renderAjax方法去掉布局。
现在重新点击”创建栏目“modal内的内容是不是清爽了很多呢
提示render方法和renderAjax方法并没有什么不同render方法会把views/layouts/main.php内的头尾信息都加载并渲染这里对我们而言其实是个累赘所以我们使用renderAjax操作。
现在你是不是可以直接填写栏目名并提交保存了
等等我没填写内容就提交走了能不能给个表单验证告诉我name必填
这个问题如果是yii2内置的validate可以很简单的解决。只需要在 Category 模型的rules方法中添加一个required即可如下1
2
3
4
5
6
7public function rules()
{
return [
[['name'], 'required'],
[['name'], 'string', 'max' => 20],
];
}
下面我们来看一下栏目的更新操作。
栏目更新
栏目更新操作跟我们刚刚的操作并无太大差异无非一点不同的是”创建按钮”是我们自己创建的更新的按钮是gridview生成的。其实都一样哈gridview的操作按钮我们也需要自己另外修改。
找到视图文件 category/index.php 的GridView::widget一段我们把’class’ => ‘yii\grid\ActionColumn’, 这一段修改如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28[
'class' => 'yii\grid\ActionColumn',
'template' => '{update} {delete}',
'header' => '操作',
'buttons' => [
'update' => function ($url, $model, $key) {
return Html::a("栏目信息", $url, [
'title' => '栏目信息',
// btn-update 目标class
'class' => 'btn btn-default btn-update',
// 固定写法
'data-toggle' => 'modal',
// 指向modal中begin设定的id
'data-target' => '#operate-modal',
]);
},
'delete' => function ($url, $model, $key) {
return Html::a('删除', $url, [
'title' => '删除',
'class' => 'btn btn-default',
'data' => [
'confirm' => '确定要删除么?',
'method' => 'post',
],
]);
},
],
],
为按钮增加触发事件跟创建有一点不同的是触发时需要获取gridview每一行的主键ID,利用jquery的closest方法获取即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<?php
// 更新
$requestUpdateUrl = Url::toRoute('update');
$js = <<<JS
// 更新操作
$('.btn-update').on('click', function () {
$('.modal-title').html('栏目信息');
$.get('{$requestUpdateUrl}', { id: $(this).closest('tr').data('key') },
function (data) {
$('.modal-body').html(data);
}
);
});
JS;
$this->registerJs($js);
?>
异步操作或者表单项我们在上一章节已经做好了。这里我们修改下update操作渲染视图同样不使用布局即可。1
2
3
4
5
6
7
8
9
10
11
12public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
// return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->renderAjax('update', [
'model' => $model,
]);
}
}