Vue.js 正在为 Select2 中的选项检索远程数据

Vue.js Retrieving Remote Data for Options in Select2

我正在开发一个使用 Vue.js and Vue Router as the frontend javascript framework that will need to use a select box of users many places throughout the app. I would like to use select2 for the select box. To try to make my code the cleanest I can, I've implemented a custom filter to format the data the way select2 will accept it, and then I've implemented a custom directive similar to the one found on the Vue.js website.

的项目

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

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

这是我的问题:如何从 AJAX 调用中检索选项(无论用户 select 框出现多少次,整个应用程序都应该只调用一次显示),然后将它们加载到 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')
  }
})

从模板中选择 2 的示例实现

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

我可能找到了一些可行的方法,尽管我不确定这是解决问题的最佳方法。这是我更新的代码:

从模板中实现 Select2

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

摘自app.js

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

        this.$nextTick(function () {
            var optionsData = this.$eval('globals.users.data | userList');
            console.log('optionsData', optionsData);
            $('.select2-users').select2({
                data: optionsData
            });
        }); 
    });
},

这种方式对我有用,但感觉还是有点老套。如果有人对如何执行此操作有任何其他建议,我将不胜感激!

谢谢,但我正在处理公司遗留项目,由于 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>