Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create reusable controls using knockout, jquery, and ASP.NET MVC?

I'm wanting to create reusable controls which get used on a knockout / jquery / asp.net mvc page

For instance, various items can have a discussion ( a list of comments). I want a discussion control which handles showing and adding comments, etc.

initial thoughts are to use a partial view to inject the html, and then have .js file with some javascript that sets up the knockout viewmodel. It seems a bit clunky though. I'm just wondering if someone has a really nice way of doing all this and packaging it up as a nice control?

like image 298
Keith Nicholas Avatar asked Oct 16 '11 22:10

Keith Nicholas


1 Answers

Here is one approach.


You have a separate WebAPI controller for handling the data access from the clientside.

//Inside your CommentsApiController, for example
public IEnumerable<Comment> Get(int id)
{
    var comments = _commentsService.Get(int id);    //Call lower layers to get the data you need
    return comments;
}

Your MVC controllers have actions which return PartialViewResults. It is a simple action which returns a partial view.

//Inside your MVC CommentsController, for example
public PartialViewResult CommentsList(int id)
{
    return PartialView(id);
}

Your partial view renders out your markup, with knockout bindings. We make a unique ID for our HTML so we can bind our knockout viewmodel to this specific section of the page (avoid conflicting with other knockout components on the page). The JavaScript we require (knockout viewmodels etc) gets included, a new ViewModel created and knockout bindings applied.

@{
    var commentsId = Model;    //passed from our MVC action
    var uniqueIid = System.Guid.NewGuid().ToString();
}
<section class="comments" id="@uniqueIid ">
    <ul data-bind="foreach: { data: Comments, as: 'comment' }">
        <li>
            <span data-bind="text: comment.Author"></span>
            <p data-bind="text: comment.Message"></p>
        </li>
    </ul>
</section>


@Scripts.Render("~/js/comments")    //using MVC Bundles to include the required JS
@{
    //generate the URL to our WebAPI endpoint.
    var url = Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Comments", id = commentsId });
}
<script type="text/javascript">
    $(function() {
        var commentsRepository = new CommentsRepository('@url');
        var commentsViewModel = new CommentsViewModel(commentsRepository);

        var commentsElement = $('#@uniqueIid')[0];
        ko.applyBindings(commentsViewModel, commentsElement);
    });
</script>

Inside our JavaScript, we define our knockout viewmodels etc.

var CommentsRepository = function(url) {
    var self = this;
    self.url = url;

    self.Get = function(callback) {
        $.get(url).done(function(comments) {
            callback(comments);
        });
    };
};

var CommentsViewModel = function (commentsRepository) {
    var self = this;
    self.CommentsRepository = commentsRepository;
    self.Comments = ko.observableArray([]);

    //self executing function to Get things started
    self.init = (function() {
        self.CommentsRepository.Get(function(comments) {
            self.Comments(comments);
        });
    })();
};

And we're done! To use this new component, we can use RenderAction

@* inside our Layout or another View *@
<article>
    <h1>@article.Name</h1>
    <p>main page content here blah blah blah</p>
    <p>this content is so interesting I bet people are gonna wanna comment about it</p>
</article>
@Html.RenderAction("Comments", "CommentsList", new { id = article.id })
like image 59
elwyn Avatar answered Sep 23 '22 02:09

elwyn