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?
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 })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With