script block not include in ajax response - asp.net-mvc

In my application I have a common layout for all the views, from one of the view(Index.cshtml) i have used Ajax.BeginForm to display another view as a response the code for this is
#using (Ajax.BeginForm("Edit", "Home", new AjaxOptions { UpdateTargetId = "content",InsertionMode=InsertionMode.Replace }))
now my problem is as a response the edit view is rendered as expected but the script block with in the edit view is not included and hence the javascript is not executed.
#model Test.Models.CARE
#{
ViewBag.Title = "Edit";
}
<script>
function s() {
alert("hi");
}
</script>
<h2 style="padding: 0px; margin-top: 0px;">
Edit</h2>
#using (Html.BeginForm("SaveForConfirmation", "Home"))
{
Plz help.

It's bad practice to put javascript code in views, let alone partial views. So I would recommend you to externalize this javascript code in a separate function in a separate javascript file.
And then you could subscribe to the OnSuccess event of the Ajax.BeginForm helper:
#using (Ajax.BeginForm("Edit", "Home", new AjaxOptions { OnSuccess = "editSuccess", UpdateTargetId = "content", InsertionMode = InsertionMode.Replace }))
{
...
}
and then define the editSuccess function in a separate javascript file:
function editSuccess(result) {
alert('hi');
}
now in your partial view you should leave only what is supposed to be in a partial - markup. Get rid of any <script> tags, you don't need them. You don't need any inline scripts. They only increase the size of your webpages and waste bandwidth as they cannot be cached by the browsers:;
#model Test.Models.CARE
<h2 style="padding: 0px; margin-top: 0px;">Edit</h2>
#using (Html.BeginForm("SaveForConfirmation", "Home"))
{
...
}
UPDATE:
If you have some poorly written javascript that depends on crap like ViewBag inside the partial then you could invoke the corresponding function from within the OnSuccess callback:
#using (Ajax.BeginForm("Edit", "Home", new AjaxOptions { OnSuccess = "s", UpdateTargetId = "content", InsertionMode = InsertionMode.Replace }))
{
...
}
Notice how I have defined OnSuccess = "s" where s is the function that you have defined in your partial and which will be invoked in this case. But I repeat once again, this is a wrong approach and should only be used if you don't have time to refactor your code properly.

Finally have achieved the required functionality.
this is how I have done it
View (from where the call was made)
<input type="submit" value="Create" />
#using (Ajax.BeginForm("Edit", "Home", new AjaxOptions {OnSuccess="DisableControls()",
InsertionMode= InsertionMode.Replace,UpdateTargetId="DivToUpdate" }))
The Controller
[HttpPost]
public ActionResult Edit(FormCollection fc)
{
if (fc.Keys[0].ToString().TrimEnd() == "Display")
{
TempData["readonly"] = "readonly";
}
else
TempData["readonly"] = "";
The Called Partial view
#model TestApp.Models.CUSTOMER
#{
ViewBag.Title = "Edit";
}
<script>
function s() {
var readonly="#TempData["readonly"].ToString()";
if(readonly=="readonly")
{
$('input[type="text"],select').attr('readonly','readonly');
$("#Save").remove();
}
}
</script>
<h2 style="padding: 0px; margin-top: 0px;">
Edit</h2>
#using (Html.BeginForm("Save", "Home"))
{
#Html.ValidationSummary()
<fieldset>
<legend>CUSTOMER</legend>
<table style="margin: 0px auto;">
<tr>
<td>
Now on diagnosing the cause for the previous failure attempt I wrote a alert() in the script to see whether the script is executing or not.
<script>
function s() {
var readonly="#TempData["readonly"].ToString()";var s="hi"+ prompt("name","");
alert(s);
if(readonly=="readonly")
{ //To make all text boxes and dropdown readonly
$('input[type="text"],select').attr('readonly','readonly');
$("#Save").remove();//To Remove the save button
}
}
</script>
on further looking up with the firebug I could see that the response contains the script
<script>
function s() {
var readonly="readonly";var s="hi"+ prompt("sdfs","kakaji");
alert(s );
if(readonly=="readonly")
{
$('input[type="text"],select').attr('readonly','readonly');
$("#Save").remove();
}
}
</script>
<form action="/Grid/Save" method="post"><div class="validation-summary-valid" data-valmsg-summary="true">
<ul><li style="display:none"></li></ul>
</div>
<fieldset>
<legend>CUSTOMER</legend>
<table style="margin: 0px auto;">
<tr>
<td>
<div class="editor-label">
But what found that the generated page doesn't include the script for which I found a reason here
Hence this solves my problem and gave me a reason behind the fact that WHY I could not see or watch my script tag from the Ajax response.

Related

Facing issue while submitting Partial View using JQuery post

I have an Area like below.
Controller Class
public class AdminController : Controller
{
//
// GET: /Admin/Admin/
[HttpPost]
public ActionResult Index_partialPost(AdminModule model)
{
return PartialView("_PartialPage1", model);
}
[HttpGet]
public ActionResult Index_partial()
{
return PartialView("_PartialPage1");
}
[HttpGet]
public ActionResult Index()
{
AdminModule model = new AdminModule();
model.MyName = "My Name";
return View("Index", model);
}
}
View
#model _1.Areas.Admin.Models.AdminModule
#{
ViewBag.Title = "Index";
Layout = "~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml";
}
<h2>
Index</h2>
<div id="myForm">
<p id="pid">
</p>
</div>
<input id="BTN" type="submit" value="Button" />
<script language="javascript" type="text/javascript">
$('#BTN').click(function(){
$('#pid').load("#Url.Action("Index_partial", "Admin")");
});
</script>
View
#model _1.Areas.Admin.Models.AdminModule
#{
ViewBag.Title = "Index";
Layout = "~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml";
}
<h2>
Index</h2>
<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript">
</script>
<div id="myForm">
<p id="pid">
</p>
</div>
<input id="BTN" type="submit" value="Button" />
<script language="javascript" type="text/javascript">
$('#BTN').click(function(){
$('#pid').load("#Url.Action("Index_partial", "Admin")");
});
</script>
Partial View
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm())
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString">
</p>
<input type="submit" value="Click here" id="btn" />
}
<script language="javascript" type="text/javascript">
$('#btn').click(function () {
var url = '#Url.Action("Index_partialPost", "Admin")';
$.post(url, null, function (data) {
});
});
</script>
Issue is - When trying to post the partial view using jQuery-post not working and giving 404. It's working with Ajax using below mentioned code of Partial View
#model _1.Areas.Admin.Models.AdminModule
#using (Ajax.BeginForm("Index", "Admin",
new AjaxOptions { UpdateTargetId = "myForm", HttpMethod = "Post" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString">
</p>
<input type="submit" value="Click here" id="btn" />
}
You should cancel the default action of the form by returning false from your click handler:
<script type="text/javascript">
$('#btn').click(function () {
var url = '#Url.Action("Index_partialPost", "Admin")';
$.post(url, null, function (data) {
});
return false;
});
</script>
If you don't do that, the form is submitted to the server and the browser redirects to the target url leaving you absolutely no time for your AJAX request to ever execute.
Notice that it is much better to subscribe to the .submit event of the form in order to perform the AJAX request instead of the .click event of the submit button. The reason for this is obvious: there are other means to submit a form than clicking on a submit button. For example pressing the Enter key while inside an input field. If this happens your AJAX will never execute.
So here's the correct way. Start by giving an unique id to your form:
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString">
</p>
<input type="submit" value="Click here" id="btn" />
}
and then you could unobtrusively AJAXify this form:
<script type="text/javascript">
$('#myForm').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
}
});
return false;
});
</script>
I think you need to define the Area in the routeparameter where you do #Url.Action in your partial view:
$('#btn').click(function () {
var url = '#Url.Action("Index_partialPost", "Admin", new { area = "Admin"})';
$.post(url, null, function (data) {
});
});
Else it will be posted to your main (root) Area where you probably don't have a AdminController...
You can doublecheck the url in the JS your HTML source when the form is rendered, it sould read "/Admin/Admin/Index_partialPost" instead of just "/Admin/Index_partialPost"

Facing issue while showing the error messages in Partial View using unobstructive JQuery

Following is my Area in MVC3
Model
public class AdminModule
{
[Display(Name = "My Name")]
[Required]
public String MyName { get; set; }
}
Partial View
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString">
</p>
<input type="submit" value="Click here" id="btn" />
}
<script language="javascript" type="text/javascript">
$('#btn1').click(function () {
debugger;
var $form = $("#myForm");
// Unbind existing validation
$form.unbind();
$form.data("validator", null);
// Check document for changes
$.validator.unobtrusive.parse(document);
// Re add validation with changes
$form.validate($form.data("unobtrusiveValidation").options);
if ($(this).valid()) {
var url = '#Url.Action("Index_partialPost", "Admin",
new { area = "Admin" })';
$.post(url, null, function (data) {
alert(data);
$('#myForm').html(data);
});
}
else
return false;
});
</script>
Controller Action
[HttpPost]
public ActionResult Index_partialPost(AdminModule model)
{
return PartialView("_PartialPage1", model);
}
[HttpGet]
public ActionResult Index_partial()
{
return PartialView("_PartialPage1");
}
Whenever I submit the form and leaves the required field empty. it goes to server i think. I checked here...
My confusion is => How can I modify my below mentioned code to display the same validation messages mentioned in model at client end using $.post ?
You could enable unobtrusive client side validation. Start by adding the following script reference:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.validate.unobtrusive.js")"></script>
and then:
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString"></p>
<input type="submit" value="Click here" />
}
<script type="text/javascript">
$('#myForm').submit(function () {
if ($(this).valid()) {
$.post(this.action, $(this).serialize(), function(data) {
$('#myForm').html(data);
$('#myForm').removeData('validator');
$('#myForm').removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse('#myForm');
});
}
return false;
});
</script>
UPDATE:
Now that you sent me your actual code by email I see that there are a hell lot of a problems with it. Instead of going through all of them I prefer to completely rewrite everything from scratch.
So we start by the ~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml:
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div>
<ul>
<li>#Html.ActionLink("Home", "Index", "Home", new { area = "" }, null)</li>
</ul>
#RenderBody()
</div>
<script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>
#RenderSection("Scripts", required: false)
</body>
</html>
Notice how I moved all scripts to the bottom of the file as well as added a specifically dedicated section where custom scripts will be placed.
Next we move to the ~/Areas/Admin/Views/Admin/Index.cshtml:
#model _1.Areas.Admin.Models.AdminModule
#{
ViewBag.Title = "Index";
Layout = "~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml";
}
<div id="formContainer" data-url="#Url.Action("Index_partial", "Admin", new { area = "Admin" })"></div>
<input id="BTN" type="button" value="Button" />
#section Scripts {
<script type="text/javascript" src="#Url.Content("~/areas/admin/scripts/myscript.js")"></script>
}
Here you could notice that I have replaced the type of the button from submit to button because this button is not contained within a form to submit. I have also gotten rid of the <p> element you were having with id="myForm" which was not only useless but you were ending up with duplicate ids in your DOM which is invalid markup. I have also used the data-url HTML5 attribute on the container div to indicate the url of the server side script that will load the form. And the last thing I did in this file was to override the scripts section we defined earlier in the Layout with a custom script.
So the next part is the custom script: ~/areas/admin/scripts/myscript.js:
$('#BTN').click(function () {
var $formContainer = $('#formContainer');
var url = $formContainer.attr('data-url');
$formContainer.load(url, function () {
var $form = $('#myForm');
$.validator.unobtrusive.parse($form);
$form.submit(function () {
var $form = $(this);
if ($form.valid()) {
$.post(this.action, $(this).serialize(), function (data) {
$form.html(data);
$form.removeData('validator');
$form.removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse($form);
});
}
return false;
});
});
return false;
});
Pretty standard code here. We subscribe to the click event of the button and load the partial using an AJAX call. As soon as this is done we instruct the unobtrusive validation framework to parse the newly added contents to our DOM. The next step is to AJAXify the form by subscribing to the .submit event. And because in the success handler we are once again replacing the contents of the container we need to instruct the unobtrusive validation framework to parse the new contents.
and finally the partial:
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm("Index_partialPost", "Admin", FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString"></p>
<input type="submit" value="Click here" />
}
Here you could notice that I have gotten rid of absolutely any traces of javascript. javascript belongs to separate files. It has nothing to do in views. You should not mix markup and scripts. When you have separate scripts not only that your dynamic markup will be much smaller but also you could take advantage of things like browser caching for the external scripts, minification, ... Another thing you will notice in this partial is that I remove the <script> nodes in which you were referencing jQuery and the jQuery.validate scripts. You already did that in the Layout, do not repeat it twice.

pass model from view to controller with html.actionlink

I am trying to get the model data from a strongly typed view to a controller.
Using the submit button is ok, I can get the data. Now I want to achieve the same with html.actionlink.
This is what I have:
View:
#model WordAutomation.Models.Document
#{
ViewBag.Title = "Document";
}
<script type="text/javascript">
$(function () {
$("#dialog").dialog();
});
</script>
<h2>Document</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Document</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ClientTitle)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ClientTitle)
#Html.ValidationMessageFor(model => model.ClientTitle)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ClientFullName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ClientFullName)
#Html.ValidationMessageFor(model => model.ClientFullName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ClientCustomSSN)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ClientCustomSSN)
#Html.ValidationMessageFor(model => model.ClientCustomSSN)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Preview", "PreviewWordDocument", "Home", null, new { id = "previewLink" })
</div>
<div id="dialogcontainer">
<div id="dialogcontent"><input type="submit" value="Create" /> </div>
</div>
#section Scripts {
<script type="text/javascript">
$(document).ready(function() {
$("#dialogcontainer").dialog({
width: 400,
autoOpen:false,
resizable: false,
title: 'Test dialog',
open: function (event, ui) {
$("#dialogcontent").load("#Url.Action("PreviewWordDocument", "Home")");
},
buttons: {
"Close": function () {
$(this).dialog("close");
}
}
});
$("#previewLink").click(function(e) {
e.preventDefault();
$("#dialogcontainer").dialog('open');
});
});
</script>
}
Controller:
public ActionResult Document()
{
return View();
}
[HttpPost]
public ActionResult Document(WordAutomation.Models.Document model)
{
Models.Utility.EditWord word = new Models.Utility.EditWord();
word.EditWordDoc(model);
return View("Display", model);
}
public ActionResult PreviewWordDocument()
{
var image = Url.Content("~/Content/preview.jpeg");
return PartialView((object)image);
}
The document actionresult can get the model, but I want to know how can I get the values from the actionlink which will trigger the PreviewWordDocument action.
Thanks in advance, Laziale
The form can only be posted using the submit button to the URL given by its action attribute.
You can however send the form data to a different URL using the jQuery post method, manually validating the form before it is sent.
That way you can send the form data to the PreviewWordDocument controller method and handle the response in order to show the preview in the desired div.
(It will be helpful if you give an id to the form, so you can easily find it using jQuery)
So your click event handler for the preview link will look like this:
$("#previewLink").click(function(e) {
e.preventDefault();
if($("#YourFormId").valid()){
$("#dialogcontainer").dialog('open');
}
});
In the open function of the dialog you will post the form (which was already validated) to the preview controller method, using the jQuery ajax function. The response will be loaded into the dialogContent div:
$.ajax({
type: "POST",
url: $("#previewLink").attr("href"), //the preview controller method
data: $("#YourFormId").serialize(),
success: function (data) {
//load ajax response into the dialogContent div
$("#dialogcontent").html(data);
},
error: function(xhr, error) {
$("#YourFormId").prepend('<div id="ajaxErrors"></div>')
.html(xhr.responseText);
}
});
Now you will now be able to receive the whole document in the PreviewWordDocument action:
public ActionResult PreviewWordDocument(WordAutomation.Models.Document model)
{
var image = Url.Content("~/Content/preview.jpeg");
return PartialView((object)image);
}
in a HTML page when you click on a submit button all the input elements inside the form which the submit button resides in will posted to server, but when you click on a anchor (<a> tag ). you only send a request with a Get method and without posting any value.but if you want to send particular value to the server with this approach you can do it by query string.you have used following to make a request :
#Html.ActionLink("Preview", "PreviewWordDocument", "Home", null,
new { id = "previewLink" })
this will produce :
<a id="previewLink" href="/Home/PreviewWordDocument"> Preview </a>
which is incorrect.to pass any value to the server with ActionLink use 4th parameter like this :
#Html.ActionLink("Preview", "PreviewWordDocument", "Home",
new { id = "previewLink" }, null)
the result from this code would be :
Preview
cheers!

ASP.NET MVC - Iframe Download Dialog not showing up

I need to make a file download that displays a loading icon during the processing of the file (because I don't know how long it is going to take), I decided to use an iframe, the code runs fine, but the problem is that the file download dialog box doesn't show up.
I've tested in IE, Firefox and Chrome and it is not working in any of them.
Here's my code:
View:
<table id="progress" style="visibility:hidden">
<tr>
<td>
<img src="~/Content/Images/ajax-loader.gif" />
</td>
<td>
<h4>please wait.</h4>
</td>
</tr>
</table>
<div class="buttons">
<input type="button" value="Ok" class="button" id="downloadButton">
</div>
<iframe id="iframe" style="visibility:hidden"></iframe>
<script>
$(document).ready(function () {
$('#downloadButton').click(function () {
$('#progress').show();
$('input:button').attr("disabled", true);
$('#iframe').src = "#Url.Action("GenerateFile", "Files", null)";
$('#iframe').load("GenerateFile", function () {
$('input:button').attr("disabled", false);
$('#progress').hide();
});
});
});
</script>
Server side Action:
[HttpGet]
public void GenerateFile(Filters viewModel)
{
var result = GetCustomers().WithName(viewModel.Name);
StringBuilder file = new StringBuilder();
foreach (var customer in result)
{
// fill up the string builder with csv format
}
System.Web.HttpContext.Current.Response.AddHeader("content-disposition","attachment; filename=Customers.csv");
System.Web.HttpContext.Current.Response.ContentType = "application/csv";
System.Web.HttpContext.Current.Response.Write(file);
System.Web.HttpContext.Current.Response.End();
}
Any help would be appreciated, thanks!
I think there are multiple problems with this code. Try this:
public FileResult GenerateFile(Filters viewModel)
{
var result = GetCustomers().WithName(viewModel.Name);
StringBuilder file = new StringBuilder();
foreach (var customer in result)
{
// fill up the string builder with csv format
}
var content = Encoding.UTF8.GetBytes(file.ToString());
return File(content, "application/csv", "Customers.csv");
}
and I think there is no jQuery method called src, so try this also:
$('#iframe').attr('src',url);
I assume you're using Razor syntax, so try this for your View code:
#{
ViewBag.Title = "Test";
}
<table id="progress" style="display: none">
<tr>
<td>
<img src="~/Content/Images/ajax-loader.gif" />
</td>
<td>
<h4>please wait.</h4>
</td>
</tr>
</table>
<div class="buttons">
<input type="button" value="Ok" class="button" id="downloadButton">
</div>
<iframe id="iframe" style="display: none"></iframe>
#section scripts{
<script type="text/javascript">
$(document).ready(function () {
$('#downloadButton').click(function () {
$('#progress').show();
$('input:button').attr("disabled", true);
var url = "#Url.Action("GenerateFile", "Files", null)";
$("#iframe").attr('src', url);
$('#iframe').load("/Files/GenerateFile", function () {
$('input:button').attr("disabled", false);
$('#progress').hide();
});
});
});
</script>
}
And I also believe that you should specify an Action parameter in your jQuery load function, eg. $('#iframe').load("/Files/GenerateFile/parameter"...) because your GenerateFile action takes Filters parameter.

Is it possible to nest a Razor section within a Razor form block?

I have the following block of Razor markup:
#section StoreSearch
{
#Html.Partial("SearchPartial", Model)
}
<section id="gallery-index">
...
</section>
<nav class="pager">
Prev Page #(Model.PageIndex + 1) of #Model.PageCount Next
</nav>
I would like to surround this whole block with a form, as in
#using (Html.BeginForm("Index", "Gallery", FormMethod.Post, new { id = "search-form" }))
{
#section StoreSearch
{
#Html.Partial("SearchPartial", Model)
}
<section id="gallery-index">
...
}
Yet when I do that, the parser seems to balk at the #section StoreSearch part, and complains "Cannot resolve symbol StoreSearch".
Is what I'm trying to do not allowed, or am I just missing some sort of escaping method?
Looks like you found a bug in the Razor parsing engine. Most likely the first # sign in a using clause is being parsed as a variable, which in your case is actually a function so it's throwing an exception. For now the solution is to surround your section with a div (or any html element really).
#using (Html.BeginForm("Index", "Gallery", FormMethod.Post,
new { id = "search-form" }))
{
<div>
#section StoreSearch
{
#Html.Partial("SearchPartial", Model)
}
<section id="gallery-index">
...
</section>
</div>
}

Resources