I have a MVC c#, signalR project where Agent follow below steps in Application
Login To application. Once login success application hides Login div panel & displays list of campaign & telephony buttons
Application displays list of campaigns agent is assigned to
Application displays button in front of each campaign to set Ready / Not Ready in campaign. In this case it is RestAPI & Telemarketing
If agent need to set himself not ready in campaign it opens popup window with list not ready reasons.
Issue is :
When Agent select reason and submit it application post back it lost view and reset to login window.
Controller action after submit of breakreason in PopUp window:
public ActionResult SetBreak(breakReasonModel form)
{
string tok=form.accessToken;
string cmp = form.campaign;
string selreason = "";
for (int i=0;i < form.arrReasons.Length;i++)
{
selreason = form.arrReasons[i];
}
SetBreak obj = new SetBreak();
System.Collections.Generic.List<ISCampaigns> IScampaignNames = new System.Collections.Generic.List<ISCampaigns>();
IScampaignNames = obj.setNotReadyInCampaign(tok, cmp, selreason);
return RedirectToAction("Index");
}
PopUp Partial View :
#using Altitude.IntegrationServer.RestApiWebApp.Models
#model Altitude.IntegrationServer.RestApiWebApp.Models.breakReasonModel
<div id="divBreakReasons">
#using (Html.BeginForm("SetBreak", "Home"))
{
#Html.ListBoxFor(m => m.arrReasons, Model.reasonsMultiSelectList, new { #class = "form-control" })
#Html.TextBoxFor(model => model.accessToken, new { id = "txtaccessToken" })
#Html.TextBoxFor(model => model.campaign, new { id = "txtcampaign" })
<br />
<button id="btn" type="submit" class="btn btn-block bg-primary" value="Submit" >Submit</button>
<br />
}
</div>
Index.chtml
<div class="row">
<div class="col-md-4 table-responsive" id="telButtons">
<table id="tblTelephony" class="table">
--Telephony Buttons
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 table-responsive">
<p id="demo"></p> // Campaign table with Ready/Not Ready buttons
</div>
</div>
//ajax call to open popup
<div id="dialog" style="display: none"></div>
<script type="text/javascript">
function getBreak(nrReason) {
$("#dialog").dialog({
autoOpen: false,
modal: true,
});
$.ajax({
type: "POST",
url: "#Url.Action("popupBreak","Home")",
data: '{breakReason : "' + dataToSend + '",accessToken : "' +acc+ '",campaign : "' + cmp + '"}',
contentType: "application/json; charset=utf-8",
dataType: "html",
success: function (response) {
$('#dialog').html(response);
$('#dialog').dialog('open');
console.log(response);
},
failure: function (response) {
},
error: function (response) {
}
});
}
</script>
It does exactly what you coded. If you need to return result to current view you should use ajax call that will return action result.
example
#using (Ajax.BeginForm("Action", "Controller", FormMethod.Post, new AjaxOptions() { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "YourTargetForResult" }, new { #id = "ajaxForm" }))
You must reference jquery.unobtrusive-ajax.js to receive postback in current view.
Example based on your comment:
<input type="hidden" id="hdnResponseMessage" /> // add dom object where response hits
#using (Ajax.BeginForm("SetBreak", "YourControllerName", FormMethod.Post, new AjaxOptions() { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "hdnResponseMessage" }, new { #id = "form" }))
{
#Html.ListBoxFor(m => m.arrReasons, Model.reasonsMultiSelectList, new { #class = "form-control" })
#Html.TextBoxFor(model => model.accessToken, new { id = "txtaccessToken" })
#Html.TextBoxFor(model => model.campaign, new { id = "txtcampaign" })
<br />
<button id="btn" type="submit" class="btn btn-block bg-primary" value="Submit" >Submit</button>
<br />
}
Conroller:
[HttpPost]
public JsonResult SetBreak(breakReasonModel form)
{
string tok=form.accessToken;
string cmp = form.campaign;
string selreason = "";
for (int i=0;i < form.arrReasons.Length;i++)
{
selreason = form.arrReasons[i];
}
SetBreak obj = new SetBreak();
System.Collections.Generic.List<ISCampaigns> IScampaignNames = new System.Collections.Generic.List<ISCampaigns>();
IScampaignNames = obj.setNotReadyInCampaign(tok, cmp, selreason);
return Json("SetBreak");
}
jQuery set listener in document ready:
// add dom object listener
$('#hdnResponseMessage').bind('DOMNodeInserted', function () {
var txt = $('#hdnResponseMessage').text();
if (txt == 'SetBreak')
{
//do your stuff here;
}
});
Related
I have a login form in which I would like to show custom error like "User not exists" same way as ValidationMessageFor messages works.
I am adding the custom error in the controller, but it is not shown in the form.
In order to show validation messages in an Ajax form I used the techinque of returning a login PartialView which I return from the controller
Thanks for the help!
Controller:
[HttpPost]
public ActionResult Login(Login login)
{
if (!ModelState.IsValid) //Check for validation errors
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return PartialView("_login", login);
}
ActiveDirectoryDAL.ADQueries ad = new ActiveDirectoryDAL.ADQueries();
var userName = login.UserName.ToLower().Trim();
var password = login.Password.Trim();
var isValidUser = ad.Authenticate(userName, password);
if (!isValidUser)
{
ModelState.AddModelError(string.Empty, "Login failed");
return PartialView("_login", login);
}
FormsAuthentication.SetAuthCookie(login.UserName, false);
return Json(new { RedirectUrl = Url.Action("Index", "Home") });
}
The View
#model PushNotificationWebSite.Models.Login
#{
ViewBag.Title = "Login";
Layout = "";
}
<html>
<head>
<link href="~/Content/login.css" rel="stylesheet" />
#Styles.Render("~/Content/bootstrap/css")
#Scripts.Render("~/bundles/modernizr")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
</head>
<script type="text/javascript">
function OnSuccess(data) {
if (data.RedirectUrl)
window.location.href = data.RedirectUrl;
}
function OnLoginFailure(data) {
debugger;
$('#login').html(data.responseText);
}
</script>
<body id="LoginForm">
<div class="container">
<h1 class="form-heading">login Form</h1>
<div class="login-form">
<div class="main-div">
<div class="panel">
<h2>Admin Login</h2>
<p>Please enter your username and password</p>
</div>
#using (Ajax.BeginForm("Login", "Account", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
LoadingElementId = "loader",
OnSuccess = "OnSuccess",
OnFailure = "OnLoginFailure"
}, new { id = "login" }))
{
#Html.ValidationSummary(true)
<div class="form-group">
#Html.TextBoxFor(model => model.UserName, new { #class = "form-control", autocomplete = "off", placeholder = "Username" })
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Password, new { #class = "form-control", autocomplete = "off", type = "password", placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
<button type="submit" class="btn btn-primary">Login</button>
}
<div id="loader" class="loader">
<img class="center" src="~/Images/ajax-loader.gif" />
</div>
</div>
</div>
</div>
</body>
</html>
Partial View:
#model PushNotificationWebSite.Models.Login
#using (Ajax.BeginForm("Login", "Account", new AjaxOptions
{
HttpMethod = "POST",
LoadingElementId = "loader",
OnSuccess = "OnSuccess",
OnFailure = "OnLoginFailure"
}, new { id = "login" }))
{
#Html.ValidationSummary(true)
<div class="form-group">
#Html.TextBoxFor(model => model.UserName, new { #class = "form-control", autocomplete = "off", placeholder = "Username" })
#Html.ValidationMessageFor(m => m.UserName, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Password, new { #class = "form-control", autocomplete = "off", type = "password", placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
<button type="submit" class="btn btn-primary">Login</button>
}
Instead of using string.Empty as key name, you can assign any string as key name to the custom ModelState error. Change ModelState.AddModelError() from this:
ModelState.AddModelError(string.Empty, "Login failed");
to this example:
ModelState.AddModelError("LoginFailed", "Login failed");
Afterwards, put a ValidationMessage helper inside partial view with same key name as assigned in ModelState.AddModelError like example below:
#* message without styling *#
#Html.ValidationMessage("LoginFailed")
#* message with styling *#
#Html.ValidationMessage("LoginFailed", new { #class = "text-danger" })
Or use ViewData.ModelState to display it, again with same key name:
#ViewData.ModelState["LoginFailed"].Errors[0].ErrorMessage
Note:
In addition to InsertionMode.Replace setting, you may consider using UpdateTargetId to update partial view form contents when validation failed:
#using (Ajax.BeginForm("Login", "Account", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
LoadingElementId = "loader",
UpdateTargetId = "elementId", // here you can set HTML element ID to update
OnSuccess = "OnSuccess",
OnFailure = "OnLoginFailure"
}, new { id = "login" }))
{
// form contents
}
I found the root cause.
I forgot to add Response.StatusCode = (int)HttpStatusCode.BadRequest;
before sending return PartialView("_login", login);
The server sent resonse OK 200 so validators did not triggered
I keep getting a 404 and searching all over SO and cannot target the issue here. The form is the result of a render action and appears on the home page (home controller). However, I want it to post a different controller action and it keeps giving me a 404. I have included all the correct script for unobtrusive javascript as well as the necessary web.config settings and I'm unable to come across a similar problem from my research.
This is the partial with the form that is being rendered:
#model AFS.Models.SearchLocationModel
<div class="site-search-module">
<div class="site-search-module-inside">
#using (Ajax.BeginForm("SearchCare", "LevelOfCare", null, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "searchDiv" }, new { #class = "search-form", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="row">
<div class="col-md-12">
<h5>Select a category</h5>
#Html.DropDownListFor(x => x.Level, Model.LevelSelectList, new { #class = "form-control input-lg selectpicker" })
</div>
<div class="col-md-12">
<h5>Enter location</h5>
<input type="text" id="Location" name="Location" class="form-control input-lg selectpicker" placeholder="City, State OR Zip Code" required />
</div>
<div class="col-md-12"> <button type="submit" class="btn btn-primary btn-block btn-lg search"><i class="fa fa-search"></i> Search</button> </div>
</div>
}
</div>
The controller action is:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SearchCare(SearchLocationModel model)
{
if (ModelState.IsValid)
{
SearchLocationModel geocodeModel = Geocode(new SearchLocationModel() { Level = model.Level, Location = model.Location });
if (geocodeModel.Status == "OK")
{
Session["level"] = model.Level;
return RedirectToRoute("LevelCity", new { level = model.Level, state = geocodeModel.State, city = geocodeModel.City, latitude = geocodeModel.Latitude, longitude = geocodeModel.Longitude });
}
else
{
ModelState.AddModelError(string.Empty, "Please enter City, State OR Zip Code.");
return RedirectToAction("SearchWidget", "Home");
}
}
else
{
return RedirectToAction("SearchError");
}
}
After much of struggle im posing this question. Im using a Kendo Upload on a page. Am able to post the selected files on the asyn mode with whe the page has Html.BeginForm. But I'm not able to send file details as HttpPostedFileBase when I use ajax request to send data to the controller.
Following is my html
<form class="form-horizontal" role="form">
<div class="form-group">
#Html.Label("Complaint Name", new { #class = "col-sm-3 control-label" })
<div class="col-sm-4">
#Html.TextBoxFor(
m => m.ComplaintName,
new
{
#TabIndex = "1",
#class = "form-control input-sm",
disabled = true
})
</div>
</div>
<div class="form-group">
#Html.Label("Complaint Details", new { #class = "col-sm-3 control-label" })
<div class="col-sm-4">
#Html.TextBoxFor(
m => m.ComplaintDetails,
new
{
#TabIndex = "2",
#class = "form-control input-sm",
disabled = true
})
</div>
</div>
<div class="form-group">
#Html.Label("Choose files to upload", new { #class = "col-sm-3 control-label" })
<div class="col-sm-9 nopaddingforTextArea">
<input name="files" id="files" type="file" />
</div>
</div>
<div class="form-group">
<div>
<input id="btnSubmit" class="btn btn-primary pull-right" type="button" />
</div>
</div>
</form>
Following is my action
public ActionResult SaveComplaintDetails(string complaintName, string complaintDetails, IEnumerable<HttpPostedFileBase> files)
{
}
Following is my js
$("#files").kendoUpload({
async: {
saveUrl: '#Url.Action("EsuperfundCommentsBind", ClientInboxConstants.NewQuery)',
autoUpload: false
},
multiple: true
});
$("#btnSubmit").click(function () {
//Ajax call from the server side
$.ajax({
//The Url action is for the Method FilterTable and the Controller PensionApplicationList
url: '#Url.Action("SaveComplaintDetails", "Complaints")',
//The text from the drop down and the corresponding flag are passed.
//Flag represents the Index of the value in the dropdown
data: {
complaintName: document.getElementById("ComplaintName").value,
complaintDetails: document.getElementById("ComplaintDetails").value,
files: //What to pass here??
},
contentType: "application/json; charset=utf-8",
//Json data
datatype: 'json',
//Specify if the method is GET or POST
type: 'GET',
//Error function gets called if there is an error in the ajax request
error: function () {
},
//Gets called on success of the Ajax Call
success: function (data) {
}
});
});
My question is how to pass the selected files in Kendo Upload in ajax as a parameter?
Any help in this aspect would be really appreciated.
If your view is based on a model and you have generated the controls inside <form> tags, then you can serialize the model to FormData using:
<script>
var formdata = new FormData($('form').get(0));
</script>
This will also include any files generated with: <input type="file" name="myImage" .../> and post it back using:
<script>
$.ajax({
url: '#Url.Action("YourActionName", "YourControllerName")',
type: 'POST',
data: formdata,
processData: false,
contentType: false,
});
</script>
and in your controller:
[HttpPost]
public ActionResult YourActionName(YourModelType model)
{
}
or (if your model does not include a property for HttpPostedFileBase)
[HttpPost]
public ActionResult YourActionName(YourModelType model,
HttpPostedFileBase myImage)
{
}
If you want to add additional information that is not in the form, then you can append it using
<script>
formdata.append('someProperty', 'SomeValue');
</script>
**Example Usage :**
View :
#using (Html.BeginForm("Create", "Issue", FormMethod.Post,
new { id = "frmCreate", enctype = "multipart/form-data" }))
{
#Html.LabelFor(m => m.FileAttachments, new { #class = "editor-label" })
#(Html.Kendo().Upload()
.HtmlAttributes(new { #class = "editor-field" })
.Name("files")
)
}
<script>
$(function () {
$('form').submit(function (event) {
event.preventDefault();
var formdata = new FormData($('#frmCreate').get(0));
$.ajax({
type: "POST",
url: '#Url.Action("Create", "Issue")',
data: formdata,
dataType: "json",
processData: false,
contentType: false,
success: function (response) {
//code omitted for brevity
}
});
});
});
</script>
*Controller :*
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] Model viewModel, IEnumerable<HttpPostedFileBase> files)
{
//code omitted for brevity
return Json(new { success = false, message = "Max. file size 10MB" }, JsonRequestBehavior.AllowGet);
}
<script>
$(function () {
$('form').submit(function (event) {
event.preventDefault();
var formdata = new FormData($('#createDetail').get(0));
$.ajax(
{
type: 'POST',
url: '#Url.Action("Detail_Create", "Product")',
data: formdata,
processData: false,
success: function (result) {
if (result.success == false) {
$("#divErr").html(result.responseText);
} else {
parent.$('#CreateWindowDetail').data('kendoWindow').close();
}
},
error: function (xhr, ajaxOptions, thrownError) {
$("#divErr").html(xhr.responseText);
}
});
});
});
#using (Html.BeginForm("Detail_Create", "Product", FormMethod.Post, new { id = "createDetail", enctype="multipart/form-data"}))
{
<div id="divErr" class="validation-summary-errors"></div>
<fieldset>
<ol>
<li>
#Html.LabelFor(m => m.Price)
#(Html.Kendo().NumericTextBoxFor(m => m.Price).Name("Price").Format("{0:0}")
.HtmlAttributes(new { style = "width:100%" })
)
</li>
<li>
#Html.LabelFor(m => m.Description)
#Html.TextBoxFor(model => model.Description, new { #class = "k-textbox", id = "Description", style = "width:100%;" })
</li>
<li>
#Html.LabelFor(m => m.Group)
#(Html.Kendo().ComboBox()
.Name("Group")
.Placeholder("Введите группу детали")
.DataTextField("Name")
.DataValueField("Id")
.HtmlAttributes(new { style = "width:100%;" })
.Filter("contains")
.MinLength(1)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("Group_Read_ComboBox", "Product");
})
.ServerFiltering(true);
})
)
</li>
<li>
#Html.LabelFor(m => m.Image)
#(Html.Kendo().Upload()
.Name("files")
)
</li>
</ol>
</fieldset>
<button type="submit" id="get" class="k-button">Добавить</button>
}
[HttpPost]
public ActionResult Detail_Create(DetailModel model, IEnumerable<HttpPostedFileBase> files)
{
string error = string.Empty;
if (ModelState.IsValid)
{
.....
}
IEnumerable<System.Web.Mvc.ModelError> modelerrors = ModelState.SelectMany(r => r.Value.Errors);
foreach (var modelerror in modelerrors)
{
error += "• " + modelerror.ErrorMessage + "<br>";
}
return Json(new { success = false, responseText = error }, JsonRequestBehavior.AllowGet);
}
after pressing the button, the controller null comes to how to fix. 2 days already sitting, and the time comes to an end
I have some ajax forms in my page and I need to get the form Id or some element inside the form when the OnSuccess function is called, example:
<li>
#using (Ajax.BeginForm(new AjaxOptions
{
OnSuccess = "form.onSuccess"
}))
{
#Html.TextBoxFor(m => m.TaskId)
<button type="submit">Save</button>
}
</li>
how can I get?
Option 1:
#using (Ajax.BeginForm(new AjaxOptions{OnComplete = "DefaultEditOnComplete(xhr, status, 'Person')"}))
{
//Person data and submit button
}
function DefaultEditOnComplete(xhr, status, entityName) {
//xhr - the ajax response
//status - the response text, ex. "success"
//entityName - your custom argument, in this example 'Person'
alert('DefaultEditOnComplete fired for ' + entityName);
}
Option 2:
$('form').submit(function () {
$(this).addClass('activeForm');
});
#using (Ajax.BeginForm(new AjaxOptions{OnSuccess= "JaxSuccess(xhr, status)"}))
{
....
}
function JaxSuccess(xhr, status) {
var active = $(".activeForm");
//Do some stuff here
.....
//When Done, remove the activeForm class, making everything clean
$(".activeForm").removeClass('activeForm');
}
Option 3:
Abandon Ajax.BeginForm, and substitute for regular form and jquery pairing:
#using (Html.BeginForm("SomethingNice", "Home", FormMethod.Post, new { #id = "CoolForm", #class = "ajaxForm" }))
{
#Html.LabelFor(m => m.Rating)
#Html.TextBoxFor(m => m.Rating)
#Html.LabelFor(m => m.Comment)
#Html.TextBoxFor(m => m.Comment)
<input type="submit" value="Submit"/>
}
<script type="text/javascript">
$(function() {
$(".ajaxForm").submit(function(e) {
e.preventDefault();
var form = $(this);
var jaxUrl = form.attr('action');
var dat = form.serialize();
alert(form.attr('id'));
$.ajax({
url: jaxUrl,
data: dat,
success: function(data) {
form.parent().append(data);
},
error: function(xhr, status) {
}
});
});
});
</script>
I want to click the button on the "main view" and populate the partial view in its own . Updated
I have a main view:
#{
using (Ajax.BeginForm("PastClaims", "Claim", FormMethod.Post, new AjaxOptions { UpdateTargetId = "update_panel", InsertionMode = InsertionMode.Replace }, new { #class = "form-horizontal" }))
{
<legend>Submit a Claim</legend>
#Html.EditorForModel()
<div class="controls">
<input id="btnCheckForClaims" type="submit" class="btn btn-primary" value="Submit Claim" />
</div>
}
I need to click the submit button then display this other view:
If I do below then on the page on the initial load it displays the div
<div id="update_panel">#Html.Partial("PastClaims")</div>
If I leave the div blank then I get a new partial view.
<div id="update_panel"></div>
if I leave them null like darrin suggested:
using (Ajax.BeginForm("PastClaims", "Claim", FormMethod.Post, new AjaxOptions { UpdateTargetId = "update_panel", InsertionMode = InsertionMode.Replace }, new { #class = "form-horizontal" }))
I am redirected to the same page (what I want) but the additional partial view is not displayed.
Thanks to Darrin below to get me this far.
You could use an Ajax.BeginForm in this case:
#using (Ajax.BeginForm(null, null, new AjaxOptions { UpdateTargetId = "update_panel", InsertionMode = InsertionMode.Replace }, new { #class = "form-horizontal"}))
{
#Html.EditorForModel()
<div class="controls">
<input id="btnOpenPartialView" type="submit" class="btn btn-primary" value="OpenPartialView" />
</div>
}
<div id="update_panel">
#Html.Partial("PartialView")
</div>
and then the corresponding controller action will return the partial view:
[HttpPost]
public ActionResult SomeAction()
{
// some processing ...
return PartialView("PartialView");
}
For Ajax.* helpers to work don't forget to include the jquery.unobtrusive-ajax.js script:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
#Html.Partial is server code, if you want load partial after submit, then try this:
$("form").on('submit', function(event){
event.preventDefault();
var form = $(this);
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
data: form.serialize(),
success: function(r) {
$('#update_panel').html(r);
}
});
});