Question

I have a page with different knockout generated content. I have a right menu with data that should load new data in the middle of the page depending the row chosen in the right menu.

So, I can fetch the content in the middle of the page. It loads the current content from the backend and displays it. However, when I choose an item in the right panel... I would like to re-fetch data for the middle of the page and update the page. This, however, is a stopping point for me. Since the fetching of the content is done in a namespace and global variables are not allowed, I cannot reuse the current viewModel (or, at least I do not know how to). Hence I am creating a new viewModel, which I cannot apply binding to, since these have already been applied to the same type of viewModel when the page was loaded.

This is my JS code:

Function.registerNamespace('Domain.Discussion');

$(document).ready(function () {
    var repliesViewModel = Domain.Discussion.RepliesView.ReplyPage;

    ko.applyBindings(repliesViewModel, document.getElementById('discussion'));
});

Domain.Discussion.RepliesView = Domain.Discussion.RepliesView || {
    ReplyPage: (function () {
        function Reply(data) {
            var self = this;
            self.ParentItemId = data.ParentItemID;
            self.Body = data.Body;
            self.Created = data.Created;
        }
        var onGetRepliesSuccess = function (replies) {
            var buffer;
            buffer = $.map(replies.RepliesResult, function (item) { return new Reply(item); });
            viewModelReplies.replies(buffer);
        };
        var onGetRepliesError = function (sender, args) {
            alert(args.message);
        };
        var viewModelReplies = {
            replies: ko.observableArray([]),
            loadReplies: function (parentItemId, listId) {
                if (parentItemId !== null && listId !== null) {
                    console.log("ParentItemId: " + parentItemId + " -- ListId: " + listId);
                    alert("Domain.Discussion.RepliesView");

                    $.ajax({
                        url: absolutePath + "api/Replies/" + listId + "/" + parentItemId,
                        headers: { "Accept": "application/json; odata=verbose" },
                        success: onGetRepliesSuccess,
                        error: onGetRepliesError
                    })
                }
            },
        };

        viewModelReplies.loadReplies(1, '6a5ed4ad-5a7c-428f-947e-b8aa2de54489');

        return viewModelReplies;
    }())
};

// The method loaded by the row-click in the right panel.
Domain.Discussion.LoadDiscussionReplies2 = function (discussionListId, currentItem) {
    try {
        if (currentItem.ListId !== "" && currentItem.Id !== "") {
            // Call the current viewModel and tell it to loadReplies
            // This doesn't work since global variables are, of course, not allowed
            repliesViewModel.loadReplies(currentItem.Id, currentItem.ListId);
        }
    }
    catch (e) {
        alert(e);
    }
}

This is my HTML code:

<!-- page discussion -->
<div data-role="page" id="discussion">
    <!-- Content -->
    <div data-role="content">
        <!-- ladda replies -->
        <div id="replies">
            <!-- ko foreach: replies -->
            <div class="message message-first">
                <div class="message-header">
                    <div class="message-header-user">
                        <h1>Persons Name</h1>
                        <h2>Subtitle</h2>
                    </div>
                    <div class="message-header-date" data-bind="text: Created"></div>
                </div>
                <div class="message-content">
                    <span data-bind="text: Body"></span>
                    <hr />
                </div>
            </div>
            <!-- /ko -->
        </div>
    </div>
    <!-- panel -->
    <!-- Grupper -->
    <div id="nav-panel-discussion-groups" data-role="panel" data-theme="a" data-position="right" data-display="overlay">
                <!-- ko foreach: discussions -->
                <ul class="nav-search" data-role="listview" data-theme="a" data-iconpos="left" data-inset="true">
                    <li data-role="list-divider" data-swatch="a" data-theme="a" data-form="ui-bar-a">
                        <div data-type='horizontal'>
                            <label data-bind="text: Title"></label>
                            <a data-bind="attr: { eventId: Id }"  eventId="" name='newThread' data-role="button" data-icon='plus' data-inline="true" data-mini="true" data-theme="a" data-iconpos='notext' id="A4">New</a>
                        </div>
                    </li> 

                    <!-- ko foreach: Threads -->
                    <li data-mini="true" data-icon="false" data-bind="">
                        <a href="#discussion" data-bind="text: Title, click: Domain.Discussion.LoadDiscussionReplies2.bind($root.Id, Id)" data-transition="slide"></a>
                    </li>
                    <!--  /ko -->
                </ul>
                <!-- /ko -->
    </div>
    <!-- /panel -->
</div>

My question is: How can I re-fetch data and re-use the viewModel once initiated on the page load. I am completely stuck now.

Was it helpful?

Solution

I have a similar requirement in my current project. We have several viewmodels on the same page, and an action in one viewmodel might affect the state in one (or more) of the others. We solved it by using an EventEmitter bus that all the viewmodels share. So when one of the viewmodel gets some new data, it just sends it as an event to the other viewmodels that listens to this event. Something like this:

// Data fetched
eventBus.emit('mynamespace:data', [data]);

And in the other viewmodel:

// Respond to data
eventBus.on('mynamespace:data', function(data) {
    // Process data and update viewmodel state
}

So the only thing in common is the event bus. They have no other knowledge of each other.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top