Vue.js在Select2

时间:2015-09-16 23:53:14

标签: ajax vue.js jquery-select2-4

我正在开发一个使用Vue.jsVue Router作为前端javascript框架的项目,该框架需要在整个应用中使用许多位置的用户选择框。我想使用select2作为选择框。为了尽量使我的代码尽可能干净,我已经实现了一个自定义过滤器来按照select2接受它的方式格式化数据,然后我实现了一个类似于the one found on the Vue.js website的自定义指令。

当应用程序启动时,它会在api中查询用户列表,然后存储该列表供以后使用。然后,我可以在应用程序的其余部分和任何路由中引用用户列表,而无需再次查询后端。我可以成功检索用户列表,通过用户列表过滤器将其传递给select2想要的格式,然后创建一个select2,并将用户列表设置为选项。

但这仅适用于具有select2的路由不是应用程序加载的第一页。例如,如果我进入主页(没有任何select2用户列表),然后转到用户页面(使用select2),它的效果很好。但是,如果我直接进入“用户”页面,则select2将没有任何选项。我想这是因为当Vue正在加载时,它会向服务器发送一个GET请求以获取用户列表,在它收到响应之前,它将继续执行异步执行并创建没有任何选项的select2,但是一旦用户列表从服务器返回,Vue就不知道如何用选项列表更新select2。

以下是我的问题:如何从AJAX调用中检索选项(对于整个应用程序只应该执行一次,无论用户选择框显示多少次),然后将它们加载到select2中如果一个人直接进入带有select2的页面?

提前谢谢!如果您发现我应该做的其他事情,请告诉我,因为我希望此代码使用最佳实践。

这是我到目前为止所做的:

简化的app.js

var App = Vue.extend({
    ready: function() {
        this.fetchUsers();
    },

    data: function() {
        return {
            globals: {
                users: {
                    data: []
                },
            }
        };
    },

    methods: {
        fetchUsers: function() {
            this.$http.get('./api/v1/users/list', function(data, status, response) {
                this.globals.users = data;
            });
        },
    }
});

API的示例回复

{
  "data": [
    {
      "id": 1,
      "first_name": "John",
      "last_name": "Smith",
      "active": 1
    },
    {
      "id": 2,
      "first_name": "Emily",
      "last_name": "Johnson",
      "active": 1
    }
  ]
}

用户列表过滤器

Vue.filter('userList', function (users) {
    if (users.length == 0) {
        return [];
    }

    var userList = [
        {
            text : "Active Users",
            children : [
                // { id : 0, text : "Item One" }, // example
            ]
        },
        {
            text : "Inactive Users",
            children : []
        }
    ];

    $.each( users, function( key, user ) {
        var option = { id : user.id, text : user.first_name + ' ' + user.last_name };
        if (user.active == 1) {
            userList[0].children.push(option);
        }
        else {
            userList[1].children.push(option);
        }   
    });
    return userList;
});

自定义Select2指令(类似于this

Vue.directive('select', {

  twoWay: true,

  bind: function () {

  },

  update: function (value) {

    var optionsData
    // retrive the value of the options attribute
    var optionsExpression = this.el.getAttribute('options')
    if (optionsExpression) {
      // if the value is present, evaluate the dynamic data
      // using vm.$eval here so that it supports filters too
      optionsData = this.vm.$eval(optionsExpression)
    }

    var self = this
    var select2 = $(this.el)
      .select2({
        data: optionsData
      })
      .on('change', function () {
        // sync the data to the vm on change.
        // `self` is the directive instance
        // `this` points to the <select> element
        self.set(select2.val());
        console.log('emitting "select2-change"');
        self.vm.$emit('select2-change');
      })


    // sync vm data change to select2
    $(this.el).val(value).trigger('change')
  },

  unbind: function () {
    // don't forget to teardown listeners and stuff.
    $(this.el).off().select2('destroy')
  }
})

Select2来自模板的示例实施

<select
    multiple="multiple"
    style="width: 100%"
    v-select="criteria.user_ids" 
    options="globals.users.data | userList"
    >
</select>

2 个答案:

答案 0 :(得分:2)

我可能已经找到了可以正常工作的东西,虽然我不确定它是最好的方法。这是我更新的代码:

从模板中实施Select2

xMyTextx
xMyTextx
x    MyTextx
xMyText    x

摘自app.js

<select
    multiple="multiple"
    style="width: 100%"
    v-select="criteria.reporting_type_ids" 
    options="globals.types.data | typeList 'reporttoauthorities'"
    class="select2-users"
    >
</select>

这种方式适合我,但它仍然有点像hackish。如果有人对如何做到这一点有任何其他建议,我将不胜感激!

答案 1 :(得分:0)

谢谢,但是我正在从事公司的旧项目,由于select2的版本太低,我遇到了this issue。而且我不确定v-select语法是否来自vue标准(也许来自vue-select libaray?)。所以这是我基于您的实现。使用input标记代替select标记,并使用v-model表示v-select。它就像一种魅力,再次感谢@bakerstreetsystems

<input type="text"
       multiple="multiple"
       style="width: 300px"
       v-model="supplier_id"
       options="suppliers"
       id="select2-suppliers"
>
</input>
<script>
  $('#app').ready(function() {

    var app = new Vue({
      el: "#app",
      data: {
        supplier_id: '<%= @supplier_id %>', // We are using server rendering(ruby on rails)
        suppliers: [],
      },
      ready: function() {
        this.fetchSuppliers();
      },
      methods: {
        fetchSuppliers: function() {
          var self = this;
          $.ajax({
            url: '/admin_sales/suppliers',
            method: 'GET',
            success: function(res) {
              self.suppliers = res.data;

              self.$nextTick(function () {
                var optionsData = self.suppliers;
                $('#select2-suppliers').select2({
                  placeholder: "Select a supplier",
                  allowClear: true,
                  data: optionsData,
                });
              });
            }
          });
        },
      },
    });

  })
</script>