是什么导致VueJS在这种情况下停止更新DOM? [英] What causes VueJS to stop updating the DOM in this instance?

查看:97
本文介绍了是什么导致VueJS在这种情况下停止更新DOM?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我浏览了所有可以找到的类似帖子,但是似乎没有答案可以解决我的问题.具体来说,它不会更新ID为"table"的表.

I've looked on every similar post that I can find, but no answer seems to fix my issue. Specifically, it does not update the table with id "table".

HTML:

<section id="body">
    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-12">
                <div class="panel panel-primary">
                    <div class="panel-heading" id="panel">
                        <div class="row">
                            <div class="col-sm-12">
                                <h3 class="panel-title">Filters</h3>
                            </div>
                        </div>
                    </div>
                    <div class="panel-body" id="panel-body">
                        <div class="row">
                            <div class="col-sm-12">
                                <form id="filterForm" class="form-horizontal">
                                    <div class="form-group">
                                        <div class="col-sm-12">
                                            <label class="control-label" for="focusedInput">Category:</label>
                                            <select id="category" class="js-example-basic-single form-control">
                                                <option value="">Any</option>
                                                <option v-for="category in categories" value="category.categoryTitle">
                                                {{category.categoryTitle}}</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="form-inline row">
                                        <div class="col-sm-12">
                                            <label class="control-label" style="margin-right:20px;">Air Date:</label>
                                            <div style="width:35%" class="form-group">
                                                <div class='input-group date' id='datetimepicker1'>
                                                    <input type='text' class="form-control" v-model="airDate"/>
                                                    <span class="input-group-addon">
                                                        <span class="glyphicon glyphicon-calendar"></span>
                                                    </span>
                                                </div>
                                            </div>
                                            <label class="control-label">Show Number:</label>
                                            <input style="width:35%" class="form-control" type="number" id="showNumber" v-model="showNumber">
                                        </div>
                                    </div>
                                    <div class="form-inline row">
                                        <div class="col-sm-12">
                                            <label class="control-label">Question contains:</label>
                                            <input style="width:35%" class="form-control" type="text" v-model="questionText">
                                            <label class="control-label">Dollar Value:</label>
                                            <input style="width:35%" class="form-control" type="number" id="showNumber" v-model="dollarValue">
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-sm-offset-9 col-sm-3" style="margin-top:5px;">                                   
                                <button type="button" class="btn btn-warning" v-on:click="reset">Reset Filters</button>
                                <button type="button" class="btn btn-primary" v-on:click="filter">Filter</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12">
                <div class="panel panel-primary" id="tableCard" style="margin-bottom:20px; margin-top:40px;">
                    <div class="panel-heading">
                        <div class="row">
                            <div class="col-sm-10">
                                <h3 class="panel-title">Jeopardy Questions</h3>
                            </div>
                            <div class="col-sm-2">
                                <span id="totalQuestionsSpan">Total Entries: {{entries.length}} entries</span>
                            </div>
                        </div>
                    </div>
                    <div class="panel-body" style="padding-top:45px;">
                        <div class="wrapper">
                            <table id="tableScroll" class="table table-striped table-fixed">
                                <thead style="background-color:white;">
                                    <tr>
                                        <th style="cursor:pointer; min-width: 110px;">
                                            Question
                                            <span v-if="questionSort == 1" id="questionUp">&#9650;</span>
                                            <span v-else-if="questionDown == -1" id="questionDown">&#9660;</span>
                                        </th>
                                        <th style="cursor:pointer; min-width: 120px; ">
                                            Answer
                                            <span v-if="answerSort == 1" id="answerUp">&#9650;</span>
                                            <span v-else-if="answerDown == -1" id="answerDown">&#9660;</span>
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Value
                                            <span v-if="valueSort == 1" id="valueUp">&#9650;</span>
                                            <span v-else-if="valueDown == -1" id="valueDown">&#9660;</span>
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Show Number
                                            <span v-if="showNumberSort == 1" id="showNumberUp">&#9650;</span>
                                            <span v-else-if="showNumberDown == -1" id="showNumberDown">&#9660;</span>
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Category
                                            <span v-if="categorySort == 1" id="categoryUp">&#9650;</span>
                                            <span v-else-if="categoryDown == -1" id="categoryDown">&#9660;</span>
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Air Date
                                            <span v-if="airDateSort == 1" id="airDateUp">&#9650;</span>
                                            <span v-else-if="airDateDown == -1" id="airDateDown">&#9660;</span>
                                        </th>
                                    </tr>
                                </thead>
                                <tbody id="table">
                                    <tr v-for="entry in entries">
                                        <td>{{entry.questionText}}</td>
                                        <td>{{entry.answerText}}</td>
                                        <td>{{entry.dollarValue}}</td>
                                        <td>{{entry.showNumber}}</td>
                                        <td>{{entry.categoryTitle}}</td>
                                        <td>{{entry.airDate}}</td>
                                    </tr>
                                    <tr v-if="entries.length == 0">
                                        <td colspan="6" style="text-align: center;"> No entries to display </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>

JS

var app = new Vue({
        el: '#body',
        data: {
            loggedIn: false,
            questionSort: 0,
            answerSort: 0,
            valueSort: 0,
            showNumberSort: 0,
            categorySort: 0,
            airDateSort: 0,
            entries: [],
            url: "/questions",
            categories: [],

            // form model data
            categoryTitle: '',
            airDate: '',
            questionText: '',
            dollarValue: '',
            showNumber: '',
        },
        mounted: function () {
            $.get("/api/categories", function(result) {
               Vue.set(app, "categories", result.data);
                $('.js-example-basic-single').select2();
            }, "json").fail(function(err) {
                showErrorMessage(err.responseJSON.message_prettified);
            });
        },
        methods: {
            filter : function() {
                var queryParams = "?";
                var params = 0;
                app.categoryTitle = $('#category :selected').text().trim();
                if (typeof app.categoryTitle !== "undefined" && app.categoryTitle != null) {
                    params++;
                    queryParams += "categoryTitle=" + app.categoryTitle
                }
                if (app.airDate.length > 0) {
                    params++;
                    if (params > 0) {
                        queryParams += "&";
                    }
                    queryParams += "airDate=" + app.airDate
                }
                if (app.questionText.length > 0) {
                    params++;
                    if (params > 0) {
                        queryParams += "&";
                    }
                    queryParams += "questionText=" + app.questionText
                }
                if (app.dollarValue.length > 0) {
                    params++;
                    if (params > 0) {
                        queryParams += "&";
                    }
                    queryParams += "dollarValue=" + app.dollarValue
                }
                if (app.showNumber.length > 0) {
                    params++;
                    if (params > 0) {
                        queryParams += "&";
                    }
                    queryParams += "showNumber=" + app.showNumber
                }
                if (queryParams.length == 1) {
                    queryParams = "";
                }
                var url = "/questions"
                var URL = url + queryParams;
                $.get(URL, result => {   
                        Vue.set(app, "entries", result.data);
                        app.$forceUpdate();
                    }, "json").fail(function(err) {
                        showErrorMessage(err.responseJSON.message_prettified);
                    }).always(function() {
                       $("#loader").addClass("toggled");
                });
            }
        }
    });

当前行为:

对/api/categories的AJAX调用正确更新了DOM上的下拉列表,使我可以选择一个类别.挂载该应用程序后,它会更新表,并显示colspan 6无条目可显示"单元格.但是,在发送并返回过滤器请求之后,该表不会更新以反映更新后的数据(尽管在控制台中选中该数据后,该数据正确显示为已更改).

The AJAX call to /api/categories correctly updates the dropdown on the DOM, allowing me to select a category. When the app is mounted, it does update the table, showing the colspan 6 "No entries to display" cell. However, after the filter request is sent and returns, the table does not update to reflect the updated data (despite the data correctly showing as having been changed when checked in the console).

预期行为:

当使用查询参数对/questions的AJAX调用解析并更新了app中的entrys数据字段时,该表将更新以反映所做的更改.

When the AJAX call to /questions with the query params resolves and updates the entries data field in app, the table updates to reflect the changes.

尝试的修复程序:

浏览了$ forceUpdate,$ set,Vue.set,并使用for循环手动覆盖了阵列.

Explored $forceUpdate, $set, Vue.set, and manually overwriting the array using a for loop.

在进行了很多侦听并且还集成了VueX(如@WaldemarIce所建议)之后,虽然有所帮助,但是无论是否改善了迷你程序的整体代码结构,我都找到了解决方案.

After doing a lot of snooping and also integrating VueX (as suggested below by @WaldemarIce) which may have helped but regardless did improve the overall code structure of my mini program, I've arrived to a solution.

关于Laracast的帖子使我想知道是否存在数据问题:

This post on Laracast made me wonder whether perhaps there was a data issue: https://laracasts.com/discuss/channels/vue/v-for-loop-rendering-keeps-throwing-undefined-error

然后让我意识到问题出在这里的这段代码中:

Which then caused me to realize the problem was in this line of code here:

<option v-for="category in categories" value="category.categoryTitle">
                                            {{category.categoryTitle}}</option>

这是引起问题的原因,因为直到生命周期的后期才定义value ="category.categoryTitle"中的类别.我将其更改为v-bind:value ="category.categoryTitle"并更新了我的JS以使其现在可以工作.我在@Kaicui的后续讨论中发布的TypeError导致Vue失去了数据的反应性.解决了这个问题后,Vue再次开始做出正确的反应.

Which was causing an issue because category in value="category.categoryTitle" wasn't defined until later in the life cycle. I changed that to v-bind:value="category.categoryTitle" and updated my JS to make it work now. The TypeError that I posted in the follow-up discussions on @Kaicui 's post was causing Vue to lose the reactivity of the data. Once I solved that problem, Vue began to react properly again.

更新的HTML:

<section id="body">
    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-12">
                <div class="panel panel-primary">
                    <div class="panel-heading" id="panel">
                        <div class="row">
                            <div class="col-sm-11">
                                <h3 class="panel-title">Filters</h3>
                            </div>
                            <div class="col-sm-1">
                                <i id="toggleFilter" class="fa fa-chevron-down filter-collapsed" style="cursor:pointer; display:none;" aria-hidden="true"></i>
                                <i id="toggleFilter" class="fa fa-chevron-up filter-collapsed"  aria-hidden="true" style="cursor:pointer;"></i>
                            </div>
                        </div>
                    </div>
                    <div class="panel-body" id="panel-body">
                        <div class="row">
                            <div class="col-sm-12">
                                <form id="filterForm" method="GET" action="/questions" class="form-horizontal">
                                    <div class="form-inline">
                                        <div class="col-sm-12" style="margin-bottom:15px;">
                                            <input type="hidden" name="categoryTitle" id="categoryTitleHidden">
                                            <label class="control-label" for="focusedInput">Category:</label>
                                            <select style="width:90%; height:120% !important;" v-model="categorySelect" id="category" class="js-example-basic-single form-control">
                                                <option value="">Any</option>
                                                <option v-for="category in categories" v-bind:value="category.categoryTitle">
                                                {{category.categoryTitle}}</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="form-inline">
                                        <div class="col-sm-12" style="margin-bottom:15px;">
                                            <label class="control-label" style="margin-right:20px;">Air Date:</label>
                                            <div style="width:35%; margin-right:10px" class="form-group">
                                                <div style="width:100%" class='input-group date' id='datetimepicker1'>
                                                    <input type='text' class="form-control" name="airDate"/>
                                                    <span class="input-group-addon">
                                                        <span class="glyphicon glyphicon-calendar"></span>
                                                    </span>
                                                </div>
                                            </div>
                                            <label class="control-label">Show Number:</label>
                                            <input style="width:35%" class="form-control" type="number" id="showNumber" name="showNumber">
                                        </div>
                                    </div>
                                    <div class="form-inline">
                                        <div class="col-sm-12">
                                            <label class="control-label">Question contains:</label>
                                            <input style="width:35%" class="form-control" type="text" id="questionText" name="questionText">
                                            <label class="control-label">Dollar Value:</label>
                                            <input style="width:35%" class="form-control" type="number" id="dollarValue" name="dollarValue">
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-sm-offset-9 col-sm-3" style="margin-top:5px;">
                                <button type="button" class="btn btn-warning" v-on:click="reset">Reset Filters</button>
                                <button type="button" class="btn btn-primary" v-on:click="filter">Filter</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12">
                <div class="panel panel-primary" id="tableCard" style="margin-bottom:20px; margin-top:40px;">
                    <div class="panel-heading">
                        <div class="row">
                            <div class="col-sm-10">
                                <h3 class="panel-title">Jeopardy Questions</h3>
                            </div>
                            <div class="col-sm-2">
                                <span id="totalQuestionsSpan">Total Entries: {{entryCount}} entries</span>
                            </div>
                        </div>
                    </div>
                    <div class="panel-body" style="padding-top:45px;">
                        <div class="wrapper">
                            <table id="tableScroll" class="table table-striped table-fixed">
                                <thead style="background-color:white;">
                                    <tr>
                                        <th style="cursor:pointer; min-width: 110px;">
                                            Question
                                        </th>
                                        <th style="cursor:pointer; min-width: 120px; ">
                                            Answer
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Value
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Show Number
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Category
                                        </th>
                                        <th style="cursor:pointer; min-width: 80px;">
                                            Air Date
                                        </th>
                                    </tr>
                                </thead>
                                <tbody id="table">
                                    <tr v-if="entriesValid" v-for="entry in entries">
                                        <td>{{entry.questionText}}</td>
                                        <td>{{entry.answerText}}</td>
                                        <td>{{entry.dollarValue}}</td>
                                        <td>{{entry.showNumber}}</td>
                                        <td>{{entry.categoryTitle}}</td>
                                        <td>{{entry.airDate}}</td>
                                    </tr>
                                    <tr v-if="!entriesValid">
                                        <td colspan="6" style="text-align: center;"> No entries to display </td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div style="position: absolute; left: 45%; top:25%; z-index:3;">
        <i id="loader" class="fa fa-circle-o-notch fa-spin fa-5x fa-fw toggled" style="z-index:3"></i>
    </div>
</section>

更新的JS

    Vue.use(Vuex)
    Vue.config.debug = false;
    Vue.config.silent = true;
    var URL;
    const store = new Vuex.Store({
        state: {
            loggedIn: false,

            // ordering data
            questionSort: 0,
            answerSort: 0,
            valueSort: 0,
            showNumberSort: 0,
            categorySort: 0,
            airDateSort: 0,

            // server related ata
            entries: [],
            url: "/questions",
            categories: [{
                categoryTitle: "Test",
            }],
        },
        mutations: {
            categories (state, data) {
                state.categories = data;
            },
            entries (state, data) {
                console.log(data);
                state.entries = data;
                console.log(state.entries)
            }
        },
        actions: {
            fetchCategories ({ commit }) {
                $("#loader").removeClass("toggled");
                $.get("/api/categories", function(result) {
                    commit('categories', result.data);
                }, "json")
                .fail(function(err) {
                    if (err.status == 0) {
                        showErrorMessage("Network Problem");
                    }
                    else {
                        showErrorMessage(err.responseJSON.message_prettified);
                    }
                }).always(function() {
                    $("#loader").addClass("toggled");
                });
            },
        },
    });

    var app = new Vue({
        el: '#body',
        store: store,
        data: {
            categorySelect: "",
        },
        mounted: function() {
            store.dispatch("fetchCategories").then(() => {
                $('.js-example-basic-single').select2();
            });
        },
        computed: {
            categories: function() {
                return store.state.categories;
            },
            entryCount: function() {
                if (store.entries) {
                    if (typeof store.entries.length !== "undefined") {
                        return store.entries.length;
                    }
                    else {
                        return 0;
                    }
                }
                else {
                    return 0;
                }
            },
            entriesValid: function() {
                if (store.state.entries) {
                    if (typeof store.state.entries.length !== "undefined" && store.state.entries.length > 0) {
                        return true;
                    }
                    else {
                        return false;
                    }
                }
                else {
                    return false;
                }
            },
            entries: function() {
                return store.state.entries;
            },
            loggedIn: function() {
                return store.state.loggedIn;
            },
        },
        methods: {
            reset: function() {
                $('.js-example-basic-single').val('').trigger('change');
                $("#datetimepicker1").datetimepicker("clear");
                $("#categoryTitleHidden").val("");
                $("#showNumber").val("");
                $("#questionText").val("");
                $("#showNumber").val("");
                $("#dollarValue").val("");
            },
            filter : function() {
                var value = $('#category :selected').text().trim();
                if (value !== "Any") {
                    $("#categoryTitleHidden").val(value);
                }
                else {
                    $("#categoryTitleHidden").val("");   
                }
                var options = {   
                    success: function(responseText, statusText, xhr, $form) {
                        store.commit("entries", JSON.parse(xhr.responseText).data)
                    } 
                }; 
                $("#filterForm").ajaxSubmit(options);
            }
        }
    });

推荐答案

IMO问题导致Vue.set(app,...).抱歉,您无法在Vue实例本身上设置属性.

IMO problem is causing Vue.set(app, ...). AFAIK you can't set properties on Vue instance itself.

编辑:使用Vuex的实时示例和使用jQuery的异步数据

EDIT: Live example with Vuex and async data with jQuery

var store = new Vuex.Store({
  state: {
    // Car manufacturers for datalist will be held here.
    // Cars are globally accessible, in every component,
    // as this.$store.state.cars
    cars: null
  },
  mutations: {
    // Mutations changes state, but must be sync,
    // so you can't call $.get() or another 
    // async function in any mutation.
    updateCars: function (state, payload) {
      state.cars = payload
    }
  },
  actions: {
    // For async ops there are actions,
    // but they can't change state - for state
    // change fire particular mutation.
    loadCars: function (context, payload) {
      $.get(payload.src).then(function (data) {
        context.commit('updateCars', data)
      })
    }
  }
})

Vue.component('my-list', {
  template: '#my-list',
  props: ['src'],
  // All components see this.$store.state.cars, but
  // still can have own local data.
  data: function () {
    return {
      manufacturer: ''
    }
  },
  // Fire async store action
  created: function () {
    this.$store.dispatch({
      type: 'loadCars',
      src: this.src
    })
  }
  // Alternatively, you can use this
  // version - w/o action. It's OK to use
  // mutation here, in callback of async function.
  /* created: function () {
    $.get(this.src).then(function (data) {
      this.$store.commit('updateCars', data)
    })
  } */
})

new Vue({
  el: '#app',
  // Inject store state to all components
  store: store
})

<div id="app">
  <my-list src="https://api.mockaroo.com/api/32318a80?count=20&key=cdbbbcd0">
  </my-list>
</div>

<template id="my-list">
  <div>
    <label>
      Choose a car manufacturer:<br>
      <input list="cars" name="myCars" v-model="manufacturer">
    </label>
    <datalist id="cars">
      <option
        v-for="car in $store.state.cars"
        :value="car.car"
      >
        {{ car.id }}
      </option>
    </datalist>
    <p>
      Selected manufacturer:<br>
      {{ manufacturer }}
    </p>
  </div>
</template>

<script src="https://unpkg.com/vue@2.5.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex@3.0.0/dist/vuex.min.js"></script>
<script src="https://unpkg.com/jquery@3.2.1/dist/jquery.min.js"></script>

这篇关于是什么导致VueJS在这种情况下停止更新DOM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆