I have an mvc view that contains a list of items. Each item is displayed using a partial view. The user can edit, add, and delete items in the list. All of this works.
However, if the user adds an item, the item cannot be edited until the page is refreshed. When the user selects the submit button, the post does not occur. There is no network activity shown in Chrome dev tools - nothing happens.
The output html is the same for items that are included in the initial page load and items added via ajax.
<form action="/booking/UpdateRoomFlow" data-ajax="true" data-ajax-method="POST" data-ajax-success="update_27365547" id="roomflowform_27365547" method="post" novalidate="novalidate"></form>
I noticed that when I inspect element in chrome, the form does not wrap the form fields, but when I look at view source, it does.
I've tried useing Ajax.BeginForm and I've tried writting the html out myself, with the same results either way.
This is the Ajax.BeginForm...
#using (Ajax.BeginForm(
"UpdateRoomFlow",null,
new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = targetfunction
},
new { #id = #formid, #novalidate = "novalidate"}))
{
Any ideas?
EDIT: Something to note - this form is placed inside a tr tag above a td tag, which could cause an issue as that is invalid markup - however, its working in all cases except items added via ajax call.
<script> tags included in an HTML AJAX reponse are stripped, and since Ajax.BeginForm adds script tags to the HTML to enable it's functionality, you essentially end up with a regular old form that does nothing. There's nothing you can do about this, as the browsers strip <script> tags for security reasons. Your only recourse is to stop using Ajax.BeginForm and write your own JS to handle the AJAX submit, which you can then include in the main view or an external file which will not be affected, which is not a bad idea anyways. The Ajax.* family of helpers are awful just for reasons like this.
Related
In my ASP.NET MVC project, I need to dynamically manipulate some html elements when the user makes a couple of selections. I assume this needs to be done in code-behind C# (as opposed to in jquery), because how I respond is using data that is retrieved from a SQL Server database and is part of the View (I don't think jQuery would/could know about this MVC/.NET-specific data).
Specifically, when a user clicks a checkbox on the page, provided a selection has been made from an html-select element, my code needs to spring into action to populate other elements on the page.
The View (.cshtml) is getting the data in a Razor code block like so:
#model WebAppRptScheduler.Models.HomeModel
#using System.Data
#{
DataTable dtUnitReportPairEmailVals = Model.UnitReportPairEmailVals DataTable;
var unitRptPairEmailVals = from x in dtUnitReportPairEmailVals.AsEnumerable()
select new
{
emailAddr = x.Field<string>("EmailAddr")
};
This data comes from the Controller:
DataTable UnitReportPairEmailValsDT = new DataTable();
UnitReportPairEmailValsDT = SQL.ExecuteSQLReturnDataTable(SQL.UnitReportPairEmailQuery, CommandType.Text, null);
model.UnitReportPairEmailVals = UnitReportPairEmailValsDT;
Does this (event handling) code belong in the Controller? If so, how is the method declared/decorated so that there is a connection between it and the html elements whose events it needs to monitor?
This is what needs to happen:
User selects an option from an html select
User clicks a checkbox
The code determines that both an option has been selected, and a checkbox checked; it then uses the data to populate other controls, something like this:
#if (unitRptPairEmailVals.Count > 0)
{
email1.Text = unitRptPairEmailVals[0];
}
#if (unitRptPairEmailVals.Count > 1)
{
email2.Text = unitRptPairEmailVals[1];
}
#if (unitRptPairEmailVals.Count > 2)
{
email3.Text = unitRptPairEmailVals[2];
}
The above would populate the text/value of these html inputs:
<label class="margin4horizontal">Email 1</label><input type="text" name="email1" id="email1" />
<label class="margin4horizontal">Email 2</label><input type="text" name="email2" id="email2" />
<label class="margin4horizontal">Email 3</label><input type="text" name="email3" id="email3" />
BTW, if I'm barking up the wrong tree, or am even in the wrong forest altogether, please let me know what the preferred approach is.
UPDATE
Once I pieced together several different answers to this and related questions, I wrote up a tip on how to do this here.
Update: I was answering on mobile last night, and here is a clearer edit and some sample code...
Asp.net MVC has controllers, which contain actions. When you navigation to a URL (HTTP GET), ASP.net translates this via routes into a specific controller action to invoke, then the result of that action is sent back to the browser as plain HTML.
Also when you submit a form (HTTP POST) to specific URL it translates to invoking a controller action via routes. and a ModelBinder will read the body of that HTTP POST and converts it into your Model class. Unlike the classic web forms that used ViewState to track page controls, state, and events.
What you want to achieve can be done in 2 different ways...
Using JavaScript and JQuery. You can issue an Ajax request (GET, POST, etc) to a URL that exists in your ASP.net MVC routes table to execute an action and the results will be returned back to your JavaScript without navigating to another page.
Posting an HTML Form back to the server will allow ASP.net MVC to read the new/changed values in your HTML controls and pass those new values to the controller action, which in turn can render the same view again with the new values and behaviour.
I am writing a recipe manager for my wife in C#/.NET MVC 5. I'm getting to the page for creating a recipe, and I'm a little stumped. A recipe consists of a Name and a list of ingredients.
When I create a view, I have my form:
#using(Html.BeginForm()){
//Form elements
#Html.DisplayNameFor(x => Model.Name)
//button for adding a new ingredient to the recipe
<input type="submit" text="Submit New Recipe!" />
}
When the button for adding an ingredient is clicked, it should render a block of html inside the form just above the button itself, that way the user can add any number of ingredients and then submit the recipe back when the form is posted back to the controller.
For this functionality, should I make the button call a controller that sends back a partial view or something? I'm not sure how to accomplish this outside of JavaScript, but I'm wanting to use a .NET MVC solution if I can.
I try to minimize my reliance on javascript whenever I can, however I agree with #br4d that knockout is your best option here. If you want to avoid it at all cost, it will be more complex, slower and not as user friendly but here is how I would do it.
Enclose the form in a div. Have a place holder div inside the form to hold your ingredients list. Make the "Add new ingredient" call into a controller that will return a partial view with the required fields. In the target attribute indicate the place holder div as the update target and append the response to the html of the place holder div by specifying InsertionMode.InsertAfter.
<div id="parentDiv">
#Html.BeginForm........
#Ajax.ActionLink("Add New Ingredient","ActionName","ControllerName",routeValues,
new AjaxOptions
{
UpdateTargetId = "ChildDiv",
InsertionMode = InsertionMode.InsertAfter
}
<div id=ChildDiv>
</div>
</div>
This code is by no means comprehensive or production ready (I prefer to have a way of handling failed ajax calls and you might want to block off interactions until the call comes back just to mention two of the enhancements). Once again KnockOut would be the preferred way to do this.
I have a fairly simple page with a set of jQuery tabs, the content of some is called via ajax. I also have a search box in the masterpage in my header.
When I open the tabbed page the search box works fine. However once I have clicked on one of the ajax tabs the search box fails to work with an "Invalid Viewstate" yellow screen of death.
I believe this is because the ajax page is replacing the __VIEWSTATE hidden input with its own.
How can I stop this behaviour?
UPDATE: I have noticed that the YSOD only appears in IE and Chrome, Firefox doesn't seem to have the same issue. Although how the browser influences the ViewState, I'm not sure.
UPDATE: I've put a cut down version of the site that shows the issue here: http://dropbox.com/s/7wqgjqqdorgp958/stackoverflow.zip
The reason of such behavior is that you getting content of the ajaxTab.aspx page asynchronously and paste it into another aspx page. So you getting two instances of hidden fields with __VIEWSTATE name and when page posted back to server theirs values are mixing (might depends on how browser process multiple controls with same name on submit). To resolve this you can put second tab's content into a frame:
<div id="tabs">
<ul>
<li>Default Tab</li>
<li>ajax Content</li>
</ul>
<div id="tabs-1">
<p>
To replicate the error:
<ul>
<li>First use the search box top right to search to prove that code is ok</li>
<li>Then click the second ajax tab, and search again.</li>
<li>N.B. Chrome / IE give a state error, Firefox does not</li>
</ul>
</p>
</div>
<iframe id="tabs-2" src="ajaxTab.aspx" style="width:100%;" ></iframe>
</div>
Also, I'm not sure but this seems like error in the Web_UserControls_search control. In my opinion, NavBarSearchItemNoSearchItem_OnClick method must be refactored as below:
protected void NavBarSearchItemNoSearchItem_OnClick(object sender, EventArgs e)
{
var searchFieldTbx = NavBarSearchItemNo;
var navBarSearchCatHiddenField = NavBarSearchCatHiddenField;
var term = searchFieldTbx != null ? searchFieldTbx.Text : "";
if (term.Length > 0) //There is actually something in the input box we can work with
{
//Response.Redirect(Url.GetUrl("SearchResults", term));
Response.Redirect(ResolveClientUrl("~/Web/SearchResults.aspx?term=" + term + "&cat=" + navBarSearchCatHiddenField.Value));
}
}
Draw attention that we resolving client url when redirecting to search results page and instead of navBarSearchCatHiddenField use navBarSearchCatHiddenField.Value as cat parameter.
I guess that you use AJAX to fill the content of the tab. So in this case, content of your tab will be replaced by the new one from ajax and certainly _VIEWSTATE will be replaced. At server, do you use data from ViewState? In the "static tabs", you should prevent them auto reload by using cache:true
Your issue is that with your ajax call you bring in a complete ASPX page. Including the Form tag and its Viewstate. If you remove the Form tag from ajaxTab.aspx you will see everything works fine. asp.net does not know how to handle two Form tags in one page. Same goes for hidden Viewstate fields. You cannot bring in a full aspx page via ajax. Just bring in the content Div you want to display and you`ll be good to go.
As part of my current task in a given list of items, user can select some of them and invoke 'Print' no the selected items.
For each selected item we need to print the details. It is similar to printing invoices of selected items in a sales system.
I have created a partial view to write each record details but I am not sure how to use it as per my requirement.
Can I call jQuery print on document.ready to achieve my requirement?
As #Levib suggested, calling partial view in my PrintView. And PrintView's document.reay function is calling window.print. But when I try to invoke 'Print', I can not see print dialogue.
This is my view,
#section Styles
{
<link rel="stylesheet" href="AdminStyle.css" type="text/css" media="all" />
<link rel="stylesheet" href="AdminPrintOrder.css" type="text/css" media="print" />
}
#foreach (var item in Model)
{
<div id="content" style="page-break-before: always">
#{Html.RenderPartial("_OrderDetailView", item);}
</div>
}
#section scripts
{
<script type="text/javascript">
$(document).ready(function () {
debugger;
window.print();
});
</script>
}
And my print invoker view is
function printInvoices(){
$.ajax({
type: 'POST',
url: '/Batch/PrintInvoices',
data: '{ "allocationId" : "' + unSelected.toString() + '"}',
contentType: "application/json; charset=utf-8",
traditional: true,
success: printedView,
error: errorInSubscribing
});
}
Do I need to handle ajax reposne to populate print dialogue.
function printedView() {
unSelected = [];
}
And controller action is
[HttpPost]
public ActionResult PrintInvoices(string allocationId)
{
var context = new BatchContext();
var orderDetails = context.GetOrderDetails(RetriveList(allocationId));
return View(orderDetails);
}
Define a print stylesheet for your page. You do this by using the #media print declaration.
What you can then do is wrap each partial view on the page that you are going to print in a div and apply the 'page-break-before: always' CSS attribute to it.
This will ensure that each partial view ends up on a different page. Call 'window.print()' on the page load and you're done!
Given the extensive questions posed in the comments for this answer, here is a more detailed description of what needs to be done:
Preparation
A stylesheet needs to be defined and applied to the page which defines what the page will look like when printed.
This can either be done by using a #media print { } declaration in an existing stylesheet, or by applying the media="print" attribute to the link tag in your page which includes the stylesheet. You appear to have done this correctly.
This stylesheet must include a page-break-before: always declaration for all elements before which you would like there to be a page break. You seem to have done this with inline styles. Personally I would have rather done this in the print stylesheet than as an inline style. This step is integral to you being able to print one item per page.
Printing
window.print() allows you as the page author to define when the page is printed. This does the same thing as CTRL + P or as clicking the print button, except you can do it from your JavaScript.
Ajax has nothing intrinsically to do with printing. Ajax is a means to asynchronously make HTTP calls from your page without changing or reloading it and updating your page as a result. If you wanted to dynamically populate your page with items to print based on user input, then you could very well do so with Ajax. If you merely want to print the page, then you don't need to use Ajax.
the navigator.**
Two important points:
window.print() prints the page that is currently onscreen. If you want to print a different page, you need to load the other page in some way shape or form (perhaps through a popup) and call window.print() on that page.
The print stylesheet defines what the printed page will look like in contrast to the onscreen version. This means that you can have a page of items and lots of other stuff, and print only the items when the user clicks the print button. You would do this by setting the display: none property in your print stylesheet on all the elements that you do not want to appear on the printed page.
About PDFs:
I have nothing against exporting pages to PDF when necessary, but as you did not specifically ask about a PDF solution, I have given you the HTML+CSS solution to your question. PDFs also take a minute to generate and load. They are great, however, when your users will want to save copies of what they are printing. If this is the case for your site I strongly recommend that you consider such a solution.
Testing:
How do you test a print stylesheet? The easiest way is to simply click the print button in Chrome which will show you a lovely preview of what your site is going to look like when it is printed.
Final word:
For now, forget about window.print() and just focus on getting your page looking like it should by applying the appropriate CSS. Write your CSS, run your page, look at the output in Chrome, modify your print stylesheet as needed... Rinse and repeat. Only once you have the page appear exactly as you want it when clicking the print button should you then look at calling the print function automatically in your JavaScript.
How I do printing in MVC:
Create a view with exactly what you want to print layed out how you want it to look
Use the Rotativa library to turn your MVC View in a pdf document.
Simple as changing your Action to look like this
public ActionResult PrintInvoice(int invoiceId)
{
return new ActionAsPdf(
"Invoice",
new { invoiceId= invoiceId })
{ FileName = "Invoice.pdf" };
}
I believe it will obey the css page-break, so if you need to only print one item per page you can use that markup to force items to new pages.
I will create a PDF to better control the layout of the page.
With jQuery I will get the selected items from the user than make an ajax call, or a simple POST, to an action method that accept a list of your items as parameter and then return a filestream containing your pdf.
There are a lot of libraries free and commercial to create a pdf both at runtime or at design time.
I personally use DevExpress and I'm happy with it.
As an opensource alternative you can consider PDFSharp
I have an "Edit Profile" lightbox on my page, which posts to the controller via jQuery ajax. I handle the response in the jquery end instead of returning a View. However, when the profile is saved, I need to refresh the values on the page displaying the popup. How could I achieve this in MVC2? For example, if the user changes her name and avatar (in the lightbox), after she saves the profile, I'd like to update the avatar and name everywhere it occurs on the page.
Well what i would be doing is make your Controller return a PartialViewResult, which the end result is basically HTML.
The Partial View would be the popup itself, so essentially your calling your Controller method via AJAX, doing your server-side work, then re-rendering the Partial View to the client.
Have the action you post to via jQuery return a success for failure message. If it is a success, change the avatar/name/etc on the page using the values already in the textboxes (i.e.: the values you posted to the controller). If it is a failure message, display the validation errors.
In your jQuery AJAX, everything can be done in the callback function of the AJAX request.
Prabhu - both your profile page (i.e. the 'main' div contained within it) and the popup div should be partialviews. on posting the popup back to the server, you should requery the main page partialview and return the appropriate html, targetting the 'main' div.
that's certainly the approach that i take for a very similar task.