Razor Pages - reload Partial View on ajax success - c#

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)

Related

asp.net mvc if else not working in view

This is my part of code from view called index
<div class="box-body">
#{ Html.RenderAction("_BlogsGrid",new {country=""});}
</div>
...
...
<script>
$("#SelectedCountry").change(function () {
var selectedCountry = $(this).val();
$.ajax({
url: '#Url.Action("_BlogsGrid","Blog")' + '?country=' + selectedCountry,
sucess: function(xhr, data) {
console.log('sucess');
},
error: function (err) {
console.log(err);
}
});
});
</script>
Here is the controller action code
public ActionResult _BlogsGrid(string country)
{
var blogs = _blogService.GetBlogWithCountryName(country).ToList();
var blogsList = BlogMapper.ToBlogIndex(blogs);
return PartialView(blogsList);
}
and here is _BlogsGrid view
#model Blog.BlogsList
<div class="pull-right">
#Html.DropDownListFor(m => m.SelectedCountry, new SelectList(Model.CountriesList), "Select a country", new { #class = "form-control" })
</div>
<br />
<br />
#if (Model.Blogs.Count == 0)
{
<div class="box-group">
<h4>Sorry we couldn't find any blog related to the country you asked for.</h4>
#Html.DisplayText("Hello world!")
</div>
}
else
{
<div class="box-group" id="accordion">
#foreach (var blog in Model.Blogs)
{
#*Some code*#
}
</div>
}
Thing is when i load it first time everything works fine, the controllers method gets hit and all the blogs get loaded this is how the view looks
but when I select a country from dropdown list and there are no blogs matching that country
it hits the if condition(putting a breakpoint in view I check if if condition is being executed or else condition is being executed, and it goes through if condition) in view (which is a partial view)
but the content of "if" is not loaded in the browser.
I am still getting same content as before. Any idea why my content is not being updated?
Update:
<div id="grid" class="box-body">
#{ Html.RenderAction("_BlogsGrid",new {country=""});}
</div>
<script>
$("#SelectedCountry").change(function () {
var selectedCountry = $(this).val();
$.ajax({
url: '#Url.Action("_BlogsGrid","Blog")' + '?country=' + selectedCountry,
sucess: function (data) {
$('#grid').html(data);
},
error: function (err) {
console.log(err);
}
});
});
</script>
in browser response
But still my div is not updating.
As others suggested, you're not updating your div content. That's way yo don't notice a change when the AJAX call is completed.
To ease things, in addition to .ajax(), jQuery provides the .load() method, which automatically fed the returned content into the matched elements. So your javascript could look like so:
<script>
$("#SelectedCountry").change(function () {
var selectedCountry = $(this).val();
$("div.box-body").load('#Url.Action("_BlogsGrid","Blog")', 'country=' + selectedCountry);
});
</script>
Your ajax success function does nothing with the server response. It just print success in the browser console.
You need to use the server response on the success function and replace the atual content of the page with the new content.
Your html response is the first argument from success function.
You can look at jquery documentantion for better undertanding (http://api.jquery.com/jquery.ajax/)
To replace the content of your page the success function should be like that:
sucess: function(data) {
$("div.box-body").html(data);
console.log('sucess');
},
When the page is created, Razor replace all # by their value to generated the whole HTML page. This is why in debug mode, your #Html.DisplayText is hit.
However, when you load the page, the if is taken into account, and if the condition is false, you don't see the inner HTML.
To update your DOM, you have to use the data parameter of the success ajax call. This parameter is automatically set by the return of your _BlogsGrid method.
You can see it by modifying your success callback. Note that the data parameter should be the first parameter
sucess: function(data) {
console.log(data);
},
You're not updating the data after the initial load. Your ajax call returns the html content of your partial view. You'd have to update it to the DOM manually. Give your container div an id.
<div id="blogs-grid" class="box-body">
#{ Html.RenderAction("_BlogsGrid",new {country=""});}
</div>
And then in your ajax success callback:
sucess: function(data) {
$('#blogs-grid').html(data);
},
Now whatever content your ajax returned will be bound to the blogs-grid div

Simplest way to get model data/objects in the view from input with an ajax partial view call(s)

Hello i googled to find a solution to my problem, every link brought me to asp forms solution, or solutions that do not ask for form inputs, this problem has form inputs and i cant seem too find a link for help, what im asking for is simple: get the data from user input through models by ajax calls.
View (index.cshtml):
#model UIPractice.Models.ModelVariables
<!-- Use your own js file, etc.-->
<script src="~/Scripts/jquery-1.10.2.js" type="text/javascript"></script>
<div id="div1">
#Html.BeginForm(){ #Html.TextBoxFor(x => x.test1, new { style = "width:30px" })}
<button id="hello"></button>
</div>
<div id="result1"></div>
<script type="text/javascript">
//Job is to load partial view on click along with model's objects
$(document).ready(function () {
$('#hello').click(function () {
$.ajax({
type: 'POST',
url: '#Url.Action("HelloAjax", "Home")',
data: $('form').serialize(),
success: function (result) {
$('#result1').html(result);
}
});
});
});
Model (ModelVariables.cs):
public class ModelVariables
{
//simple string that holds the users text input
public string test1 { get; set; }
}
Controller (HomeController.cs):
// GET: Home
public ActionResult Index()
{
ModelVariables model = new ModelVariables();
return View(model);
}
public ActionResult HelloAjax(ModelVariables model)
{
ViewBag.One = model.test1;`
return PartialView("_MPartial", model);
}
Partial View (_MPartial.cshtml):
#model UIPractice.Models.ModelVariables
#{
<div>
<!--if code runs, i get a blank answer, odd...-->
#Model.test1
#ViewBag.One
</div>
}
So go ahead and copy/paste code to run, you will notice that you get nothing when you click the button, with user text input, odd...
I see a few problems.
Your code which use the Html.BeginForm is incorrect. It should be
#using(Html.BeginForm())
{
#Html.TextBoxFor(x => x.test1, new { style = "width:30px" })
<button id="hello">Send</button>
}
<div id="result1"></div>
This will generate the correct form tag.
Now for the javascript part, you need to prevent the default form submit behavior since you are doing an ajax call. You can use the jQuery preventDefault method for that.
$(document).ready(function () {
$('#hello').click(function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '#Url.Action("HelloAjax", "Home")',
data: $('form').serialize(),
success: function (result) {
$('#result1').html(result);
}
,error: function (a, b, c) {
alert("Error in server method-"+c);
}
});
});
});
Also, you might consider adding your page level jquery event handlers inside the scripts section (Assuming you have a section called "scripts" which is being invoked in the layout with a "RenderSection" call after jQyery is loaded.
So in your layout
<script src="~/PathToJQueryOrAnyOtherLibraryWhichThePageLevelScriptUsesHere"></script>
#RenderSection("scripts", required: false)
and in your indidual views,
#section scripts
{
<script>
//console.log("This will be executed only after jQuery is loaded
$(function(){
//your code goes here
});
</script>
}

How to call action method without view

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.

Search method issue

I'm using MVC 5, C# and I'm trying to build a search filter that will filter through upon each key stroke. It works as so, but the textbox erases after submitting. Now this is probably not the best approach to it either. Is there a way to make so when it posts it doesn't erase the textbox, or better yet, is there a better alternative?
#using (Html.BeginForm("Index", "Directory", FormMethod.Post, new { id = "form" }))
{
<p>
Search Employee: <input type="text" name="userName" onkeyup="filterTerm(this.value);" />
</p>
}
<script>
function filterTerm(value) {
$("#form").submit();
event.preventDefault();
}
</script>
I agree with the comments on your question. Posting on every key stroke would be a frustrating user experience.
So, two answers, use ajax to perform the search (which will then keep the value since the whole page will not post) or have a submit button and name the input the same as the controller action parameter.
Controller code (used with your existing code):
public class DirectoryController : Controller
{
[HttpPost()]
public ActionResult Index(string userName)
{
// make the input argument match your form field name.
//TODO: Your search code here.
// Assuming you have a partial view for displaying results.
return PartialView("SearchResults");
}
}
View Code (to replace your code with Ajax):
<p>
Search Employee:#Html.TextBox("userName", new { id = "user-name-input" })
</p>
<div id="results-output"></div>
<script type="text/javascript">
$("#user-name-input").change(function(e) {
$.ajax({
url: '#Url.Action("Index", "Directory")'
, cache: false
, type: "post"
, data: {userName: $("#user-name-input").val() }
}).done(function (responseData) {
if (responseData != undefined && responseData != null) {
// make sure we got data back
$("#results-output").html(responseData);
} else {
console.log("No data returned.");
alert("An error occurred while loading data.");
} // end if/else
}).fail(function (data) {
console.log(data);
alert("BOOOM");
});
}
</script>
A better way is to ditch your Html.BeginForm (unless you actually need it for something else) and use a pure ajax method of getting the data.
So your modified html would be:
<p>
Search Employee:
<input type="text" name="userName" onkeyup="filterTerm(this.value);" />
</p>
<script>
function filterTerm(value) {
$.ajax({
url: '#Url.Action("Index", "Directory")',
data: {
searchTerm: value
},
cache: false,
success: function (result) {
//do something with your result,
//like replacing DOM elements
}
});
}
</script>
You also need to change the action that ajax will be calling (and I have no idea why you are calling the "Index" action).
public ActionResult Index(string searchTerm)
{
//lookup and do your filtering
//you have 2 options, return a partial view with your model
return PartialView(model);
//or return Json
return Json(model);
}
The best thing about this ajax is there is no posting and it's async, so you don't have to worry about losing your data.

Display a loading screen using anything

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();
});
});

Categories