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

0👍

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). If I create another Vue app the code works as expected:

<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. Instead I have to pass the array of wallets to each of the Vue apps I want to be reactive to updates.

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.

Leave a comment