Call Controller Method Which Return View With Ajax Call From Asp.net MVC View Page - ajax

i'm using .Net core 2.1 application, inside for loop how can i trigger controller view page without returning to ajax response,
see following code
this is my razor view
#foreach (var item in Model.CategoryList)
{
<div class="col-sm-6 col-md-4 mb-4 mb-lg-0 col-lg-2" onclick="SelectCategory(#item.CategoryId)">
<a href="#" class="popular-category h-100">
<span class="icon"><span class="#item.Icon"></span></span>
<span class="caption mb-2 d-block">#item.CategoryName</span>
<span class="number">32,891</span>
</a>
</div>
}
<script>
function SelectCategory(id) {
$.ajax({
type: 'POST',
url: '#Url.Action("ProductsList","Home")',
dataType: 'json',
data: { parentId: id }
});
}
</script>
public ActionResult ProductsList(int parentId)
{
List<PilCategory> all = new List<PilCategory>();
if(parentId > 0)
all = _context.PilCategory.Where(x=>x.ParentId == parentId).OrderBy(a => a.ParentId).ToList();
else
all = _context.PilCategory.OrderBy(a => a.ParentId).ToList();
return View(all);
}
pls someone help me, thaks for advance

how can i trigger controller view page without returning to ajax response
As far as I know, we couldn't trigger controller view page without returning to ajax response. Since the ajax will send the request to the controller view and the controller view will return the view as html response back to the ajax.
If we don't handle the response, that means current page will not change.
If you just want to pass the parentId to the ProductsList method, I suggest you could try to use window.location, this will make this page redirect to the ProductsList view which achieve refresh the whole page.
Notice: If we using ajax to call ProductsList, the whole page will not refresh. If we using window.location, it will redirect to another page or refresh the whole page.
More details about how to achieve this, you could refer to below codes:
#section Scripts{
<script>
function SelectCategory(id) {
location.href = "/Home/ProductsList?parentId=" + id;
}
</script>
}
If you want to use ajax to achieve this, I suggest you could try to handle the response by using ajax success method. Normally, we will return the partial view in controller method and then we could add it into the html page by using jquery without refreshing the page.
More details about how to achieve this, you could refer to this answer.

Related

Update the data in View with AJAX not working

I have diffrent <a> tags that normally pass a parameter to an action using asp-route="". Now i want to use AJAX t do this. each tag is supposed to pass an ID that comes from the model chats when the page loads up. (asp-route-ChatID="#Model.ID").
How can I achieve this in AJAX and pass that parameter? I tried using Data-ajax-update and Data-ajax-url but these option arent even available in Intellisense. Here is the method that i want to call
public IActionResult Chat(string ChatID)
{
Chats userchats=new Chats(); // a class that holds chats
//selecting data from the database......
return View(userchats);//returns view with the model
}
here is the <a> tag that gets clicked and it needs to pass a specific ID that comes from the model
<a > View Chat </a>
here is the <div> that needs to be updated depending on what <a> is clicked
<div id="UpdateThis"> <!--show data that comes from the method ajax calls--> </div>
How can I implement ajax here with tag helpers or any other way?
Use PartialView instead of view if you want to update div.
public IActionResult Chat(string ChatID)
{
Chats userchats=new Chats(); // a class that holds chats
//selecting data from the database......
return PartialView(userchats);//returns view with the model
}
View
<a id="btnView" data-id="1">View Chat</a>
<div id="UpdateThis"></div>
<script>
$("#btnView").click(function () {
$.post('#Url.Action("Chat")', { ChatID: $(this).data("id") }).done(function (e) {
$("#UpdateThis").html(e);
})
});
</script>

Spring mvc Ajax search Form with Thymeleaf

I'm trying to add a search form in the navbar on every page in my spring mvc web app, just like the one here on stackoverflow, and I'm having issues. Right now I have a working search functions on a couple of my pages, using the typical mvc forms. I take the inputted string and store a variable called "searchString" in an object I created called "searchForm.java". Then I try to query that inputted string in the database using spring data's findbycontaing method, and then put that result on the model, and then represent that on the view, using thymeleaf. However I think that the navbar should be done using ajax, since it's on every page and pages with other forms.
So I think I'm sending the string that was submitted to the search form in the navbar to the controller where I queried it in the repository to bring back search results, then I tried to put the search results on the model, but I get nothing, all it does is redirect me to the search page. I may not be making very much sense, but I'll show my code, and if anyone could let me know if I'm going about my problem in the right way or not, and if you guys see any errors in my code. Thanks in advance.
So here's my ajax and jquery to submit the form.
<script th:inline="javascript">
/*<![CDATA[*/
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
$(document).ready(function(){
$("#searchButton").on("click", function(ev) {
$.ajax({
url : "navSearch",
type : "post",
data : {
"newSearch" : $("#newSearch").val()
},
success : function(data) {
console.log(data);
},
error : function() {
console.log("There was an error");
}
});
});
});
/*]]>*/
</script>
There may be an issue here, because in the console in the chrome developer tools, before it redirects, a message pops up very quickly that says uncaught TypeError: Cannot read property 'toLowerCase' of undefined, and it's coming from jquery.min.js:5 so that could be my issue, but I have no idea how to go about fixing this, and I've searched for answers so far with no luck.
Here's my html form, I think this shouldn't be a problem, but who knows, so I'll put it up anyways. And I'm using thymeleaf for this view.
<form action = "setSearch" class="navbar-form navbar-right">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" id="newSearch"></input>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</div>
<button type="submit" class="btn btn-default" id="searchButton">Search</button>
</form>
Here's my searchForm.java class, where I temporarily store the string to be queried in the database.
public class SearchForm {
private String searchString;
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
}
And Here's my controller, where I'm trying to handle the ajax submission and return it as search results on the setSearch.html page. What I'm thinking here is that the string "newSearch" from the form could be matched using the Spring Data query methods, and then be able to return it and add it to the model, but it's not working, it's just redirecting me to the /searchSet page with no data, because that's where the form action goes and that's what I tell it to return. So honestly I'm no sure if any data is even getting to this point.
#RequestMapping(value="setSearch/navSearch", method=RequestMethod.POST)
public #ResponseBody String navSearch (#RequestParam String newSearch, ModelMap model)
{
List<QuestionAnswerSet> questionAnswerSetByQuestion = (List<QuestionAnswerSet>) questionAnswerSetRepo.findByQuestionContaining(newSearch);
model.put("searchResult", questionAnswerSetByQuestion);
return "setSearch";
}
And here's an example of a working search method that I have in my controller that I use on a regular form, with no ajax, on the /searchSet page.
#RequestMapping(value="/setSearch", method=RequestMethod.GET)
public String searchGet(ModelMap model) {
SearchForm searchForm = new SearchForm();
model.put("searchForm", searchForm);
return "setSearch";
}
#RequestMapping(value="/setSearch", method=RequestMethod.POST)
public String searchPost(#ModelAttribute SearchForm searchForm, ModelMap model) {
List<QuestionAnswerSet> questionAnswerSetByQuestion = (List<QuestionAnswerSet>) questionAnswerSetRepo.findByQuestionContaining(searchForm.getSearchString());
model.put("searchResult", questionAnswerSetByQuestion);
return "setSearch";
}
UPDATE
I've changed my code in the form from <button type="submit" class="btn btn-default" id="searchButton">Search</button> to <button type="button" class="btn btn-default" id="searchButton">Search</button> and now I get the Uncaught TypeError: Cannot read property 'toLowerCase' of undefined from earlier and nothing happens with the page.
UPDATE
I can now submit the ajax form without a problem, I was missing meta tags in the header, so the csrf wasn't submitting correctly, so now I get this error in the chrome developer tools console XHR Loaded (navSearch - 405 Method Not Allowed - 7.265999971423298ms - 634B)
UPDATE
Now everything works on the Ajax side, I needed to adjust my url to match the url I had in the request mapping on the controller and it runs through all the code fine. However the overall search function still doesn't work, here's my updated controller.
I know my issue here is that I'm returning a string and not an object, but I'm not sure how to return the object and then redirect the url to the /setSearch page. It's running through the code and returning a string "setSearch" in the console, because I told it to at the end of the controller.
#RequestMapping(value="/setSearch/search", method=RequestMethod.POST)
public #ResponseBody String search (#RequestParam String newSearch, ModelMap model)
{
List<QuestionAnswerSet> questionAnswerSetByQuestion = (List<QuestionAnswerSet>) questionAnswerSetRepo.findByQuestionContaining(newSearch);
model.put("searchResult", questionAnswerSetByQuestion);
return "setSearch";
}
Here's my working ajax
<script th:inline="javascript">
/*<![CDATA[*/
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
$(document).ready(function(){
$("#searchButton").on("click", function(ev) {
$.ajax({
url : "/setSearch/search",
type : "post",
data : {
"newSearch" : $("#newSearch").val()
},
success : function(data) {
console.log(data);
},
error : function() {
console.log("There was an error");
}
});
});
});
/*]]>*/
</script>
but it's not working, it's just redirecting me to the /searchSet page
with no data, because that's where the form action goes and that's
what I tell it to return
You are right, it is because you are submitting the form and in the action you specify it to submit to setSearch, that is why the page is getting redirected to the same page. Just replace button type="submit" with button type="button" so that the form will not be submitted when searchButton is clicked.

Spring controller - send html as response

I have a loaded jsp page with products and addtocart link against it. I am trying to show the cart in a div in the same page. I want to send html as response. This is what i have done. It just returns the string <div>output</div>. Can someone show me how i should do it.
Controller
#RequestMapping(value="/addtocart{id}", produces = "text/plain;charset=UTF-8")
#ResponseBody
public String addToCart(#PathVariable("id") int id, #ModelAttribute("cart") Cart cart,Model model)
{
Product product = productService.getProductById(id);
if (product != null) {
CartLine line = new CartLine();
line.setProduct(product);
line.setQuantity(1);
productService.updateProduct(product);
}
return "<div>output</div>";
}
JSP
<td><a id="demo4" href="addtocart${product.id}">Add To Cart</a> </td>
$('#demo4').click(function() {
$.ajax({
url : '/addtocart{id}',
dataType: 'json',
contentType: "text/html",
type : 'GET',
data :{id:id},
success : function(response) {
$('#output').html(response);
}
});
});
<div id="output" style="display:none">
<h2>Cart Content(s):</h2>
</div>
I also favor the approach with using the view, and separate page even on ajax call. Nevertheless what you're asking is possible, simply change your produces = "text/plain;charset=UTF-8" to produces
produces = "text/html;charset=UTF-8"
There are many other aspects that appear wrong, not related to Spring MVC, so even with the produces corrected you still have to do few corrections to get what you're expecting.
I think that you're not sending an ajax call at all. You're most likely doing a complete browser redirect. When first read, I was confused that "text/plain" vs "text/html" makes a difference in an ajax response, but now I believe that you're actually redirecting via browser. Change this <a id="demo4" href="addtocart${product.id}">Add To Cart</a> into something like this <a id="demo4" href="#">Add To Cart</a> and add return false to the end of your function. This will execute the function, and the return will make sure that the link is not followed
When you do this you'll notice a few issues with your ajax call as well; firstly, url : '/addtocart{id}' should be url : '/addtocart${product.id}
Capture your response in complete function not in success, and get the output as response.responseText, the response will return fine, but the browser will attempt to parse it as json and fail.
Your div will remain invisible, you should add some js to toggle that
One Spring MVC gotcha, your Cart bean seems to have a property called id as well. Because of this, your id path variable should be renamed, otherwise it'll be ignored
This would be something that, if not completly, than much closer to working
<a id="demo4" href="#">Add To Cart</a>
<div id="output"></div>
<script>
$('#demo4').click(function() {
$.ajax({
url : '/addtocart${product.id}',
dataType: 'json',
contentType: "text/html",
type : 'GET',
data :{id:4},
complete: function(response) {
$('#output').html(response.responseText);
}
});
return false;
});
</script>
Renaming PathVariable
#RequestMapping(value="/addtocart{productId}", produces = "text/plain;charset=UTF-8")
public String addToCart(#PathVariable("productId") int productId, #ModelAttribute("cart") Cart cart,Model model)
You can do it with AJAX and still return a separate page for your Card, as Slava suggested.
JSP page cart.jsp:
<%# page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- custom html for your cart representation, just an example -->
<div>
<h1>${cart.headline}</h1>
<p>${cart.otherProperty}</p>
</div>
Controller:
#RequestMapping(value="/addtocart{id}")
public String addToCart(#PathVariable("id") int id, #ModelAttribute("cart") Cart cart, Model model) {
doSomethingWithCart(cart);
model.addAttribute("cart", cart); // add cart to model after doing some custom operations
return "cart"; // resolved to cart.jsp by your view resolver
}
This way you are using AJAX but still you are returning dynamic html content (
adjusted to one specific cart).

Ajax UpdateTargedId / Browser doesn't refresh ASP MVC Partial View

I Have HTML Page "Index.cshtml"
<div class="container" >
<div class="row">
<div class="col-md-6" id="employeeList">
#Html.Partial("IndexPartial");
</div>
<div id="second">
other div
</div>
<button onclick="Increase()">increase</button>
</div>
</div>
partial view which contains employee table, button which clicked calls JS Script which executes controller method increasing employee age
<script>
function Increase(id) {
$.ajax({
url: 'Main/Increase',
data: { id: 5 },
UpdateTargetId:"employeeList",
success: function () {
alert('Added');
}
});
}
</script>
I ran firebug and it shows that each button click returns html response with updated employee Table (employee age is updated) but in browser there are still old values until I manually refresh page
You seem a bit confused. Let's go over a few things.
Partial is a built-in method that renders a view to a string (an IHtmlString). This runs once when the page is being constructed.
$.ajax() is a jQuery function. This function (as far as I can recall) does not accept a property called UpdateTargetId. I think you've confused that with the .NET AjaxOptions class which does accept an option called UpdateTargetId.
One way to quickly get things working is to change your success function to take a data argument and then insert the HTML into the div.
success: function (data, textStatus, xhr) { }

MVC correct handling of ajax search form and paging

I have a question regarding the ajax and simple filtering. Before writing this post i did read some question more or less regarding this topic, but it is not the same and i didn't receive my answere there!
I have a simple search page, with only few filtering, this filtering should not immidiately be adjustaed to the results or whatever. So when the user changes the filters and click search he gets the result, when he modifies filter nothing should happen until he clicks search again.
So my question is more about handling with the paging, i know how the paging works on the server side with .Skip() .Take() however when i click on the page number, i still have to do an ajax call and submit the same model back just with other page number, but the paging is outside of the form and it doesn't have submit stuff on it ... so how do i resubmit the model back with all the filteres in there?
So here is my view:
#using (Ajax.BeginForm("Index", "Search", new AjaxOptions { UpdateTargetId = "searchResults" }, new { #role = "form", #class = "form-horizontal" }))
{
#Html.AntiForgeryToken();
#Html.EditorFor(m => m.IsOnline)
#Html.EditorFor(m => m.UserLocation)
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">#Base.Search</button>
</div>
</div>
}
And the paging is:
#if(Model.TotalPages>1)
{
<div class="row">
<div class="col-sm-12 col-centered">
<ul class="pagination text-center">
<li class="disabled">«</li>
#{
for(int i = 1; i<=Model.TotalPages; i++)
{
<li><a href="#">#i
#if(Model.CurrentPage==i)
{
<span class='sr-only'>(current)</span>
}
</a></li>
}
}
<li>»</li>
</ul>
</div>
</div>
}
And here is my controller:
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult Index(SearchModel searchModel)
{
if (ModelState.IsValid)
{
searchModel = Operations.GetSearchedListUsers(searchModel);
}
return View(searchModel);
}
So how i see it, even when i click on the page number i should do an index post action and re submit model but for example with other pagenumber how should i do it?
Or is my thinking is totaly wrong and in first place i shouldn't use ajax.form and model??
Please give advice?
Lets start with a general search page.
In search page we will a SearchPage view which have partial view SearchResult view which also may have another paging controls
SearchPage view will have all the control Filter that will keep the states if search, searchResult view update when Search Button is click. To maintain the filter,search keyword and page number and other states we would need to override the default submit and handle all through the ajax calls.
SearchResult view is partial view which will be updated on the searchbutton or Page click.
We might need to create searchModel model that has all the requires attributes:Filters,keyword and Page.
To make everything in ease, we would handle all these secenario in a single action called
Search with SearchModel. The searchModel will return a partialView("SearchResult").
Regarding the ajax call, lets say we have
var keyword=$("#txtKeyword").val();
var filter1=$("#txtFilter11").val();
function SubmitSearch() {
$.ajax({
url: '/Home/Search',
type: 'POST',
data: { keyword: keyword, Filter1: filter1}
timeout: 50000,
success: function (data) {
//Process data
},
error: function (x, t, m) {
if (t === "timeout") {
alert("got timeout");
} else {
alert(t);
}
}
});
}
And you can hook in the search button, page button etc.

Resources