I want to call a action method in controller. but the actonmethod has no view.
I have this:
<div class="col-lg-6 col-md-8 col-sm-10 ">
<i class="fa fa-fw fa-check"></i> #Resources.Action.Navigation.GeneratePDF
</div>
and this is my action method:
[HttpPost]
public ActionResult GeneratePDFFFromHtml(EditProductModel model, string data)
{
SubmittedForm sf = new SubmittedForm();
string schema = requestSchema;
customer_DbConnection db = new customer_DbConnection();
RenderFormController renderController = new RenderFormController();
renderController.GeneratePdf(data, db,sf);
//return RedirectToAction(model.DesignId, "Prdocut/Edit");
return Content("It works");
}
If you want do to some work based off a button/link, why not just use an ajax call?
For example:
<div class="col-lg-6 col-md-8 col-sm-10 ">
<button onclick=GeneratePdf('#Model.DesignId', <string data>) class="btn btn-primary enabled"><i class="fa fa-fw fa-check"></i> #Resources.Action.Navigation.GeneratePDF </button>
</div>
And in your .cshtml, I would recommend having a Script section at the bottom of the file:
#section Scripts{
<script type="text/javascript">
//modify as needed to make it pass in what you need.
function GeneratePdf(designId, stringData) {
$.ajax({
url: "#Url.Action("GeneratePDFFFromHtml","Product")",
data: { designId: designId, strData: stringData },
cache: false,
contentType: false,
processData: false,
type: "POST",
success: function (data) {
//TODO: Add whatever if you want to pass a notification back
},
error: function(error) {
//TODO: Add some code here for error handling or notifications
}
}
</script>
}
Then, in your controller, you can have your function return void. NOTE: I am not really sure if you even need to pass in the DesignId but you have it in there so I will keep it there. You will most-likely need to edit this method some more to make it work properly but hopefully this will get you going.
[HttpPost]
public void GeneratePDFFFromHtml(string designId, string strData)
{
SubmittedForm sf = new SubmittedForm();
string schema = requestSchema;
customer_DbConnection db = new customer_DbConnection();
RenderFormController renderController = new RenderFormController();
renderController.GeneratePdf(strData, db, sf);
//return RedirectToAction(model.DesignId, "Prdocut/Edit");
}
Also, this will be async so you may want some notification to the user that some action is being done like a spinner.
Try with System.Web.Mvc.EmptyResult or redirecting to same page with
System.Web.Mvc.RedirectResult.
Easier way is to use routing, route attribute with your controller as below.
In your controller simply decide the needed route and add it. I've used ImportExport
[Area("Exporting")]
[Route("api/ImportExport")]
public class ImportExportController : Controller
{
............
[Route("GeneratePDF")]
[HttpPost]
public void GeneratePDFFFromHtml(string designId, string strData)
{
SubmittedForm sf = new SubmittedForm();
string schema = requestSchema;
customer_DbConnection db = new customer_DbConnection();
RenderFormController renderController = new RenderFormController();
renderController.GeneratePdf(strData, db, sf);
//return RedirectToAction(model.DesignId, "Prdocut/Edit");
}
.......
And in your cshtml page simply call this path as below
<form enctype="multipart/form-data" method="post" action="~/api/ImportExport/GeneratePDF" id="frmGenerate" novalidate="novalidate" class="form-horizontal">
.....
Thank you Andy Korneyev!!! Your sample code helped me solve something I have been struggling with all day. I used your code (and modified it slightly) to go back after running a code block function without a refresh/view.
return Content("<script type='text/javascript'>window.history.back();</script>");
I know this is really old but hopefully you see this.
Related
I started learning AJAX like this week and I was trying to make a simple voting thingy on page in asp mvc - when you click one button you get message like a popup (in browser) and count increments, when you click second, you get another count decrements, you get the idea.
I wanted to test it's possible to do like voting system (upvotes/downvotes) that will update itself's oount on click without needing to refresh the page.
However, when I click on this buttons, it gets me blank page with the things that return json contains. (picture included at the very bottom of post).
I am most likely missing something obvious, so please bear with me and if you could navigate me where am I wrong, please do.
My Controller:
public IActionResult Privacy()
{
Vote vote = new Vote();
vote.Votes = 0;
return View(vote);
}
[HttpPost]
public ActionResult VoteUp(string plus, string minus)
{
Vote vote = new Vote();
if (plus == null)
{
vote.Votes = vote.Votes -1;
var message = "You voted down";
return Json(new { success = true, message = message }, new Newtonsoft.Json.JsonSerializerSettings());
}
else if ((minus == null))
{
vote.Votes = vote.Votes +1 ;
var messagez = "You voted up";
return Json(new { success = true, message = messagez }, new Newtonsoft.Json.JsonSerializerSettings());
}
else { }
var messagebad = "STH WENT WRONG";
return Json(new { success = true, message = messagebad }, new Newtonsoft.Json.JsonSerializerSettings());
}
My View:
#model JSON_RETURN.Models.Vote
#addTagHelper*, Microsoft.AspNetCore.Mvc.TagHelpers
#{
ViewData["Title"] = "sssss";
}
<form asp-action="VoteUp" asp-controller="Home" method="POST" data-ajax="true">
<div class="form-group"> </div>
<div class="input-group-button">
<button name="plus" class="btn btn-dark" onclick="" value="1" >+</button>
#Model.Votes
<button name="minus" class="btn btn-dark" onclick="" value="-1" >-</button>
</div>
</form>
#section scripts{
<script src="~/lib/ajax/jquery.unobtrusive-ajax.js"></script>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script type="text/javascript">
function SubmitForm(form) {
form.preventDefault();
$.ajax({
type: "POST",
url: "HomeController/VoteUp", //form.action,
data: ('#form'),
success: function (data) {
if (data.success) {
alert(data.message);
} else {
}
},
});
};
</script>
}
My Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace JSON_RETURN.Models
{
public class Vote
{
public int Votes { get; set; }
}
}
And there's the blank page I'm getting every click (message varies ofc):
(https://imgur.com/uVNSmE6)
What you did is just a form submit instead of using ajax. Why it return json string that is because you return json string in your backend code(return Json(new { success = true, message = messagebad }, new Newtonsoft.Json.JsonSerializerSettings());).
I saw you use jquery.unobtrusive-ajax.js in your code, also you create a js function with ajax. Actually, you just need to choose one of the two ways to achieve your requrement.
Here is the correct way of using jquery.unobtrusive-ajax.js :
Note:
1.If you use asp.net core, it contains jquery.js in _Layout.cshtml by default. So when you use #section Scripts{}, no need add the jquery.js again. If your _Layout.cshtml does not contain jquery.js, you need add this js file before jquery.unobtrusive-ajax.js:
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/ajax/jquery.unobtrusive-ajax.js"></script>
2.You need specific data-ajax-update to tell the elements where need to be updated with the AJAX result.
More supported data attributes for jquery.unobtrusive-ajax.js you can refer to here.
View:
#model Vote
#addTagHelper*, Microsoft.AspNetCore.Mvc.TagHelpers
#{
ViewData["Title"] = "sssss";
}
<div id="result"> //add this div...
//add this...
<form asp-action="VoteUp" asp-controller="Home" method="POST" data-ajax-update="#result" data-ajax="true">
<div class="form-group"> </div>
<div class="input-group-button">
<button name="plus" class="btn btn-dark" value="1">+</button>
#Model.Votes
<input hidden asp-for="Votes" /> //add this....
<button name="minus" class="btn btn-dark" value="-1">-</button>
</div>
</form>
</div>
#section scripts{
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js" integrity="sha256-v2nySZafnswY87um3ymbg7p9f766IQspC5oqaqZVX2c=" crossorigin="anonymous"></script>
}
Controller:
Note: You can see that I add a hidden input for Votes in form, that is because only input or select type of element can be post to backend. The reason for why I want to get Votes value is because your code always create a new instance for Vote, the value will always plus start with 0.
public IActionResult Privacy()
{
Vote vote = new Vote();
vote.Votes = 0;
return View(vote);
}
[HttpPost]
public ActionResult VoteUp(string plus, string minus)
{
Vote vote = new Vote();
vote.Votes = int.Parse(Request.Form["Votes"]);
if (plus == null)
{
vote.Votes = vote.Votes - 1;
}
else if ((minus == null))
{
vote.Votes = vote.Votes + 1;
}
else { }
return PartialView("Privacy", vote);
}
Result:
In my view (let's name this view - Main/Index) I have list with some items on the left side (used Component), and main div on the right side, which has partial view inside.
When I click some item on the left side, I want to send parameter to the method in Main/Index class, process this data and send it back as json, but also refresh the partial at the same time.
Partial is getting data from this view.
Really, I don't know the way I try to do this is good, maybe will you have some suggestions for me? Would be great.
Anyway my code is:
Main/Index.cshtml
<div class="row">
<div class="col-3">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
#await Component.InvokeAsync("DisplayBar")
</div>
</div>
<div class="col-9">
<div id="mainDiv">
<partial name="~/Pages/Shared/_MainContent.cshtml" />
</div>
</div>
</div>
Main/Index.cshtml.cs
public class IndexModel : PageModel
{
public string ParamValue{ get; set; }
public void OnGet() {}
public JsonResult OnPostRefreshMain(string paramValue)
{
ParamValue= DictionaryHelper.TranslatedParamsDict[paramValue];
return new JsonResult(ParamValue);
}
}
Shared/_MainContent.cshtml (partial)
#model Pages.Main.IndexModel
#{
var paramValue = string.IsNullOrEmpty(Model.ParamValue) ? "No param" : Model.ParamValue;
}
<h5>#paramValue selected</h5>
js script
$(document).on('click', 'a.nav-link.appsList-element:not(.active)', function () {
var paramValueResult = 'some id';
$.ajax({
url: '/Main/Index?handler=RefreshMain',
type: 'post',
dataType: 'json',
data: {
paramValue: paramValueResult
},
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
success: function (obj) {
$('#mainDiv').load('/Shared/_MainContent.cshtml');
}
});
});
Now js is calling my method fine, method is returning translated data, but the partial is not reloading.
In the future I will get many data depends on this paramValue - I will display statistics of product related to this paramValue, so I'm not sure my solution is ok and maybe is better way to do this?
you have to populate view with jquery you get the json from controller in success of ajax so <h5>#paramValue selected</h5> this should have new value that is returned so you can use some sort of identity to h5 element and change it's content with new value so in the success function $('h5'),html('<h5>'+what you want to show here+' selected</h5>') and you received the json in obj you can see what is in obj by console.log(obj); in success function
in short you can send the whole html from controller and just replace the html content $('#parentDivId').html(data); so in this case ajax data type should be 'text/html' or you can get the json and put json content where ever you want which been described earlier
btw I said you can transfer whole page so in this case controller should return html (return type should be accordingly could be View)
C# asp.net MVC project: I have my index page with a button in it, I want to press it and update the same page with some results.
Here's some code:
The View: (with a button that calls the getConfig method in the controller)
#{
ViewBag.Title = "Home Page";
}
<form method="get" action="/Home/GetConfig/" >
<input type="submit" value="Get Config WS" />
</form>
<p>
#ViewBag.totalRecords
</p>
The controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Test webservices";
return View();
}
public void getConfig()
{
string totalRecords = string.Empty;
wsConfig.config_pttClient client = new wsConfig.config_pttClient();
wsConfig.getConfigInput gci = new wsConfig.getConfigInput();
wsConfig.getConfigOutput gco = new wsConfig.getConfigOutput();
gco = client.getConfig(gci);
totalRecords = gco.result.totalRecords.ToString();
ViewBag.totalRecords = totalRecords;
}
I want to press the view's button and show the totalRecords on the same page.
How can I achieve this?
Edit: There might be other solutions, (if you don't mind updating your entire page) but this how I generally do it.
Ok, there are a couple of things that you need to change in order to make it work:
Create a new partial view that contains just the part that you would like to update (and wrap it an element with an id). In this example, let's call it 'Partial_TotalCount'.
This partial view will contain the following code:
<div id="updated">
<p>
#ViewBag.totalRecords
</p>
</div>
Now, change your original view so that it includes the partial view:
#{
ViewBag.Title = "Home Page";
}
<form method="get" action="/Home/GetConfig/" >
<input type="submit" value="Get Config WS" />
</form>
#Html.Partial("Partial_TotalCount", #Model)
Now, update your controller to work with an ajax request. This would make your controller looks like:
public ActionResult Index()
{
ViewBag.Message = "Test webservices";
if (Request.IsAjaxRequest())
{
getconfig();
return PartialView("Partial_TotalCount");
}
return View();
}
Now, you need to be able to submit the page when you click the button. This can be done through javascript:
First your javascript function that will update the contents:
<script type="text/javascript">
function Refresh() {
$.ajax({
url: '/Home/Index',
type: "GET",
dataType: "html",
success: function(data) {
$("#updated").html(data);
},
error: function() { alert('Refreshing error.'); }
});
}
</script>
You just need to add an onclick on your button. And you can remove the form tags from around your form aswell.
Edit: As requested by the questioner, I provide a bit of explanation on the Javascript function itself:
$.ajax means that we are doing an Ajax request. It means that we are doing some asynchronous requests with the server.
Then a couple of parameters are passed:
Url: The url that should be executed. In your example, the code behind the url "Home/GetConfig" get's executed.
Type: The type of submit that you want to do (POST, GET, ...)
dataType: The type we are expecting back from the server.
Success: The piece of javascript that needs to execute when complete. (In this case, update the DIV element with the id "WithCss" with the contents that are received with the url "Home/Getconfig".
Error: A function that is executed when the request failed for some reason.
There are a lot of other parameters you can pass (for example if you need to pass an id, and others.
For more explanation, please look at the original documentation.
Also, consider marking this answer as accepted.
I hope it works.
Try This:
Replace your input button code with the following code :
<input type="submit" id="btnSave" name="BtnSave" value="Get Config WS" />
Then in controller change the whole code for this code:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Test webservices";
return View();
}
public ActionResult getConfig()
{
return View();
}
[HttpPost]
public ActionResult getConfig(FormCollection Form)
{
if(Form["BtnSave"]!=null)
{
string totalRecords = string.Empty;
wsConfig.config_pttClient client = new wsConfig.config_pttClient();
wsConfig.getConfigInput gci = new wsConfig.getConfigInput();
wsConfig.getConfigOutput gco = new wsConfig.getConfigOutput();
gco = client.getConfig(gci);
totalRecords = gco.result.totalRecords.ToString();
ViewBag.totalRecords = totalRecords;
}
return View();
}
Hopefully it works...!
I'm clearly missing something but can't for the life of me see what it is so would appreciate if anyone could point out my error.
I have a simple details page with a form to add comments to the selected detail.
I have a view with the following formed contained within it:
#using (Html.BeginForm("Details", "Home", FormMethod.Post, new { id ="commentForm" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.NewComment.Name);
#Html.TextBoxFor(model => model.NewComment.Name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewComment.Body);
#Html.TextAreaFor(model => model.NewComment.Body, new { #class = "form-control" })
</div>
<input type="submit" class="btn btn-primary" value="Add Comment" />
}
This view then calls the following c# controller method:
[HttpPost]
public ActionResult Details(int id,DetailsViewModel model)
{
if (!ModelState.IsValid)
return View(model);
var content =_data.First(c => c.Id == id);
content.Comments.Add(model.NewComment);
return View(new DetailsViewModel(content));
}
If I use the form without adding any additional code to catch the submit with jquery then this all works correctly.
When i add the following JQuery code to the page then the above server code is not executed (I know i am not actually returning any json in the above method but if the method is not executed that seems redundant for now?):
$(document).ready(function () {
$("#commentForm").submit(function (event) {
event.preventDefault();
var url = $(this).attr('action');
$.getJSON(url, $(this).serialize(), function (comment) {
alert(comment)
});
});
});
If is also worth noting that if i add any alerts around the getjson call then these all fire correctly.
Does anyone have any ideas about what i'm doing wrong?
When you are using .getJSON it makes a GET request, and your Details method only answers POST requests.
Try this instead:
$.ajax({
type: "POST",
url: url,
data: $(this).serialize(),
success: function(comment) {
alert(comment);
}
});
Try posting to the controller.
$.getJSON is performing a http get under the covers. Your controller endpoint is expecting a post and will not accept a http get.
Here is a function(blog reference) that will provide the same functionality:
(function ($) {
$.postJSON = function (url, data) {
var o = {
url: url,
type: "POST",
dataType: "json",
contentType: 'application/json; charset=utf-8'
};
if (data !== undefined) {
o.data = JSON.stringify(data);
}
return $.ajax(o);
};
} (jQuery));
Simply add this somewhere after your jQuery include.
use $.post() instead, when you use FormMethod.Post : http://api.jquery.com/jQuery.post/
I sometimes have operation that takes a while to compute. I would like to be able to display something, like a kind of grey layer covering everything, or a loading screen, while the operation computes. But I frankly have no idea how to do it.
I'm building an MVC app using MVC4, I'm beginning with jQuery and opened to any suggestions. How might I do that?
EDIT
Here's a sample of page I've been building:
<h2>Load cards</h2>
<script type="text/javascript">
$(document).ready(function () {
$("form").submit(function (event) {
event.preventDefault();
alert("event prevented"); // Code goes here
//display loading
$("#loadingDialog").dialog("open");
alert("dialog opened"); // Never reaches here.
$.ajax({
type: $('#myForm').attr('method'),
url: $('#myForm').attr('action'),
data: $('#myForm').serialize(),
accept: 'application/json',
dataType: "json",
error: function (xhr, status, error) {
//handle error
$("#loadingDialog").dialog("close");
},
success: function (response) {
$("#loadingDialog").dialog("close");
}
});
alert("ajax mode ended");
});
});
</script>
#using (Html.BeginForm())
{
<div class="formStyle">
<div class="defaultBaseStyle bigFontSize">
<label>
Select a Set to import from:
</label>
</div>
<div class="defaultBaseStyle baseFontSize">
Set: #Html.DropDownList("_setName", "--- Select a Set")<br/>
</div>
<div id="buttonField" class="formStyle">
<input type="submit" value="Create List" name="_submitButton" class="createList"/><br/>
</div>
</div>
}
Here's a snippet of code from my javascript file:
$(document).ready(function ()
{
$(".createList").click(function() {
return confirm("The process of creating all the cards takes some time. " +
"Do you wish to proceed?");
});
}
As a bonus (this is not mandatory), I'd like it to be displayed after the user has confirmed, if it is possible. else I do not mind replacing this code.
EDIT
Following Rob's suggestion below, here's my controller method:
[HttpPost]
public JsonResult LoadCards(string _submitButton, string _cardSetName)
{
return Json(true);
}
And here's the "old" ActionResult method:
[HttpPost]
public ActionResult LoadCards(string _submitButton, string _setName)
{
// Do Work
PopulateCardSetDDL();
return View();
}
As of now the code never reaches the Json method. It does enter the ajax method up there (see updated code), but I don't know how to make this work out.
We hide the main content, while displaying an indicator. Then we swap them out after everything is loaded. jsfiddle
HTML
<div>
<div class="wilma">Actual content</div>
<img class="fred" src="http://harpers.org/wp-content/themes/harpers/images/ajax_loader.gif" />
</div>
CSS
.fred {
width:50px;
}
.wilma {
display: none;
}
jQuery
$(document).ready(function () {
$('.fred').fadeOut();
$('.wilma').fadeIn();
});
First you want to have jQuery "intercept" the form post. You will then let jQuery take care of posting the form data using ajax:
$("form").submit(function (event) {
event.preventDefault();
//display loading
$("#loadingDialog").dialog("open");
$.ajax({
type: $('#myForm').attr('method'),
url: $('#myForm').attr('action'),
data: $('#myForm').serialize(),
accept: 'application/json',
dataType: "json",
error: function (xhr, status, error) {
//handle error
$("#loadingDialog").dialog("close");
},
success: function (response) {
$("#loadingDialog").dialog("close");
}
});
});
More information on the $.ajax() method is here: http://api.jquery.com/jQuery.ajax/
You could use the jquery dialog to display your message: http://jqueryui.com/dialog/
There are other ways to display a loading message. It could be as simple as using a div with a loading image (http://www.ajaxload.info/) and some text, then using jQuery to .show() and .hide() the div.
Then, in your controller, just make sure you're returning JsonResult instead of a view. Be sure to mark the Controller action with the [HttpPost] attribute.
[HttpPost]
public JsonResult TestControllerMethod(MyViewModel viewModel)
{
//do work
return Json(true);//this can be an object if you need to return more data
}
You can try creating the view to load the barebones of the page, and then issue an AJAX request to load the page data. This will enable you to show a loading wheel, or alternatively let you render the page in grey, with the main data overwriting that grey page when it comes back.
This is how we do it in our application, however there is probably a better way out there...
if not I'll post some code!
EDIT: Here's the code we use:
Controller Action Method:
[HttpGet]
public ActionResult Details()
{
ViewBag.Title = "Cash Details";
return View();
}
[HttpGet]
public async Task<PartialViewResult> _GetCashDetails()
{
CashClient srv = new CashClient();
var response = await srv.GetCashDetails();
return PartialView("_GetCashDetails", response);
}
Details View:
<div class="tabs">
<ul>
<li>Cash Enquiry</li>
</ul>
<div id="About_CashEnquiryLoading" class="DataCell_Center PaddedTB" #CSS.Hidden>
#Html.Image("ajax-loader.gif", "Loading Wheel", "loadingwheel")
</div>
<div id="About_CashEnquiryData"></div>
<a class="AutoClick" #CSS.Hidden data-ajax="true" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#About_CashEnquiryData"
data-ajax-loading="#About_CashEnquiryLoading" data-ajax-loading-duration="10"
href="#Url.Action("_GetCashDetails", "Home")"></a>
</div>
Custom Javascript:
$(document).ready(function () {
// Fire any AutoClick items on the page
$('.AutoClick').each(function () {
$(this).click();
});
});