How do knockout.js and ASP.net MVC 3 co-exist ?

2,888 views
Skip to first unread message

graphicsxp

unread,
Jun 7, 2011, 10:30:56 AM6/7/11
to KnockoutJS
Hi,

I'm fairly new to knockout.js but I definitely see how useful it could
be for making my code neater.

I have a question though regarding how it works with MVC 3.

In MVC 3, you would bind the controls in the View to the Model. Then
the data in the view would be validated against the validation
attributes of the model ( RequiredAttribute, RemoteAttribute, etc...)

But it seems that if I use knockout, I'm no longer binding my controls
to the model, but instead I'm binding the controls to a javascript
model, which is a JSON representation of my server-side object model.
Then validation would be done by knockout.

Are my assumptions correct ?

Does anyone have an example of knockout used with MVC 3 ?

Stacey Thornton

unread,
Jun 7, 2011, 10:51:53 AM6/7/11
to KnockoutJS
Right now, there is A LOT of manual work that has to go into it. There
are some MVC Helpers out there, but I personally found them very
confusing (though far above my experience level)

You can still use your helpers to bind to your model, but you need to
design a way to get the JSON data back to the Controller. I found the
following to work well for me. (I used the Newtonsoft JSON.NET
library, but the standard Javascript Serializer can work, but I found
it to be more difficult)

http://pastie.org/2032652

Please pay the closest attention to the last section of the pastie
document. If you are POSTING, and ONLY posting (not ajax), you must
declare the name of your Controller expected parameter, and ko.toJS on
the viewModel in question. This (from what I have observabled),
unwraps everything into solid Json data.

Stacey Thornton

unread,
Jun 7, 2011, 10:56:17 AM6/7/11
to KnockoutJS
For some reason, my first response did not go through...

You can still model bind to the Controller, the trick is to set up a
binder to interpret it a bit more clearly. The kind of JSON that
Knockout sends gets 'encoded' in a way that MVC does not natively
expect. Here is how I did it.

http://pastie.org/2032652

Pay close attention to the first section, and the last section. I used
Newtonsoft Json.NET library for the 'ToJson' extender for my Models,
but the standard Javascript Serializer built into .NET
(System.Web.Extensions.dll) does work, but I found it to be
troublesome when it came to Enums.

You MUST declare the NAME OF THE PARAMETER that the controller
expects, and ko.toJS on the javascript viewModel when posting back.
This is a little easier if you use the $.ajax method instead of
postJson.

postJson takes your viewModel and compiles it into JSON, then creates
a new <form> element on the page and adds the JSON as an input field
and posts it using the action method of the original form. This causes
a lot of strange behavior in the MVC Framework. The ModelBinder given
there assists in eliminating this.

Stacey Thornton

unread,
Jun 7, 2011, 11:05:20 AM6/7/11
to KnockoutJS
Oops, Sorry for the double post. I guess my internet is so laggy at
work that it didn't alert me of the success. Anyway, I updated the
pastie (http://pastie.org/2032652) to include more of my code. It is
a lot less difficult than it looks, once you get the concept down.
Keep in mind that 90% of this is code you write anyway for ASP.NET
MVC, so don't let that dissuade you too much.

Jeff Sheldon

unread,
Jun 7, 2011, 11:35:07 AM6/7/11
to knock...@googlegroups.com
Honestly, I'm still figuring out the way I'm most comfortable with.   I've been watching Stacey's posts because she seems to be going a different path than I have, so I'm curious how that will work out.

I think I took a completely different approach, and now I'm kind of stuck.  Regardless, here's how I've done it so far.
  • I stripped out all of the ASP.NET MVC stuff and went back to HTML roots.  (No @model, no HTML Helpers, just straight HTML)
  • I created a Common.js to house all of my custom model binders and methods to interact with the site controllers via Ajax.
  • I then create a page specific JavaScript file (As Necessary) to handle view model binding on at a page level.   This is accomplished by calling something like this in my view:

    @section scripts {
        @Url.CombresLink("somePageJs")

        <script type="text/javascript">
            $(document).ready(function () {
                var o = @Html.Raw(Json.Encode(ViewBag.PageSpecificData))
                myPageJS.Init(@Model.Quote.ID, o);
            });
        </script>
    }

There are a couple of things I'm trying to work out:
  • A way to setup some kind of inheritance model so that my Common file houses the common fromJS/applyBindings, but the page specific JavaScript handles the customizing of the model.  (Adding functions, and dependents etc)
  • Dialog Windows.   Don't even know where to start with this one compared to how I was doing it.
  • Validation.  So many people are attacking this problem that I haven't worked on it much yet, instead focusing on the rest.

Hope that helps to give you another point of view. :)

-Jeff

Stacey Thornton

unread,
Jun 7, 2011, 11:40:48 AM6/7/11
to KnockoutJS
Just remember the only 'wrong' answer is one that doesn't actually run
and compile correctly. Jeff's design seems a lot faster and simpler to
use, but I prefer the helpers because of the consistency concept.

In response to Jeff's post, my solution was designed to specifically
resolve the exact issues you claim you are trying to work out. But
your approach is a lot simpler and quicker to get running.

Maybe one day we'll have a solution that does it all. I'm actually
working on that... I have a prototype done that looks like this ...

@using(Html.Template("__property_template")){
<text>${ Id }</text>
@Html.TextBoxFor( model => model.Name , new
{ @data_bind="value: Name" })
}

And I am working on improving it to be more robust and aware of the
model.

Stacey Thornton

unread,
Jun 7, 2011, 11:46:39 AM6/7/11
to KnockoutJS
Jeff, for Dialog Windows, what I'm doing is pulling in a Partial View
through a Controller Action, and passing in an empty model. I create
an instance of the viewModel (JS version) and bind it to the new
partial view (loaded using $.ajax and then $(selector).load(href)),
and instead of trying to couple the dialog to the original view model,
I just push the result to the original viewModel before disposing of
the dialog, Like this... (http://pastie.org/2032915)

This isnt' a good example code, it doesn't run. It did run once, but I
took it and began to work on a cleaner solution that isn't finished
yet, but it demonstrates the core idea of what I am doing.

Jeff Sheldon

unread,
Jun 7, 2011, 12:06:26 PM6/7/11
to knock...@googlegroups.com
Still waiting for the infrastructure team to unlock access to pastie.org so I'll have to get back to you on that.

Pre-Knockout I think I did it the same way, I made an ajax call which returned a render partial.   I threw the results into a div, then called $(div).dialog();

The problem I'm running into now is the dialog could be a blank (Adding) or not blank (Editing) and it has a DropDown/Combobox (Currently Telerik) on it.  And wiring all that together with knockout without passing around the combobox's available items is what I'm figuring out (about 2000 items I think?)


Jeff Sheldon

unread,
Jun 7, 2011, 12:08:39 PM6/7/11
to knock...@googlegroups.com
Oh, and I wasn't trying to hijack this thread with my problems, just trying to offer a different point of view.  

Also Stacey, I don't know about my solution being faster.   Took me a few weeks of head banging, and I went this route simply to boil it down to the least common denominator.   I think I'll put helper methods and such back in to clean it up some, just don't need that in there when trying to figure out a big picture solution. :)

Stacey Thornton

unread,
Jun 7, 2011, 4:41:22 PM6/7/11
to KnockoutJS
It is alright. You did not hijack the thread, you just presented other
things to look at.

I am writing some helpers right now, I think I will post them to this
forum when I am done and see if anyone else likes the idea.

graphicsxp

unread,
Jun 8, 2011, 2:05:21 AM6/8/11
to KnockoutJS
I just woke up this morning and I've been through your replies. I have
to say it was a lot to grap for me :p But thank you for taking the
time to reply.

Stacey, your code seems to be doing exactely what I was trying to
achieve. It's such a shame that it doesn't work out of the box, I
think Microsoft should integrate knockout within its next version of
MVC, that'd make things a lot easier.

I'm trying to re-use your code in one of my sample apps in order to
get my head around it. But I'm having trouble using the extension
method you've created for serializing an object to JSON. I've created
the extension method at the root of my web app, but when I try to call
it on my Model in the view, the extension is simply not found.
It could be something silly I'm missing but I can't figure it out and
I was wondering if you'd have an idea what it could be ?

Thank you

On Jun 7, 10:41 pm, Stacey Thornton <stacey.cielia.l...@gmail.com>
wrote:

graphicsxp

unread,
Jun 8, 2011, 2:13:04 AM6/8/11
to KnockoutJS
Like I said, that was something silly... I did not use the namespace
System.Web.Mvc for my extension method....

Ok, I'm going to try and finish my sample and I'll get back to you for
a feedback once it's working (or not ;))

graphicsxp

unread,
Jun 8, 2011, 7:35:24 AM6/8/11
to KnockoutJS
Ok, I've created my prototype using knockout and mvc, based on
Stacey's code and it works just great.

However I can't figure out how to use it in the following scenario. In
this case the Model is a List of Restaurant and in my view I do the
following:

@foreach (var item in Model)
{
<tr>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.Id },
new AjaxOptions { HttpMethod = "GET" }) |
@Html.ActionLink("Details", "Details", new { id =
item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id =
item.Id })
</td>
<td>
@item.Name
</td>
<td>
@item.Location
</td>
<td>
@item.Rating
</td>
</tr>
}

With what should I replace @item.Name ?

And what should I replace this line with ?

var viewModel = new MvcApp.Restaurant( ko.toJS(serverData) );

Do I then need a separate js viewModel when my model is a list of
Restaurant ?

Stacey Thornton

unread,
Jun 8, 2011, 8:41:40 AM6/8/11
to KnockoutJS
It won't work this way.

Your @foreach loop is a C# statement, it has nothing to do with MVC or
even HTML or Knockout. It will loop through a collection.

You would instead want to write it like this ...


<div data-bind='template { name: "resturauntTemplate", foreach:
resturaunt }'></div>

<script id="resturauntTemplate" type="text/html">
<div data-bind="text: Name"></div>
</script>

Or something similar. This is where Knockout and MVC do not live
together. And it is the problem I am working on a pseudo-remedy for.

As for putting things in a separate .js file. No, you do not need to.
I do it because I use Telerik JustCode, and .js files get intellisense
support for me, so it is convenient. There is no reason why you have
to, it is a developer-choice.

I actually think the fact that ASP.NET MVC, Unaltered, can work with
Knockout with the simple tweaks a developer like me who doesn't even
understand it is pretty amazing. The problem isn't MVC, in truth. It
is the HTML Helpers that do not work with Knockout. ASP.NET MVC will
accept JSON data on a controller, but it expects that to be sent as a
raw object. The Knockout ko.utils.postJson() methods doesn't do this.
It constructs a form and inserts the JSON as an input text and submits
the form, which causes the ASP.NET framework to insert strange hash
characters into it (thus the need for the extra model binder).

If you were to do ...

$.ajax({
url: /create,
type: 'json',
// ... etc.
data: ko.toJS(viewModel)
})

It would actually model bind out of the box without the need for extra
interpretation.

Stacey Thornton

unread,
Jun 8, 2011, 8:47:52 AM6/8/11
to KnockoutJS
As for your foreach loop, it would be great if it worked that way. But
here is what will happen if you do it that way.

User Hits the Controller
-> Controller Renders View with View Model (C#)
-> -> foreach loop renders
-> -> -> for each item in the collection, something is drawn to the
screen.
-> -> -> -> Somehow, your data is changed with knockout
-> -> -> -> -> Your data is updated behind the scenes, but the Model
in the View is not.
-> -> -> -> -> -> Data on the View and Actual Data are now out of
Synchronization.

Stacey Thornton

unread,
Jun 8, 2011, 9:07:05 AM6/8/11
to KnockoutJS
As for your other query about what to replace the 'var viewModel = new
MvcApp.Restaurant( ko.toJS(serverData) ); ' with, that's where you
need to get a bit more creative.

If MvcApp.Restaurant is a javascript object that can take the initial
data, then you don't actually need to change it for anything. In
otherwords, in my code, I do this...

// attempt to bind any data we received from the server
var serverData = @(new MvcHtmlString(Model.ToJson()));
// pass any data we received into a new viewModel
var viewModel = new Lightcast.Aspect( ko.toJS(serverData) );

Lightcast.Aspect is an object of its own to Javascript, and it accepts
a single variable. This variable is assumed to be a Javascript object
with its own properties.

If there is no data passed into my View, then 'serverData' is just
"{}", and it is basically an empty Javascript object. So calling 'new
Lightcast.Aspect({})' is the same thing. Each field tries to populate,
and finds nothing to be populated with, so it defaults to an empty
observable or observableArray(). This way, this same code works for
both Display views and Input Views.

So the truth is you don't really need to change that line!

On that note, let me show you what I am working on. You might like it.

http://pastie.org/2037470

This is a replacement of my "Sample View" from my original pastie
using some HtmlTag code I wrote inspired by FubuMVC (It is not
FubuMVC, I just took his code and I _very literally copy and pasted a
lot of it_. I do not, I repeat I do not take any kind of credit for
the idea or even most of the implementation. I just re-wrote it to
suit my specific tastes and to fit the MVC Views)

Notice how I define my Html Tags with a fluent syntax.

Html.EnumTagFor( model => model.Classification, tag => {
tag.Name("Classification")
.Id("Classification")
.Bind("value", "Classification")
.Validate();
})

I am still using the Html Helpers. I am still getting the Html5
Validation Syntax in the output HTML, but now I can very specifically
fine-tune them without that long, and rather ambiguous
IDictionary<string, object> going on. It's kind of funny that I would
wand more code over less, but sometimes more clean code is better than
less dirty code -- if that makes any sense. I've included a property
called "Bind". This allows you to define it an infinite number of
times, and it cascades all of them into a single data-bind="property:
value, property2: value2, etc: etcValue" attribute on the Html (thus
the different bindings that Knockout supports).

You can see an example of that on the Text Box.

@Html.TagFor( model => model.Name, tag => {
tag.Is("input")
.Type("text")
.Name("Name")
.Id("Name")
.Attribute("size", "45")
.Bind("value", "Name")
.Bind("valueUpdate", "'change, blur'")
.Validate();
})

With a fluent syntax, I just tell it how I want my HTML to look.
However then you'll be confused by the Markdown Editor. This uses
Markdown Deep (http://www.toptensoftware.com/markdowndeep/) to render
a Markdown Editor to the screen similar to what you see on
Stackoverflow. I am especially proud of this one..

@Html.HtmlEditorFor( model => model.Description , editor => {
editor
.Id("Description")
.Name("Description")
.Attribute("cols", "50")
.Attribute("rows", "10")
.Bind("value", "Description")
.Validate();
}, "__preview_aspect")

The Extension Method that defines this looks like this...

http://pastie.org/2037489

Notice how I am almost writing the same syntax in my helper as I would
in my View, but it is still fluent like I am doing Fluent nHibernate
or Entity Framework CodeFirst.

htmlEditor
.Class("mdd_editor")
.Attribute("data-mdd-preview", String.Format("#{0}", preview));

htmlEditor.Before(htmlToolbar => {
htmlToolbar.Is("div").Class("mdd_toolbar");
})
.After(htmlResizer => {
htmlResizer
.Is("div")
.Class("mdd_resizer")
.After(htmlPreview => {
htmlPreview.Is("div").Id(preview);
});
});

It draws out tag Hierarchy using TagBuilder (I went with TagBuilder
instead of HtmlTextWriter. Still debating on the two. I had a lot of
trouble with Opening/Closing Tag issues using HtmlTextWriter, whereas
TagBuilder seemed 'smart' and could figure out self closing tags on
its own).

So this will actually draw out the syntax

<div class="mdd_toolbar"></div>
<textarea class="mdd_editor" data-mdd-preview="#__preview_aspect"
rows="10" cols="50" ></textarea>
<div class="mdd_resizer"></div>
<div id="__preview_aspect"></div>

Which can seamlessly plugin to Markdown Deep.


Then the last part of the first pastie, my Template Helper.

<div class="clearfix right">
<button data-bind="click: open">New Property</button>
<button type="submit">Submit</button>
</div>
<div data-bind='template: { name: "__property_template", foreach:
Properties }'></div>

@using(Html.Template("__property_template")){
<text>${ Id }</text>
}

This one does exactly what it looks like it does. It writes out ...

<script id="__property_template" type="text/html">
{any content I want goes in here}
</script>

And I am looking at writing another Tag Building Helper for actually
creating the <div data-bind='template ... section where it would look
like ..

@Html.Binding( template => {
template.Name("__property_template")
.ForEach("Properties")
.FromViewModel("viewModel")
.Is("div")
})

But I'm not sure yet. Let me know if you think this would be at all
useful to you, I would be happy to paste my entire source code up so
you can use it. The HtmlTags actually work and I am using them right
now in a real world project.

On Jun 8, 7:47 am, Stacey Thornton <stacey.cielia.l...@gmail.com>
wrote:

Stacey Thornton

unread,
Jun 8, 2011, 9:18:00 AM6/8/11
to KnockoutJS
Actually, I'll just post it here anyway for you to see.

http://pastie.org/2037517

This is two files in one pastie, so don't make the mistake of doing a
Ctrl+A and pasting it into a .cs file.

The namespaces are System.Web.Mvc.Html simply so that you have to do
NOTHING to make them just 'work' in your MVC project. But include
these two files (I call them HtmlTags.cs and HtmlTagExtensions.cs) and
you will enable this fluent syntax capability. It is still fresh so it
may be bug prone ,but it may help you out a bit since you seem to be a
lot like me (prefer MVC Helpers over raw HTML).

Once I understand knockout better, and once I have tested these
helpers more, I might publish the entire article. I was inspired by
the original "HtmlTags" from this article (http://lostechies.com/
joshuaflanagan/2011/05/22/announcing-htmltags-1-0/) and I downloaded
his code and found it was more for ASP.NET Web Forms 4.0, so I wanted
to use it in MVC. I didn't like some of the implementation because I
am completely and utterly insane, and like obsessive compulsive
documentation, so I went totally nuts on Dr. Pepper and re-wrote parts
of it from the ground up just to try and make sense of what I was
reading, and threw them into extension methods for rendering to
MvcHtmlString.

You could achieve them without using the Fluent Syntax if you wanted,
but then you would need to call a "ToMvcHtml()" method or some such
that returned them as an MvcHtmlString. I thought the fluent syntax
looked better.

The second HtmlTags class, HtmlTags<TModel, TProperty> is a stand-in
class that I had a lot of trouble with. There are some cases where you
need to be aware of the model, but you can't always. (in other words,
what if I want to add Html Tags for a Model that is not part of my
present View Model? Which will commonly be the case in Templates I put
on another View Page). That is where the "Extender" property on the
HtmlTags class comes into play.

Anything that needs to work on the Model level, place a Virtual Method
in the HtmlTags class, and then override it in the HtmlTags<TModel,
TProperty> class, and in the base class, put return IsExtended ?
Extender.MethodIWantToCall() : this;

Whenever an HtmlTag is created, the Extender is set to the instance of
itself. So if you create it in a Strongly Typed View, it will be an
HtmlTag<TModel, TProperty>, but if you just create a random Html Tag
floating out in space that doesn't reference a Model Property, It'll
just be a normal HtmlTag. So now you can add Html5 Validation for Non-
Present Models using the "ValidationFor<TModel>" extension like this..

@Html.Tag( tag => {
tag.Is("input")
.Type("text")
.ValidationFor<AnotherViewModel>( model => model.Name );
})

I'm still trying to learn a lot of this, it is very new to me. But so
far it is actually working. I'd love some community feedback though.

On Jun 8, 8:07 am, Stacey Thornton <stacey.cielia.l...@gmail.com>

Andrew Booth

unread,
Jun 8, 2011, 9:49:12 AM6/8/11
to knock...@googlegroups.com
Hi


To keep a project simple, you could do the paged list of Restaurants using a standard Razor view, then use KnockoutJs for the Details/Edit views, then build out the AJAX functionality from there.

Andy

Stacey Thornton

unread,
Jun 8, 2011, 9:57:43 AM6/8/11
to KnockoutJS
This is a really good example. The problem I ran into with this was
when the primary model required validation, and there were secondary
child objects that had to be validated. (In the case of my specific
example, Aspect -> IList<Property> Properties). Refreshing the view
caused problems because it was difficult to enforce 'finishing' the
initial model's data before attaching children to it.

Jeff Sheldon

unread,
Jun 8, 2011, 11:02:29 AM6/8/11
to knock...@googlegroups.com
Very nice.   I saw the same HtmlTag code a ways back when I was looking into FubuMvc, and had very similar thoughts.   I considered modifying it as well, but you apparently have more Dr. Pepper than I do. :)

Stacey Thornton

unread,
Jun 8, 2011, 2:24:09 PM6/8/11
to KnockoutJS
The truth is that it is a bit overkill. I would be a lot faster just
writing the HTML by hand, but the fact that the MVC View Models can
hold that validation data, that is what keeps drawing me back to the
Helpers.

I think a lot of us MVC developers are not so much irritated by
knockout as much as we are the fact that using parts of it takes away
many of the tools that Visual Studio provides that makes our lives
easier. I had a debate with a co-worker this morning on this very
subject who tried to claim that utilizing the IDE made me less of a
programmer than he was, so I promptly asked him why he was using an
electric drill to take apart the PC chasis he was repairing.

I am still waiting on his answer to that question, but I think if
Visual Studio made working with mixed Javascript/HTML/C# a bit more
convenient, it would be a lot less confusing overall.

CodeFuzion

unread,
Aug 5, 2011, 10:27:23 AM8/5/11
to KnockoutJS
Your code you got and modified rox! I have made more modifications to
it to auto set the ID/Name when the user does not provide it (in my
case, I always want a input, select, textarea to always have an ID/
Name. I also very much love the Templating approach you use, as it
gives us intellisense and syntax highlighting for templates.

I added the following Extension Methods (and modified one I saw you
post on Stack Overflow to fix the Date issue for Interpret):
public const string JsonDatePattern = @"""\\/Date\((.*?)\)\
\/""";

public static MvcHtmlString Interpret<TModel>(this HtmlHelper
html, TModel model) {
var result = model.ToJson();
return new
MvcHtmlString(FixDateForJavascriptObject(result));
}

public static MvcHtmlString InterpretJson<TModel>(this
HtmlHelper html, TModel model) {
var result = model.ToJson();
return new MvcHtmlString(result);
}

private static string FixDateForJavascriptObject(string input)
{
var regex = new Regex(JsonDatePattern);
input = regex.Replace(input, @"new Date($1)");
return input;
}

public static string ToJson(this object item) {
return new JavaScriptSerializer().Serialize(item);
}

public static string NameFor<TModel, TProperty>(this
HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>>
expression) {
return
html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression));
}

public static string IdFor<TModel, TProperty>(this
HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>>
expression) {
return HtmlHelper.GenerateIdFromName(NameFor(html,
expression));
}


I utilize the NameFor and IdFor in a virtual method that the
HtmlTag<TModel, TProperty> implements to gaurentee that a ID/Name is
provided for input, selects, and textareas. This override is used in
ToHtmlString before Attributes are Merged.

public string ToHtmlString() {
//....
AddAdditionalAttributes(Attributes);
// build the html attributes for the tag.
htmlTag.MergeAttributes(Attributes);
//....
}

public override void
AddAdditionalAttributes(IDictionary<string, string> attributes) {
base.AddAdditionalAttributes(attributes);
var tagType = Tag.ToLower();
if (tagType == "input" || tagType == "select" || tagType
== "textarea") {
if (!attributes.ContainsKey("name")) {
attributes.Add("name",
((HtmlHelper<TModel>)HtmlHelper).NameFor(Model));
}
if (!attributes.ContainsKey("id")) {
attributes.Add("id",
((HtmlHelper<TModel>)HtmlHelper).IdFor(Model));
}
}
}

Thanks for you post man! It brought up Knockout, MVC3, Unobtrusive
Validation up a notch. Especially utilizing CoffeeScript to make class
creation in javascript simple to make a class based viewmodel.
-Corey Gaudin

CodeFuzion

unread,
Aug 5, 2011, 11:07:02 AM8/5/11
to KnockoutJS
Stacey,

I added a Pastie to show you how my minimal changes work. I really
like how close to Silverlight Binding it seems, especially with the
Client Side Viewmodel and cleanness of CoffeeScript this came out. I
posted the KnockoutHelpers minimally edited, as well as an example
Controller, View, and Javascript ViewModel.

http://pastie.org/private/ws4ngqqhstjfbmavsmfpw

Thanks again,
Corey J Gaudin

Stacey Thornton

unread,
Aug 5, 2011, 11:35:12 AM8/5/11
to KnockoutJS
I am really glad that you found it useful! I actually have an updated
version that fixes an issue with validation binding outside of the
model context, I will try to put it up on a gist sometime today or
tomorrow for you. It also solves an issue with model binding in the
lambda notation that doesn't bind to knockout observables.

CodeFuzion

unread,
Aug 8, 2011, 9:21:15 AM8/8/11
to KnockoutJS
Look forward to the revision Stacey. Thanks again for posting this in
the public and sharing this goodness!

On Aug 5, 10:35 am, Stacey Thornton <stacey.cielia.l...@gmail.com>
wrote:

Stacey Thornton

unread,
Aug 8, 2011, 10:09:06 AM8/8/11
to KnockoutJS
Sorry I took so long. This is the revised Validation method for the
HtmlExtensions.cs file.

https://gist.github.com/1131775

/// <summary>
/// Attempts to attach validation data from metadata of a given
model type, even if the model does not have an instance.
/// </summary>
/// <typeparam name="TModel">
/// The type of the model to get validation for.
/// </typeparam>
/// <param name="htmlTag">
/// The <see cref="System.Web.Mvc.Html.HtmlTag"/> that will have the
validation attached to it.
/// </param>
/// <param name="model">
/// The Lambda Expression that defines the model.
/// </param>
/// <returns>
/// The original <see cref="System.Web.Mvc.Html.HtmlTag"/> with
validation attached.
/// </returns>
public static HtmlTag ValidationFor<TModel>(this HtmlTag htmlTag,
Expression<Func<TModel, object>> model) {
// assume we are not inside of a strongly typed view, so always
create a new form context
htmlTag.HtmlHelper.ViewContext.FormContext = new FormContext();

// get the field name of the model property
string fieldName = ExpressionHelper.GetExpressionText(model);

// decompile the model metadata so that we can have access to the
attributes.
ModelMetadata metadata =
ModelMetadataProviders.Current.GetMetadataForProperty(null,
typeof(TModel), fieldName);

// retrieve the unobtrusive validation attributes
var validationAttributes =
htmlTag.HtmlHelper.GetUnobtrusiveValidationAttributes(fieldName,
metadata);

// set the validation attributes on the tag
htmlTag.SetValidation(validationAttributes);

// return the tag that had validation appended
return htmlTag;
}
/// <summary>
/// Gets the value of the object of an expression.
/// </summary>
/// <typeparam name="TModel">The model of the helper.</typeparam>
/// <typeparam name="TProperty">The property that the expression is
evaluating.</typeparam>
/// <param name="htmlHelper">The HTML helper instance that this
method extends.</param>
/// <param name="expression">The expression</param>
/// <returns></returns>
public static TProperty GetValue<TModel, TProperty>(this
HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>>
expression) where TModel : class {
TModel model = htmlHelper.ViewData.Model;
if (model == null) {
return default(TProperty);
}
Func<TModel, TProperty> func = expression.Compile();
return func(model);
}
I realize it isn't a huge deal different, but I did a lot of testing
and tweaking and made some key changes.

1. This creates a new FormContext.
- at first, I thought this was a bad idea, but the problem is that the
old one tried to use an existing form context. I discovered that
ASP.NET MVC binds certain attributes to the FormContext that the
Helper runs in, and so I was getting improper validation data that
way. This method goes around that.

2. I changed the Metadata Provider to not rely on the dynamic keyword.
That was just getting too 'iffy' for me and even a plain object is a
bit less dangerous. I found this syntax to be less intrusive, and a
bit easier to read, and more reliable in my testing.

3. The new method isn't so much 'new' as it is migrated. This is
actually an internal ASP.NET MVC method that really should be public
for extensibility. I've taken it and made an abstraction. But this
will, theoretically, let you pass in a Func<TModel, TProperty> and
retrieve not the field but the value. I ran into this when I tried to
use my "markdowndeep" editor extension method on a model that didn't
bind to Knockout. The textarea would keep coming up blank, and I
realized that nothing was actually binding it to the original model. I
am still working on it a bit, and I haven't had a lot of need for it.
It is really only neccessary in a situation where you use the tags to
build a nested HtmlTag, such as my HtmlEditorFor extension.

when I get a bit of a break from work, I will take an in depth look at
your changes and see if I can adapt my own structure to better
accommodate it.

jo...@brightshore.com

unread,
Oct 31, 2011, 10:37:09 AM10/31/11
to knock...@googlegroups.com
This is fantastic code.  I stumbled on this conversation while googling for htmlhelpers to use with Knockout.  Can I suggest making a full blown project on Git? 
 
I'm like you, I like the idea of knockout, but don't want to give up VS Intellisense and the built in validation.  These helpers seem like a much better way to go.
 
I would like to make some more extensions to shorten what needs to go in the view even further (like TextBoxTagFor) and then contribute this code back.  It'd be easiest if we had this somewhere all together.
 
What do you think?
 
John
 

Stacey

unread,
Oct 31, 2011, 1:23:32 PM10/31/11
to knock...@googlegroups.com
Once I get some free time not related to the job I am doing, I will try to establish it on GitHub, sure.
Reply all
Reply to author
Forward
0 new messages