Using ASP.NET MVC ViewModels with Durandal without boilerplate - validation

I'm writing a small app based on John Papa's "Hot Towel" template, meaning MVC 4 and Durandal.
As someone with mostly MVC experience it seems most "correct" to use MVC's "Models" with their Data Annotations for validation.
I also don't want to write the viewmodels OR the validation twice, which seems a reasonable enough desire.
So far I've easily figured out how to use ko.mapping for loading the ViewModel and its data from the server side using JSON, and binding my form to it. Great. But what about the validation part? For the life of me I can't find a single solution for this on the internet, as I'm not interested in making use of Razor (I've seen some solutions using its HtmlHelpers).
At the moment, the best way I see is to transform the ViewModel objects on the server to a simpler JSON object using reflection, where these annotations will be represented as members. Like so:
public object TransformVM(object vm)
{
var properties = vm.GetType().GetProperties();
var result = new Dictionary<string,object>();
foreach (var p in properties)
{
var attributes = p.GetCustomAttributes(true);
var displayName = attributes.FirstOrDefault(a => a is DisplayNameAttribute) as DisplayNameAttribute;
result.Add(p.Name, new
{
value = p.GetValue(vm),
displayName = displayName != null ? displayName.DisplayName : ""
});
}
return result;
}
meaning instead of getting the view model as this json
{ Email: 'something#somewhat' }
I get a two-levelled object like
{ Email: { value : 'something#somewhat', required: true, displayName: 'e-mail' } }
and then using ko's binding this way:
<label data-bind="text: post.Email.displayName"></label>
<input type="text" data-bind=" value: post.Email.value" />
This of course means writing a "translation" for every single DataAnnotation I wish to use, which can get cumbersome.
So is this the right way or am I pitching myself into a hole too deep to get out of?

If you are going with the same architecture as John's HotTowel, why not use Breeze JS validations? Your model will have the basic validation rules from EF/database, then you can enhance using custom validators.
Read more here: http://www.breezejs.com/documentation/validation

Related

How do I bypass the limitations of what MVC-CORE controllers can pass to the view?

From what I've read, I'm supposed to be using ViewModels to populate my views in MVC, rather than the model directly. This should allow me to pass not just the contents of the model, but also other information such as login state, etc. to the view instead of using ViewBag or ViewData. I've followed the tutorials and I've had both a model and a viewmodel successfully sent to the view. The original problem I had was that I needed a paginated view, which is simple to do when passing a model alone, but becomes difficult when passing a viewmodel.
With a model of
public class Instructor {
public string forename { get; set; }
public string surname { get; set; }
}
and a viewmodel of
public class InstructorVM {
public Instructor Instructors { get; set; }
public string LoggedIn { get; set; }
}
I can create a paginated list of the instructors using the pure model Instructor but I can't pass InstructorVM to the view and paginate it as there are other properties that aren't required in the pagination LoggedIn cause issues. If I pass InstructorVM.Instructors to the view, I get the pagination, but don't get the LoggedIn and as this is just the model, I may has well have passed that through directly.
An alternative that was suggested was to convert/expand the viewmodel into a list or somesuch which would produce an object like this that gets passed to the view
instructor.forename = "dave", instructor.surname = "smith", LoggedIn="Hello brian"
instructor.forename = "alan", instructor.surname = "jones", LoggedIn="Hello brian"
instructor.forename = "paul", instructor.surname = "barns", LoggedIn="Hello brian"
where the LoggedIn value is repeated in every row and then retrieved in the row using Model[0].LoggedIn
Obviously, this problem is caused because you can only pass one object back from a method, either Instructor, InstructorVM, List<InstructorVM>, etc.
I'm trying to find out the best option to give me pagination (on part of the returned object) from a viewmodel while not replicating everything else in the viewmodel.
One suggestion was to use a JavaScript framework like React/Angular to break up the page into a more MVVM way of doing things, the problem with that being that despite looking for suggestions and reading 1001 "Best JS framework" lists via Google, they all assume I have already learned all of the frameworks and can thus pick the most suitable one from the options available.
When all I want to do is show a string and a paginated list from a viewmodel on a view. At this point I don't care how, I don't care if I have to learn a JS framework or if I can do it just using MVC core, but can someone tell me how to do this thing I could do quite simply in ASP.NET? If it's "use a JS framework" which one?
Thanks
I'm not exactly sure what the difficulty is here, as pagination and using a view model aren't factors that play on one another. Pagination is all about selecting a subset of items from a data store, which happens entirely in your initial query. For example, whereas you might originally have done something like:
var widgets = db.Widgets.ToList();
Instead you would do something like:
var widgets = db.Widgets.Skip((pageNumber - 1) * itemsPerPage).Take(itemsPerPage).ToList();
Using a view model is just a layer on top of this, where you then just map the queried data, no matter what it is onto instances of your view model:
var widgetViewModels = widgets.Select(w => new WidgetViewModel
{
...
});
If you're using a library like PagedList or similar, this behavior may not be immediately obvious, since the default implementation depends on having access to the queryset (in order to do the skip/take logic for you). However, PagedList, for example has StaticPagedList which allows you to create an IPagedList instance with an existing dataset:
var pagedWidgets = new StaticPagedList<WidgetViewModel>(widgetViewModels, pageNumber, itemsPerPage, totalItems);
There, the only part you'd be missing is totalItems, which is going to require an additional count query on the unfiltered queryset.
If you're using a different library, there should be some sort of similar functionality available. You'll just need to confer with the documentation.

In Meteor, where do I model my business rules?

Beginner question : I've worked through the Try Meteor tutorial. I've got fields in my HTML doc, backed by helper functions that reference collections, and BOOM --> the fields are updated when the data changes in the DB.
With the "Hide completed" checkbox, I've also seen data-binding to a session variable. The state of the checkbox is stored in the Session object by an event handler and BOOM --> the list view is updated "automatically" by its helper when this value changes. It seems a little odd to be assigning to a session object in a single page application.
Through all this, my js assigns nothing in global scope, I've created no objects, and I've mostly seen just pipeline code, getting values from one spot to another. The little conditional logic is sprayed about wherever it is needed.
THE QUESTION... Now I want to construct a model of my business data in javascript, modelling my business rules, and then bind html fields to this model. For example, I want to model a user, giving it an isVeryBusy property, and a rule that sets isVeryBusy=true if noTasks > 5. I want the property and the rule to be isolated in a "pure" business object, away from helpers, events, and the meteor user object. I want these business objects available everywhere, so I could make a restriction, say, to not assign tasks to users who are very busy, enforced on the server. I might also want a display rule to only display the first 100 chars of other peoples tasks if a user isVeryBusy. Where is the right place to create this user object, and how do I bind to it from my HTML?
You can (and probably should) use any package which allows you to attach a Schema to your models.
Have a look at:
https://github.com/aldeed/meteor-collection2
https://github.com/aldeed/meteor-simple-schema
By using a schema you can define fields, which are calculated based on other fields, see the autoValue property: https://github.com/aldeed/meteor-collection2#autovalue
Then you can do something like this:
// Schema definition of User
{
...,
isVeryBusy: {
type: Boolean,
autoValue: function() {
return this.tasks.length > 5;
}
},
...
}
For all your basic questions, I can strongly recommend to read the DiscoverMeteor Book (https://www.discovermeteor.com/). You can read it in like 1-2 days and it will explain all those basic questions in a really comprehensible way.
Best Regards,
There is a very good package to implement the solution you are looking for. It is created by David Burles and it's called "meteor-collection-helper". Here it the atmosphere link:
You should check the link to see the examples presented there but according to the description you could implement some of the functionality you mentioned like this:
// Define the collections
Clients = new Mongo.Collection('clients');
Tasks = new Mongo.Collection('tasks');
// Define the Clients collection helpers
Clients.helpers({
isVeryBusy: function(){
return this.tasks.length > 5;
}
});
// Now we can call it either on the client or on the server
if (Meteor.isClient){
var client = Clients.findOne({_id: 123});
if ( client.isVeryBusy() ) runSomeCode();
}
// Of course you can use them inside a Meteor Method.
Meteor.methods({
addTaskToClient: function(id, task){
var client = Clients.findOne({_id: id});
if (!client.isVeryBusy()){
task._client = id;
Tasks.insert(task, function(err, _id){
Clients.update({_id: client._id}, { $addToSet: { tasks: _id } });
});
}
}
});
// You can also refer to other collections inside the helpers
Tasks.helpers({
client: function(){
return Clients.findOne({_id: this._client});
}
});
You can see that inside the helper the context is the document transformed with all the methods you provided. Since Collections are ussually available to both the client and the server, you can access this functionality everywhere.
I hope this helps.

Server validation does not trigger when submitting with Ajax Asp Mvc3 with kendo + knockout

I have Asp Mvc3 app. In my view I am using kendoui + knockoutjs. I am handling the client side validation with kendo validator. I am very new to asp mvc3 and I cannot make my server side validation work.
This is my business object:
[Validator(typeof(FranchiseInfoValidator))]
public class FranchiseInfo
{
public string FullName { get; set; }
public string ShortName { get; set; }
}
}
I am using FluentValidation. This is the implementation of my validation rules:
public class FranchiseInfoValidator : AbstractValidator<FranchiseInfo>
{
public FranchiseInfoValidator()
{
RuleFor(franchiseInfo => franchiseInfo.FullName).NotEmpty();
RuleFor(franchiseInfo => franchiseInfo.ShortName).NotEmpty();
}
}
This is my view model:
public class FranchiseInfoViewModel
{
public string FullName { get; set; }
public string ShortName { get; set; }
}
This is my view strongly typed to FranchiseInfoViewModel:
#model MvcApplication2.Models.FranchiseInfoViewModel
<script src="../../Scripts/Details.js" type="text/javascript"></script>
<form id="franchiseForm" action="" style="font-family: Trebuchet MS, Verdana, Helvetica, Sans-Serif;">
<table>
<tr>
<td><label for="fullName">FullName:</label></td>
<td><input id="fullName" data-bind= "value: FullName" /></td>
</tr>
<tr>
<td><label for="shortName">ShortName:</label></td>
<td><input id="shortName" data-bind= "value: shortName" /></td>
</tr>
</table>
<button id="submit" class="k-button" data-bind="click: save" form="franchiseForm">Save Franchise</button>
</form>
On submit the form I am calling javascript function save:
$(function () {
save = function () {
// some logic
$.ajax({
url: "/franchise/SaveFranchise",
type: "POST",
data: { franchiseInfoViewModel: jsonData },
dataType: 'json',
success: function (data, textStatus, xhr) {
window.location.href = data.redirectToUrl;
}
});
}
});
On save I am submitting the data in json format and sending the data to the SaveFranchise Controller:
public ActionResult SaveFranchise(string franchiseInfoViewModel)
{
var franchiseInfoVM = JsonConvert.DeserializeObject<FranchiseInfoViewModel>(franchiseInfoViewModel);
if (!ModelState.IsValid)
{
// do some action
}
return View();
}
What I want to accomplish is on (!ModelState.IsValid) to return back to the view and show fluent validation error messages. For some reason, in my case ModelState.IsValid is always true.
As mentioned, I am very new to Asp Mvc 3. From the articles I have read, the examples are submitting the form to the server (without javascript) where there is binding and the server validation messages are returned to the view. But there the view is implemented with Razor and client side validation is done via jquery.
In my view I am using javascript view model with databindings ( kendoui + knockout). What should I do to do the server side validation in my case. Please help me. Thank You for your time and effort!
Short answer:
You should make use of model bindings in the controller action - doing so enables you to make use of ModelState.IsValid.
So your controller action:
public ActionResult SaveFranchise(string franchiseInfoViewModel)
.. should be changed into something like:
public ActionResult Create(FranchiseInfo franchiseInfo)
Be sure to post your form in a format that enables the model binder to instantiate an instance of the FranchiseInfo model. You can use JSON to do so, but I think it would be better (and simpler) if you just post the form using the standard action attribute on a form.
Elaborated answer:
One of the first things I would recommend, is that you use the model validation provided by the ASP.NET MVC framework including model bindings, model state, and data annotations for server side validation, as well as unobtrusive javascript for client side validation. Doing so would give you a way more DRY implementation with clear Separation of Concerns (SoC).
To get there - I suggest you first start taking a look at model bindings in ASP.NET MVC.
As stated in "The Features and Foibles of ASP.NET MVC Model Binding":
With model binding, controller actions can be focused on providing business value and avoid wasting time with mundane request mapping and parsing.
To see this, this MSDN article have a good definition of a model binder:
A model binder in MVC provides a simple way to map posted form values to a .NET Framework type and pass the type to an action method as a parameter. (...) Model binders are like type converters, because they can convert HTTP requests into objects that are passed to an action method.
With that abstraction in hand, you will quickly see that the signature of:
public ActionResult SaveFranchise(string franchiseInfoViewModel)
.. could be changed into something like:
public ActionResult Create(FranchiseInfo franchiseInfo)
... and at this point, things are already starting to look more clean because code like your custom data mapping:
var franchiseInfoVM = JsonConvert.DeserializeObject<FranchiseInfoViewModel>(franchiseInfoViewModel);
... can be removed.
In general, you should not need to serialize the form into JSON and post that - instead you should take advantage of the model binding in MVC and submit the form data the standard way ( by using the action attribute on the form element - well, the model binder is able to bind objects based on JSON, but I think you get my point.. ). Actually, ASP.NET MVC makes it really simple to generate a form for a model using Html.BeginForm - an example use of this is shown here.
So, now when you have refactored the view to make use of a form that posts its data without serializing the form data into JSON, you should take a look at the data annotations in MVC3.
As mentioned in this example, the System.ComponentModel.DataAnnotations namespace of .NET...
provides a built-in set of validation attributes that you can apply declaratively to any class or property. (...) The validation attributes specify behavior that you want to enforce on the model properties they are applied to.
One of the available annotations is the Required attribute - it defines that a particular model property must have a value. There are several other attributes like Range, MaxLength, RegularExpression, etc. An example use of some of the attributes could look like this:
public class Movie
{
public int ID { get; set; }
[Required(ErrorMessage = "Title is required")]
public string Title { get; set; }
[Required(ErrorMessage = "Date is required")]
public DateTime ReleaseDate { get; set; }
[Required(ErrorMessage = "Genre must be specified")]
public string Genre { get; set; }
[Required(ErrorMessage = "Price Required")]
[Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
}
( The example is borrowed from: http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/adding-validation-to-the-model )
Populating your models with data annotations makes it very easy for you to check if the model is valid or not, at the point when a given form has been submitted, and your are about the check the validity of the provided model in a given action - it is here ModelState.IsValid proves to be useful.
As mentioned in this post:
(...) The second Create method calls ModelState.IsValid to check whether the movie has any validation errors. Calling this method evaluates any validation attributes that have been applied to the object.
So at this point, you should be able to see that using data annotations to declare constraints/required behavior on your model will allow you to have a simple implementation where the logic is defined in the model and can easily be checked using ModelState.IsValid.
To expand a bit on this I can tell you a bit of my experience with model validation at server and client side in ASP.NET MVC3.
As you might already have guessed, my best experience with ASP.NET MVC3 when it comes to model validation is using data annotations and the built-in support for server-side validation through ModelState.IsValid and client-side validation through unobtrusive JavaScript.
For data annotations I use a combination of the built-in annotations, and the extensions provided by the data annotation extension project.
There are a couple of good things about using ASP.NET MVC3's support for unobtrusive client validation.
First of all, SoC is really easy to achieve, as the model validation logic is only defined one place - and that is in the model where it belongs.
Secondly you can get a more DRY implementation in your view ( avoid duplicated logic and get code that is easier to maintain ) and let the framework do the heavy lifting for you instead.
An example of how to use unobtrusive client side validation in ASP.NET MVC3 can be found here.
However, there are times when using the annotations might not seem that straigt forward - for instance when you want to validate properties such as credit cards and file extensions. In these cases where the need for logic calls for alternatives other than the basic annotations, I tend to use data annotation extensions ( unless I can target my application for .NET 4.5 which, for instance, has added a series of more attributes, like e.g. the FileExtensionAttribute ) as they provide a series of great annotation extensions that just works right out of the box ( there exist a data annotation extensions nuget package for MVC3 - so it is really simple to set up and start using ).
There are also special cases when you might have a property that depends on the state of your databse - e.g. if you want to check if a username already exists when a user is filling out a user registration form. In this scenario, the ASP.NET MVC3 remote validation annotation is your friend. As this MSDN article states:
ASP.NET MVC 3 provides a mechanism that can make a remote server call in order to validate a form field without posting the entire form to the server. This is useful when you have a field that cannot be validated on the client and is therefore likely to fail validation when the form is submitted.
And the nice thing about remote validation in ASP.NET MVC3 is, that it also is expressed using a data annotation ( as illustrated in this blogpost and in this MSDN article ).
So to sum up - using these framework features (imo) really gives you a serious range of tools to cover more or less any kind of model validation "the right way" in ASP.NET MVC3.

How to access objects inside a List, saved in a Session. ASP.NET MVC 3

So I have this code:
var list = new List<Carrito> {
new Carrito { ProductId = producto.ID , Cantidad = 1, PrecioUnitario = producto.Precio }
};
Session["list"] = list;
return View();
Then I load the view but I don't know how to print the the content that is inside the session. Any ideas?
This is the code I use inside the view but doesn't work:
#foreach(var item in (IEnumerable<object>)Session["list"] )
{
<p>#item.ProductId</p>
}
it's as simple as reading back the value from your session varable and cast it to the original type, then do whatever you want
example:
#{
if(Session["list"]!= null)
{
var listBackFromSession = (List<Carrito>)Session["list"];
// do what you want
}
}
My recommendation is to use the more elegant way of ViewBag.
a quote from official asp.net mvc website about Viewbag:
New "ViewBag" Property
MVC 2 controllers support a ViewData property that enables you to pass
data to a view template using a late-bound dictionary API. In MVC 3,
you can also use somewhat simpler syntax with the ViewBag property to
accomplish the same purpose. For example, instead of writing
ViewData["Message"]="text", you can write ViewBag.Message="text". You
do not need to define any strongly-typed classes to use the ViewBag
property. Because it is a dynamic property, you can instead just get
or set properties and it will resolve them dynamically at run time.
Internally, ViewBag properties are stored as name/value pairs in the
ViewData dictionary. (Note: in most pre-release versions of MVC 3, the
ViewBag property was named the ViewModel property.)
Further more, This is a good article to read about the different ways you have in MVC in order to preserve data: http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications
example:
var list = new List<Carrito> {
new Carrito { ProductId = producto.ID , Cantidad = 1, PrecioUnitario = producto.Precio }
};
// use ViewBag
ViewBag.myList = list;
then inside your view, read them back like this:
var myList = (List<Carrito>)ViewBag.myList;
// your code
You're doing MVC fundamentally wrong. In MVC, Views are there only to render a model. The logic of accessing that model should be implemented in controller, or in any other place, but not in the View itself.
Thus I recommend that you simply pass your list to the view, and make your view strongly-typed by including #model List<Carrito> at the top.

the best way to maintain html snippets in ASP.NET MVC to return in ajax calls

i'm looking for a best practices type answer here. basically i have a very chatty application which will be returning bits of data to the client very often. the bits of data returned eventually will end up being html added dynamically to the dom. so i'm trying to choose between the following 2 ways:
return just json data, create the html on the client side using jquery and possibly jquery templates
return the actual html that is build on the server side
i would like to make the choice that is most easily maintained. that is, i want the best way that will allow me to make updates to the html snippets very often.
i'm actually looking for a way to do #2 using ASP MVC partial views and want the ability to use string formatting. essentially i'm looking to make a call like this:
string sHtml = string.Format(GetNewTradeHtml(), "GOOG", "100", "635.50");
and I want GetNewTradeHtml() to actually get the html from a ASP MVC view instead of a string constant that might look like:
const string cNewTradeHtml = "<li><span>Symbol: {0}</span><span>Qty: {1}</span><span>Price: {2}</span></li>";
the string constants seems to be a popular way to do these kinds of things and i hate maintaining those...
basically i think i'm looking for a way to manage view several view templates that i can call ToString() on and get the raw html and use string formatting on it. and i'm hoping there is a suggested way to solve my particular problem natively in ASP MVC (without some hack). but perhaps (unfortunately) the string constants + string.format is the best way to maintain server side dynamic html...
UPDATE:
here's what i've learned since i've posted this question:
there are LOTS of posts here on SO about rendering a view into a string. a lot of different ways, some work with different versions of MVC some don't. some are cleaner than others, some are pretty heavy... ALL of which are normally some type of solution that require a controller context. so in most cases the solutions work great as responses to requests. but for my case, i need to do it outside of the context of a controller so now i need to either mock the controller or make a bunch of fake objects, neither of which i really want to deal with.
so i've determined that there is actually NO easy way to render a razor partial into its string representation without using a controller in a response. they really need to make an easy way to do this without mocking up controller context and request objects.
What are Views in asp.net mvc? They are just html templates, nothing more. They take model and replace template placeholders with model values. And indeed there's no more natural way to render html in asp.net mvc than using Views.
First, declare your view model
public class NewTradeViewModel
{
public string Symbol { get; set; }
public decimal Quantity { get; set; }
public decimal Price { get; set; }
}
than your controller action
public ViewResult GetNewTrade()
{
NewTradeViewModel model = new NewTradeViewModel;
model.Symbol = "GOOG";
model.Quantity = "100";
model.Price = 635.50m;
// PartialView, as you want just html snippets, not full layouts with master pages, etc
return PartialView("TemplateViewName", model);
}
and the very ordinary view - you may have any number of these, just change controller action to return specific one
#model NewTradeViewModel
<li><span>Symbol: #Model.Symbol</span><span>Qty: #Model.Quantity</span><span>Price: #Model.Price</span></li>
Since you mentioned that your app was "chatty" you should probably consider returning Json and rendering on the client side with a template engine.
This is really a toss up though because it looks like your snippets are pretty small.
If you do go with a sending JSON back and forth, I can recommend jquery templates or mustache
backbone.js can also help you better organize your client side components. It is pretty easy to get up and running with it. By default it works with jquery templates, but you can also plug in other templates if you like.
Here is a simple approach to storing templates in separate files, http://encosia.com/using-external-templates-with-jquery-templates/
ijjo,
Just looked at your question again and notice that you are referring to returning the html partialview as a string. there are loads of references here on SO to this type od function, but below is my version taken from an 'old' mvc app that's still in production. without further ado, it's an extension method that hooks into the controller:
public static class ExtensionMethods
{
public static string RenderPartialToString(this ControllerBase controller, string partialName, object model)
{
var vd = new ViewDataDictionary(controller.ViewData);
var vp = new ViewPage
{
ViewData = vd,
ViewContext = new ViewContext(),
Url = new UrlHelper(controller.ControllerContext.RequestContext)
};
ViewEngineResult result = ViewEngines
.Engines
.FindPartialView(controller.ControllerContext, partialName);
if (result.View == null)
{
throw new InvalidOperationException(
string.Format("The partial view '{0}' could not be found", partialName));
}
var partialPath = ((WebFormView)result.View).ViewPath;
vp.ViewData.Model = model;
Control control = vp.LoadControl(partialPath);
vp.Controls.Add(control);
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
return sb.ToString();
}
}
usage (as per archil's example above):
public ViewResult GetNewTrade()
{
NewTradeViewModel model = new NewTradeViewModel;
model.Symbol = "GOOG";
model.Quantity = "100";
model.Price = 635.50m;
// PartialView, as you want just html snippets, not full layouts with master pages, etc
return this.RenderPartialToString("TemplateViewName", model);
}
good luck and enjoy...

Resources