Question

I have created a FaceBook style commenting system using the following blog post: http://techbrij.com/realtime-post-comment-notifications-signalr-knockout

I extended the functionality by adding a way to toggle the commentSection div by adding/removing the hidden css class name. It works beautifully if I have only one instance of the browser open. However, when I open a second browser, the loading of that browser resets both browser instances comment sections to their default "hidden" class. Would anyone have a suggestion on how to unbind the commentSection div from knockout bindings so that that div is controlled client side and not through knockout? Here is my abbreviated code based on the blog post noted above:

HTML:
...
<ul id="msgHolder" class="post-ul" data-bind="foreach: posts">
     <li class="postHolder post-li"> 
        <div class="postFooter">
           ...
           <a class="toggleCommentSection" href="#" data-bind="click: toggleCommentSection, visible: PostComments().length > 0">View Comment(s)</a>
           <div class="commentSection hidden">
              <ul class="post-ul" data-bind="foreach: PostComments">
                ...
              </ul>
           </div>
        </div>
     </li>
</ul>
...

SCRIPT:
function Post(data, hub, owner) {
   ...
   self.toggleCommentSection = function (item, event) {
        if ($(event.target).next('.commentSection').is(':visible')) {
            $(event.target).next('.commentSection').addClass("hidden");
            $(event.target).html("View Comment(s)");
        }
        else {
            $(event.target).next('.commentSection').removeClass("hidden");
            $(event.target).html("Hide Comment(s)");
        }
    }
}
Was it helpful?

Solution

The problem is not that it's still bound to knockoutjs. The problem is that if someone connects, all the posts are loaded again.

From the PostHub:

// GET api/WallPost
public void GetPosts()
{
    ...
    Clients.All.loadPosts(ret);
}

I think this should be Clients.Caller.loadPosts(ret). But aside from that, this means that if anyone calls GetPosts(), the function loadPosts(data) is invoked on all clients:

self.hub.client.loadPosts = function (data) {
    var mappedPosts = $.map(data, function (item) { return new Post(item, self.hub); });
    self.posts(mappedPosts);
}

This will replace the posts, with the new posts. This in turn will render the <ul id="msgHolder" class="post-ul" data-bind="foreach: posts" /> again, meaning you lost your hidden-state.

To prevent this from happening, you should also be using knockoutjs for remembering the hidden-state:

Add a property to your client side Post:

self.commentsHidden = ko.observable(true);

The HTML:

<ul id="msgHolder" class="post-ul" data-bind="foreach: posts">
     <li class="postHolder post-li"> 
        <div class="postFooter">
           ...
           <a class="toggleCommentSection" href="#" data-bind="
               click: toggleCommentSection, 
               visible: PostComments().length > 0, 
               text: commentsHidden() ? 'View Comment(s)' : 'Hide Comment(s)'
               "></a>
           <div class="commentSection" data-bind="css: { hidden: commentsHidden }">
              <ul class="post-ul" data-bind="foreach: PostComments">
                ...
              </ul>
           </div>
        </div>
     </li>
</ul>

toggleCommentSection:

self.toggleCommentSection = function() {
    self.commentsHidden(!self.commentsHidden());
}

Welcome to the world of Knockout!

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