简体   繁体   English

Vue.js无法正确呈现动态加载的选择标记选项

[英]Vue.js not correctly rendering dynamically loaded select tag options

I'm trying to render a select tag with options loaded from a Vue object, but, as you can see by the way this is rendered, it looks like something's wrong: 我正在尝试使用从Vue对象加载的选项来呈现选择标签,但是,正如您所看到的那样,呈现这种方式似乎是错误的:

在此处输入图片说明

Now this is the markup for the select tag: 现在,这是select标签的标记:

<div class="form-group ui-model">
    @Html.LabelFor(m => m.SenderWalletId, new { @class = "control-label col-sm-2" })
    <div class="col-sm-10">
        <select class="form-control" v-model="selectedSenderWallet">
            <option v-if="!walletsLoaded" disabled>Loading wallets...</option>
            <option v-if="walletsLoaded" v-for="wallet in wallets" v-bind:value="wallet.id">{{ wallet.description }}</option>
        </select>
        @Html.HiddenFor(m => m.SenderWalletId, new { }, new { value = "selectedSenderWallet" })
        @Html.ValidationMessageFor(m => m.SenderWalletId)
    </div>
</div>

And this is the script for creating the Vue app, along with SignalR taking care of the wallets (setWallets is called by the hub after successfully loading the wallets from my repository): 这是用于创建Vue应用程序的脚本,以及SignalR负责钱包的创建(从我的存储库成功加载钱包后,集线器会调用setWallets):

<script>
    var walletLinkText = Vue.extend({
        props: ['wallet'],
        template: '<span>{{ linkText }}</span>',
        computed: {
            linkText: function () {
                return this.wallet.description + ' (' + this.wallet.currentAmountFormatted + ')';
            }
        }
    });

    var uiModel = new Vue({
        el: '.ui-model',
        data: {
            wallets: [],
            walletsLoaded: false
        },
        components: {
            'wallet-link-text': walletLinkText
        }
    });

    $(function() {
        var walletsHubProxy = $.connection.walletsHub;

        walletsHubProxy.client.updateWallet = function (...) {
            ...
        };

        walletsHubProxy.client.setWallets = function (currentWallets) {
            uiModel.wallets = currentWallets;
            uiModel.walletsLoaded = true;
        };

        $.connection.hub.start().done(function() {
            walletsHubProxy.server.getWallets();
        });
    });
</script>

The thing is I'm sure wallets are loaded correctly as you can see in the menu: 关键是,如菜单所示,我确定钱包已正确装入:

钱包加载

钱包已加载

And this is the html markup for the above: 这是上面的html标记:

<ul class="nav navbar-nav ui-model">
    <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Wallets <span class="caret"></span></a>
        <ul class="dropdown-menu">
            <li v-if="!walletsLoaded"><a><i class="fa fa-spinner fa-pulse"></i> Loading...</a></li>
            <li v-if="walletsLoaded && !wallets.length"><a>No wallets.</a></li>
            <li v-if="walletsLoaded" v-for="wallet in wallets">
                <a v-bind:href="'@Url.Content(string.Format("~/Wallets/"))' + wallet.id">
                    <wallet-link-text :wallet="wallet"></wallet-link-text>
                </a>
            </li>
            <li role="separator" class="divider"></li>
            <li><a href="@Url.Action("Add", "Wallets")"><i class="fa fa-fw fa-edit"></i> Add new</a></li>
        </ul>
    </li>
</ul>

So where am I wrong? 那我在哪里错了? I feel like I'm stuck on something really simple. 我觉得自己只停留在一些非常简单的事情上。

Apparently Vue can only be mounted within one context element: .ui-model refers to several elements in the DOM, therefore just the first is rendered correctly (the menu). 显然,Vue只能安装在一个上下文元素中:.ui-model引用DOM中的多个元素,因此只有第一个元素正确呈现(菜单)。 If I create another Vue app the code works as expected: 如果我创建另一个Vue应用,则代码将按预期工作:

<div class="form-group" id="wallets-app">
    @Html.LabelFor(m => m.WalletId, new { @class = "control-label col-sm-2" })
    <div class="col-sm-10">
        <select class="form-control" v-model="selectedWallet">
            <option v-if="!walletsLoaded" disabled>Loading wallets...</option>
            <option v-else v-for="wallet in wallets" v-bind:value="wallet.id">{{ wallet.description }}</option>
        </select>
        @Html.HiddenFor(m => m.WalletId, new { }, new { value = "selectedWallet" })
        @Html.ValidationMessageFor(m => m.WalletId)
    </div>
</div>

<script>
    var app = new Vue({
        el: '#wallets-app',
        data: {
            wallets: [],
            walletsLoaded: false,
            selectedWallet: ''
        }
    });

    //SignalR code to sync hubs and ui
</script>

I wanted a single array of data to be shared between multiple elements, so that each element would've had its own html representation, its view. 我希望在多个元素之间共享一个数据数组,以便每个元素都有自己的html表示形式,其视图。 Instead I have to pass the array of wallets to each of the Vue apps I want to be reactive to updates. 相反,我必须将钱包阵列传递给每个我想对更新做出反应的Vue应用程序。

Well, it seems unefficient to me, but I'm sure I'm missing something. 好吧,这对我来说似乎效率不高,但是我确定我错过了一些东西。 Feel free to add any suggestions and/or corrections. 随时添加任何建议和/或更正。

EDIT: the correct way to achieve the result I wanted was to simply put all the data into a store object, in order to share the same data across multiple components (such as menu items for each of the wallets). 编辑:达到我想要的结果的正确方法是简单地将所有数据放入存储对象,以便在多个组件(例如每个钱包的菜单项)之间共享相同的数据。

<div class="form-group" id="select-wallet">
    @Html.LabelFor(m => m.WalletId, new { @class = "control-label col-sm-2" })
    <div class="col-sm-10">
        <select class="form-control" v-model="selectedWallet">
            <option v-if="!sharedData.walletsLoaded" disabled>Loading wallets...</option>
            <option is="wallet-select-option" v-else v-for="wallet in sharedData.wallets" v-bind:wallet="wallet"></option>
        </select>
        @Html.HiddenFor(m => m.WalletId, new { }, new { value = "selectedWallet" })
        @Html.ValidationMessageFor(m => m.WalletId)
    </div>
</div>

<script>
    var store = {
        data: {
            wallets: [],
            walletsLoaded: false
        }
    };

    Vue.component('wallet-select-option', {
        props: ['wallet'],
        template: '<option v-bind:value="wallet.id">{{ wallet.description }}</option>'
    });

    var selectWallet = new Vue({
        el: '#select-wallet',
        data: {
            sharedData: store.data,
            selectedWallet: ''
        }
    });


    $(function () {
        var walletsHubProxy = $.connection.walletsHub;

        walletsHubProxy.client.updateWallet = function (...) {
            ...
        };

        walletsHubProxy.client.setWallets = function (currentWallets) {
            store.data.wallets = currentWallets;
            store.data.walletsLoaded = true;
        };

        $.connection.hub.start().done(function () {
            walletsHubProxy.server.getWallets();
        });
    });
</script>

I forgot to mention that the @Html.HiddenFor with three parameters is an extension method I wrote to help me with v-bind attributes, which are unparsable by the razor syntax. 我忘了提到带有三个参数的@ Html.HiddenFor是我为帮助我处理v-bind属性而编写的扩展方法,而razor语法无法解析这些属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM