视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
vue2组件之select2调用的示例代码
2020-11-27 22:28:01 责编:小采
文档


目前,项目中使用了纯前端的静态项目+RESTFul接口的模式。为了更好的对数据进行操作,前端使用了vue2的mvvm功能,但是由于不是单页面应用,所以,并没有涉及到其它的如vue-route等功能,也未使用webpack等编译功能,所以,也没有使用.vue文件功能。这时候,如果用到控件,则多数从原jquery的组件中选择。

select下拉搜索选择

这次的需求调研与设计是原来做winform开发的同事,由于用惯了devexpress这个控件库,所以,对于searchlookupeditor这个控件情有独钟,所以,在设计的时候,许多地方都用到。

最初实现

最初,我使用了select2绑定select标签,设定其change事件 ,在事件中修改对应的vue的data值,同时,在vue中设定watch``data中被绑定的属性,属性值发生变化,则修改对应的dom的val,然后再触发select2的change事件。当然,这种对应关系,我在select标签上放了一个data-vuep来保存其与vue属性的对应关系,并放在全局的select2vue和dom2vue中。

//mounted中的部分代码
 select2vue = {};
 $("select").each(function (index, item) {
 var s2 = $(item).select2({
 language: "zh-CN", //设置 提示语言
 width: "100%", //设置下拉框的宽度
 theme: "classic",
 placeholder: "请选择"
 }).on("change", function (e) {
 console.log(e);
 var v = $(e.target).val();
 var p = $(e.target).attr("data-vuep");
 eval("vue_cust_busi." + p + "='" + v + "';");
 //$(e.target).find("option").attr("selected",false);
 //$(e.target).find("option[value='"+v+"']").attr("selected",true);
 });

 var p = $(item).attr("data-vuep");
 select2vue[p] = s2;
 dom2vue[p] = item;
 });
 setTimeout(function(){
 vue_cust_busi.editor.ID_CUST="3";
 vue_cust_busi.editor.NAME_CUST="*有限责任公司";
 console.log("修改");
 },10,null);


//watch中的部分代码
 "temp.P1": function (val) {
 fire(arguments.callee.name.toString(), val);

 },
//通用函数

 function fire(p, val) {
 $(dom2vue[p]).val(val);
 select2vue[p].trigger("change");
 }

//html

 <select data-vuep="editor.P1" class="form-control "> 
 <option value="" ></option> 
 <option v-for="yearOpt in yearOpts" v-bind:value="yearOpt">{{yearOpt}}</option> 
 </select>

为什么要用一个data-vuep来将数据与vue的属性关联呢,因为我发现,select2初始化了这个select标签之后,修改这个标签的值无法触发修改vue对应的v-model的属性。所以,只能用这个方法。
 最终形成的结果是:

select2到vue.editor.P1:
1.select2被选择某一项,触发其change事件。
2.select2的change事件修改vue.editor.P1的值。
3.vue.editor.P1的值被修改,触发watch,watch又引发select2的change事件,但是,select2内部监控到选择和之前的一致,所以,不再执行change事件的委托。

上面这种流程一定程度是实现了数据的双向绑定,但是,非常复杂。在后续的使用中发现,在mounted中无法为select2默认值,必须在mounted中调用setTimeout生成一个定时执行的事件来执行数据绑定操作,才会触发上述流程,达到设定触始值的效果。

使用vue指令

经过一番挣扎,觉得上面这种方式还是不行。

上述方案不好的原因如下:

1.vue事件中的代码操作了dom,这样,在生命周期上可能会出现问题,特别是后来使用了setTimeout之后,生命周期变得更加不可控制。
2.每增加一个select组件,都需要增加 html标签、watch,而且,html 标签和watch既不是传统的写法,也不是vue的写法,而是发明了一种新的东西,这破坏了开发体验。
3.维护性比较差,当想删除一个select的时候,必须要去watch里面去找与html中data-vuep相等的属性监控方法,并将其删除掉。
4.兼容性不好,本方案选择将页面所有的select全部用select2初始化了一次,使得不论是否需要的,都会被影响;其次,如果不统一初始化,那么又多出了在mounted中为每一个select写初始化代码的工作,同时,也要为每个select取一个id。

为了解决这个问题,我又找到了最初看到的那个vue使用指令和select2的整合的例子。网上有好多,我不知道版权是谁的,姑且上我最先看到的那个吧。//www.gxlcms.com/article/125654.htm

原文中的代码如下:

<!DOCTYPE html>
<html>
<head>
 <title>vue select2 封装</title>
 <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="external nofollow" rel="stylesheet" />
 <script src="https://unpkg.com/vue/dist/vue.js"></script>
 <script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
 <style type="text/css">
 .content{
 text-align: center;
 padding:50px;
 }
 .content *{
 text-align: left;
 }
 .select{
 width: 350px;
 }
 </style>
</head>
<body>
 <div class="content" id="vue-example">
 <select class="select" v-select2='options' v-model="selectValue"></select>
 <br/>
 <span>结果:{{ selectValue }}</span>
 </div>
</body>
<script type="text/javascript">
 Vue.directive('select2', {
 inserted: function (el, binding, vnode) {
 let options = binding.value || {};
 
 $(el).select2(options).on("select2:select", (e) => {
 // v-model looks for
 // - an event named "change"
 // - a value with property path "$event.target.value"
 el.dispatchEvent(new Event('change', { target: e.target })); //说好的双向绑定,竟然不安套路
 });
 },
 update: function(el, binding, vnode) {
 $(el).trigger("change");
 }
 });
 
 var vueApp = new Vue({
 el: "#vue-example",
 data: {
 selectValue: '你还没有选值',
 options: {
 data: [
 { id: 0, text: 'enhancement' },
 { id: 1, text: 'bug' },
 { id: 2, text: 'duplicate' },
 { id: 3, text: 'invalid' },
 { id: 4, text: 'wontfix' }
 ]
 }
 }
 });
</script>
</html>

作者也说了,对vue2.x的双向绑定机制不了解,希望路过的大神帮帮忙。

我不是vue2的大神,甚至连新手都不算,只能说是初学者。我对代码进行了调整,当然,也是操作了dom,但是由于封装在指令里面了,使用人员不需要再次操作,不涉及到开发人员操作dom的情况,我还是可以接受的。

 Vue.directive('select2', {
 inserted: function (el, binding, vnode) {
 let options = binding.value || {};

 $(el).select2(options).on("select2:select", (e) => {
 // v-model looks for
 // - an event named "change"
 // - a value with property path "$event.target.value"
 el.dispatchEvent(new Event('change', { target: e.target })); //说好的双向绑定,竟然不安套路
 console.log("fire change in insert");
 });
 },
 update: function (el, binding, vnode) {
 for (var i = 0; i < vnode.data.directives.length; i++) {
 if (vnode.data.directives[i].name == "model") {
 $(el).val(vnode.data.directives[i].value);
 console.log("new value in update:"+vnode.data.directives[i].value);
 }
 }
 $(el).trigger("change");
 console.log("fire change in update");
 }
 });

//html代码

<select v-select2="" v-model="editor.P1" required="required" class="form-control ">
 <option value=""></option>
 <option v-for="item in codes" v-bind:value="item.NAME">{{item.NAME}}</option>
</select>

经过好几天的研究,终于我发现在作者原来的代码的update中,加入修改el的val值,然后再触发select2的change事件,就可以了。而在使用方面,只需要给加一个v-select2即可,v-model以及option的配置都依照vue2的推荐方式,原封不动。之所以加了一个空的option是因为如果不加,默认select2是选择第一个选项的,但是,由于未知原因,与vue.editor.P1并不同步。

下载本文
显示全文
专题