I have a partial view which outputs a list of editors. Each editor in the list has a delete link, which is called using #Ajax.actionlink, like this:
if(Model.Any()){
<ul>
#foreach (var editor in Model)
{
<li>#editor.firstName #editor.lastName, #editor.emailAddress (#Ajax.ActionLink("Delete", "DeleteEditor", "EditorSurface", new { editorId = editor.id }, new AjaxOptions()))</li>
}
</ul>
}
I've included #using System.Web.Optimization; in my view and am calling the jquery unobtrusive ajax script from my view too:
#Scripts.Render("~/scripts/jquery.unobtrusive-ajax.min.js")
The issue is that when I view source, the rendered mark-up for my editor list items is like this:
<li>John Smith, johnsmith#example.com (Delete)</li>
I was a little disappointed to see the inline JavaScript in the delete links and was hoping for something a little 'cleaner' (possibly data attributes or something?). It strikes me this is going to be difficult to hook into (to provide confirmation messages etc).
Question is, is this the expected output of AJAX in ASP.NET MVC? I'm very new to this so not sure if I'm doing something wrong in my implementation.
Many thanks.
Yes, this is the markup the Ajax.* family of helpers produces. My recommendation in general, and especially in light of the fact that you don't want the on* attributes added, is to avoid these helpers like the plague. You'll have much cleaner and maintainable (though slightly more verbose) code handling AJAX yourself, i.e. just add a standard Html.ActionLink and then use jQuery to attach an event handler that calls the AJAX. Plus, when something goes wrong, as it almost inevitably does, you have full knowledge and control over the JavaScript that is running, rather than being at the mercy of some opinionated Microsoft dev.
Related
Being relatively new to MVC I have been struggling for the past several weeks getting my layout to work.
I have managed to get myself really twisted into knots. So instead of trying to explain and unravel my mess perhaps instead someone could explain how I would accomplish the following at a high level.
_Layout this would have all the css js etc. It would also have basic structure.
Of course HTML tags not allowed in code block....each render is in a div.
#RenderPartial(Header)</div>
#RenderBody()</div>
#RenderPartial(Footer)</div>
RenderBody is Index.cshtml and it would be broken into three pieces
#
#Html.Partial(NavMenu, model)</div>
#Html.Partial(SubNavMenu, model)</div>
#Html.Partial(MainContent, model)</div>
I have this basic layout and it looks fine until you click one of the menu items.
The menu items render as:
<a class="k-link" href="/stuffroute">Stuff</a>
That route goes to a controller that returns a view and that navigates away from the above arrangement in Index.cshtml. So I end up with the header, footer, and subdash nav....
So the question is...
How do I route / orchestrate my layout to not lose the differing pieces?
Partials don't do anything for you here. You're essentially asking about how to create SPA (single page application), although in this case your application will have other pages, it's just that the index view will act like a SPA.
That requires JavaScript, specifically AJAX, to make requests to endpoints that will return HTML fragments you can use to replace portions of the DOM with. For example, clicking "Stuff 1" causes an AJAX request to be made to the URL that routes to FooController.GetSubNav([stuff identifier]). That action then would use what was passed to it to retrieve the correct sub-nav and return a partial view that renders that sub-nav. Your AJAX callback will then take this response, select a portion of the DOM (specifically the parent of the sub-nav) and insert the new HTML as its innerHTML.
If you're going to be doing a lot of this, you'll want to make use of some client-side MVC-style JavaScript library, like Angular for example. These make it trivial to wire everything up.
I'm using AngularJS in my MVC application and trying to use the best of both worlds. I really like how you can in MVC set up your validation logic in your viewmodels and generate client side validation with jQuery validation in your razor views with little effort. I use AngularJS to get the SPA behavior with routing etc, but when I create a razor view that I use to inject into a page with:
<div data-ng-view="data-ng-view"></div>
then the jQuery validation stops working, even though the script files is references on the page where the view is injected. See below for my razor view:
#model BandViewModel
<div data-ng-controller="App.BandCreateCtrl">
<form name="startBandForm">
#Html.ValidationSummary(true)
<br />
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name, new { data_ng_model = "band.Name" })
#Html.ValidationMessageFor(m => m.Name)
<br/>
<input data-ng-disabled="startBandForm.$invalid" type="submit" data-ng-click="createBand(band)" value="Create"/>
</form>
</div>
First of all, IMO using Razor to render your templates is fraught with peril, at best. Generally you want to use static HTML for your page and directive templates, and then retrieve and post data as AJAX to your API. The ASP.NET web API is really good at this, actually. If your underlying model has validation, then bad web API calls will throw an exception, which you can catch in your $http or $resource handler and show to the user. Mixing standard HTTP form posts with AngularJS will be... difficult.
To achieve what you want, I think someone (not me) would have to write the AngularJS equivalent to jQuery Unobtrusive Validation library, which itself is using the jQuery Validate plugin. Not trivial. I personally don't see drop-in Angular validation happening soon, especially since it has more to do with markup and less to do with a JS configuration object.
Possibly you could re-initialize the validation when the view has finished loading using the $viewContentLoaded event. But please don't.
It pains me that everywhere I've looked we get the same replies: "HTML should just be a template". Perhaps, but I'm just not ready to delegate everything to JavaScript
Instead of using the anonymous object for passing the HTML attributes, try using the Dictionary
#Html.TextBoxFor(m => m.Name, new Dictionary<string, object>(){{ "data_ng_model", "band.Name" }})
Make sure is
Dictionary<string, object>
And not
Dictionary<string, string>
Otherwise the constructor will confuse it for
object HtmlAttribute
Not as pretty though... but it works for the most part.
Lastly, take in mind that if you include AngularJS after jQuery, then it will use jQuery for selectors.
To anyone still looking for a solution to this problem , there is a simple way to make it work.
var currForm = $("#myForm");
currForm.removeData("validator");
currForm.removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(currForm);
currForm.validate();
You could put this code in $viewContentLoaded or other places which are relevant in your code.
While I understand some of the views here which discourage MVC views being used as a templates for your Angular code. My reason being its not not a natural way of doing things in angular and hence there are chances that you will run into issues like this one. But if you benefit from using MVC views as templates its not a path to hell. IT works and can benefit you as well. I have 2 use cases in my project where using MVC views make a lot of sense.
My project actually has a requirement where a client can turn of the required validation on certain field , for this I use the MVC custom data annotation validator which allows me to add or remove the data annotation at the time of rendering the view using a single class , if you were to build the same flexibility in angular , you would have to write a lot more code. So MVC views work great here for me as Custom Data Annotations are triggerd by the DisplayFor and EditorFor helpers
We are heavily invested in Internationalization and while angular is great at other stuff internationalization is not its strong suite ( at the time of writing this atleast )I still feel the internationalization support in MVC with .RESX works great and has been here long . This again uses the data annotation power of the DisplayAttribute.
TLDR : MVC views as templates may give you a few unexpected problems but are worth it if you are really using the full power of the data annotation pipeline created by Microsoft.
Hopefully someone comes up with a Angular HTML Helpers library in the future this would be much simpler.
Maybe using Angular for validation can be less painful than you think. Using a couple of custom directives, you could easily get something quite close to Razor with markup as simple as:
<validated-input name=username display="User Name" ng-model=model.username required</validated-input>
Check out my sample here and its inspiration here. It only implements required and number at the moment, but customizing it should be easy.
You can use ngval library. It brings data annotations to client side as angularjs validation directives.
What is a good approach in ASP.NET MVC for implementing a complex form where sections of the form are shown and hidden based on a user's inputs?
My background is in Webforms. I am frequently asked to build forms where a user selects an option from a dropdown list, triggering a handful of new fields to appear. A different selection might cause a different set of fields to appear.
In the past, I would handle this scenario via an UpdatePanel and a wrapper Panel around the fields that I want to show or hide. Validation is automatically disabled when the fields are hidden.
Going forward, I'd like to make use of MVC's attribute-based model validation, but is it possible to make the validation conditional on the values of other properties?
Also, how can I handle the toggling of blocks of form fields? I know how to do it manually in jQuery, but I'm hoping there's an approach that ties in with validation and server-side code.
I am liking MVC much better overall, but I haven't figured out how to handle this type of scenario very well. Likely I just need to shift my thinking to fit better with the MVC approach.
Josh,
The first thing I's suggest is to make sure you use ViewModels for the pages that are mode complicated. A ViewModel is basically a Model you create for a specific View; for example, a ViewModel could be a composition of other classes.
As for dynamically changing the fields on your View, the best way is to use jQuery (or any other javascript library) to do it.
I also migrated from a web form environment and I know is difficult to change gears at the begining, but trust me, doing a simple jQuery even handler is much simpler than having to put in place a control panel and then the event handlers.
Not to mention that is much more efficient; update panels are after all making partial posts to the page, sometimes, with jQuery you don't even need to do that.
After a few projects with MVC, I actually now find it time consuming to go and do the Update Panels on web forms ;)
Hope this helps,
-Covo
I know this might not be the answer you're looking for, but I personally don't think complex forms are very user friendly in the first place and I always try to split them up into simpler forms where possible, or to simplify the form. I've come across many forms in websites where there are a raft of "fields" where there should really be a few questions for the user to answer. Simple stuff which gets to the point of what they want to achieve rather than the field values, along with a lot of application specific knowledge needed to set those fields to the right values.
If you still want to go ahead with a complex form, then as the other answers have already stated there are facilities provided by MVC to do that, but there isn't any set way. So, it's down to you to figure out what will work best for your users.
Traditional asp.net webforms did alot of "magic" for you whereas you have to be aware of the work that goes into creating the "magic" in asp.net MVC. The benefit is that with MVC you have more control over what is happening which can also enhance performance.
In asp.net webforms an UpdatePanel is used for ajax calls. If you need to got to the server asynchronously(without doing a full post back) then use ajax via JQuery. See below for example:
$.ajax({
type: "get",
url: "/YourController/YourAction",
success: function (obj) {
//any logic you want to do upon success
}
});
The above example will do an ajax HTTP GET call to /YourController/YourAction.
In order to handle "toggling of blocks", if you don't need to go to the server for data and you simply want to do it on the client, then use simple jquery. JQuery has a function for toggling items.
http://api.jquery.com/toggle-event/
Because of the way MVC works in contrast to Webforms you're stuck with the responsibility of really thinking about what happens on the client and what happens on the server separately as not a lot of meta-data is being passed back to give us that happy Webforms feeling.
However, there is a notion when using the built-in AJAX libraries when you render a form that you can have it auto do an update once it is posted. In a sense, it's saving you the JavaScript/JQuery because it "auto-wires" it up similar-ish to a Panel. In this way you could potentially look at progressively rendering your complex forms from the server as each section is edited, etc.
See MSDN: http://msdn.microsoft.com/en-us/library/system.web.mvc.ajax.ajaxoptions.updatetargetid.aspx
The relevant code example to give you an idea (unfortunately, it's not in the more readable Razor syntax):
The relevant line is the Ajax.BeginForm where the form tag is rendered. Once the form is posted, the MS AJAX library will auto update the element specified in "UpdateTargetId" specified in the form's AjaxOptions. In this case, the response will be placed into the SPAN element "textEntered" upon reply from the server. Here, you could progressively render more content to the user to fill out, perhaps another form, etc.
<h2><%= Html.Encode(ViewData["Message"]) %></h2>
<p>
Page Rendered: <%= DateTime.Now.ToLongTimeString() %>
</p>
<span id="status">No Status</span>
<br />
<%= Ajax.ActionLink("Update Status", "GetStatus", new AjaxOptions{UpdateTargetId="status" }) %>
<br /><br />
<% using(Ajax.BeginForm("UpdateForm", new AjaxOptions{UpdateTargetId="textEntered"})) { %>
<%= Html.TextBox("textBox1","Enter text")%>
<input type="submit" value="Submit"/><br />
<span id="textEntered">Nothing Entered</span>
<% } %>
What I mean by that is a type element that will have a label based on a value in my viewModel and also be able to submit that value back up to the viewModel so it can grab new results based on Current Page and Page Size. This is for creating a gridview in MVC that supports pagination.
None of the examples I've seen of MVC so far have had anything resembling a gridview. It's important that I create my own paging and not use any built in paging mechanisms or third party controls or html helpers or the like
I'd go with ActionLink. To build Urls you can either use Url.Action or Html.BuildUrlFromExpression(c => c.conTrollerAction)
If you use T4MVC, you will get some nice helpers that do exactly what you are looking for.
Something along the lines of:
<a href="<%: Url.Action(MVC.MyController.MyAction(Model.ActionMethodParam1, Model.ActionMethodParam2)) %>">
You can also use the Html.ActionLink helper:
<%: Html.ActionLink("Link Name", MVC.MyController.MyAction(Model.ActionMethodParam1, Model.ActionMethodParam2)) %>
T4MVC is a great little library that I use in every MVC app.
I've got a question regarding ASP.Net MVC.
I'm using an Ajax.ActionLink to load a PartialView.
In this partial view is a javascript function I'd like to get called.
However I can't figure out how to make this happen.
I've tried using AjaxOptions { OnSuccess="functionInPartialView" } when I set the Ajax.ActionLink but for some reason it can't see the Javascript.
EDIT: The PartialView is a mix of JavaScript and Html
I would suggest to use jQuery ($.get/$.ajax). It evaluates the $(function(){}) when you load the partial, so your scripts there fire. And I personally find jQuery easier and cleaner to use.
Add the javascript code in the view that will contain the parcialview, and next use the ajaxOptions { OnSuccess="functionInView" } when you set the Ajax.ActionLink.
If it's only javascript in your partial view, then you should be using a JavaScript Action result, as in this post
Otherwise, the issue is that ajax merely loading content into a div doesn't mean that it executes. In your ajax callback, you need to find the javascript content and eval it, so that your page is aware of the function definition.
I know that's a high level description, but I don't have any samples of doing this. If you post some of the code, maybe someone can suggest a cleaner way of doing this so that you have better access to the script.
You can try Multipartials , you can use them to update multiple views and even run scripts from partials, im not sure about the exact specifics as its been a while, but you can have a look at it and see if it can accomplish what you are looking for