So, I've searched alot and went through alot of tutorials and even though I do everything exactly as in the tutorial, I just can't seem to get it working. Funny thing is, I have been involved in a project where we used the exact same solution and it worked.
I've got a textbox in my forum where users can search for threads in all categories where I am using ajax to show the result in a div in form of a partial view. This is working.
The problem is that I want the thread subjects that are containing the current search term to show up (in form of a normal string) while the user is typing, but I can't seem to get the implementation of autocomplete right. By the way I am retrieving my information from a MSSQL-database.
This is the javascript that I am using to autocomplete (which is not working) and below that you can see my Ajax-form that I use for the search (that works):
<link href="~/Content/jquery-ui.min.css" rel="stylesheet" />
<script src="~/Scripts/jquery-ui.min.js"></script>
#*Scripts for Ajax to show the partial view in the div with id "partialThreads" at request*#
<script src="~/Scripts/jquery-2.2.1.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script type="text/javascript">
$(function () {
$("#txtSearch").autocomplete({
source: '#Url.Action("GetThreadsBySearch", "Forum")'
});
});
</script>
#using (#Ajax.BeginForm("Threads", new AjaxOptions() { UpdateTargetId = "partialThreads", InsertionMode = InsertionMode.Replace, HttpMethod = "POST" }))
{
#Html.AntiForgeryToken()
<p><strong>Search for thread in all categories</strong></p>
#Html.TextBox("searchTerm", null, new { id = "txtSearch", style = "width: 1000px" })
<input type="submit" value="Search" />
}
Here is the div where I show the results of the search in form of a partial view:
<div id="partialThreads">
</div>
Here is the action method that I am using for my ajax-form search (the working one):
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Threads(string searchTerm)
{
var model = string.IsNullOrWhiteSpace(searchTerm)
? new List<ThreadsListModel>()
: _threadRepo.GetThreadsBySearch(searchTerm).OrderByDescending(x => x.DateCreated).ToList();
return PartialView("_Threads", model);
}
And here is the method that I use to retrieve the information to my autocomplete (I've tried setting a break point on it, it doesn't even break):
public JsonResult GetThreadsBySearch(string term)
{
var threadNames = _threadRepo.GetThreadsBySearch(term).Select(x => x.Subject).ToList();
return Json(threadNames, JsonRequestBehavior.AllowGet);
}
Note that I use the same db-query to search with the form and for the autocomplete (only difference would be that I select the threadnames as a List in the GetThreadsBySearch method. So that can't be the problem (?). Here is query-method in case you want to have a look:
public ICollection<ThreadsListModel> GetThreadsBySearch(string subject)
{
using (var context = new ForumContext())
{
return
context.Threads.Where(x => x.Subject.ToLower().Contains(subject.ToLower()) && x.IsActive)
.Select(x => new ThreadsListModel()
{
ID = x.ID,
DateCreated = x.DateCreated,
CreatedBy = x.CreatedBy,
Subject = x.Subject,
PostsCount = x.Posts.Count
}).Distinct().ToList();
}
}
Also, I am using Visual Studio 2015 (.NET 4.5.2) MVC 5. I hope that I haven't forgot to write down any helpful information.
Your scripts are in the wrong order and jquery needs to be before jquery-ui (and also ensure that you do not have any duplicated scripts)
$("#MainContent_txtCountry").autocomplete({
source: function (request, response) {
var param = { keyword: $('#MainContent_txtCountry').val() };
$.ajax({
url: "Default.aspx/GetCountryNames",
data: JSON.stringify(param),
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
dataFilter: function (data) { return data; },
success: function (data) {
response($.map(data.d, function (item) {
return {
value: item
}
}))
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
},
});
Related
I've done so many ajax in razor pages but i can't figure out why this does not work. It keeps giving me error 400 on dev tools. It does not reach the page handler no matter what.
<script>
$.ajax({
url: "/Account/Users/Index?handler=Delete",
type: "POST",
data: {
id: id
},
success: function () {
swal("Utilizador Desactivado!", {
icon: "success",
});
},
error: function (xhr, ajaxOptions, thrownError) {
swal("Falha na ligação ao servidor. Tente novamente mais tarde.");
}
});
</script>
page handler
public async Task<IActionResult> OnPostDeleteAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _context.Users.FindAsync(id);
if (user != null)
{
user.IsActivo = false;
_context.Users.Attach(user).Property( u => u.IsActivo).IsModified = true;
await _context.SaveChangesAsync();
}
return RedirectToPage("./Index");
}
I tried many url combinations and none work. I don't see what is wrong in here....
EDIT
It seems like the problem is the anti forgery token not being validated on razor page.
I wrote Ignore Anti forgery Token on the page model and everything works correctly
As you've already found out it's the anti forgery token, that is ruining your day.
Now, if you are inside a form, asp.net core will create a hidden input with that token for you. If you are not working with a form on your page, you'll have to call #Html.AntiForgeryToken(), which will add the token for you.
Still, this will not resolve the Bad Request for you. You have to add it to your ajax call:
$.ajax({
url: "/Account/Users/Index?handler=Delete",
type: "POST",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val());
},
data: {
id: id
},
});
Additionally, add this line to your Startup.cs file:
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
I don't know what you mapping of URL use, but as usual it consists of controllerName/actionName/. In your case try to use:
url: "/Account/OnPostDeleteAsync"
or
url: "/Users/OnPostDeleteAsync"
but if your URL is correct, then try to use [FromForm] attribute
public async Task<IActionResult> OnPostDeleteAsync([FromForm]int? id)
I hope this will help
I have a page with three form fields (2 textbox, 1 dropdown), a submit button and a 'refresh' link. I want to be able to click the link and pass two form textbox values to a controller action, and get a list of values to populate the dropdown box. I do not want to submit the form at this stage.
At the moment, I have managed to call the controller action from the link click, but I cannot pass the two form field values in for some reason. Also, the return JSON just takes me to a new page instead of populating my dropdown list. Any pointers would be great as I am new to javascript and MVC. My code is below;
Controller
public ActionResult Find(AddressFormViewModel model)
{
...
var temp = new List<OptionModel>();
temp.Add(new OptionModel {Id = item.Id, Value = item.desc});
return Json(temp, JsonRequestBehavior.AllowGet);
}
HTML
#Html.TextBoxFor(x => Model.HouseNameInput, new { id = "HouseNameInput" })
#Html.TextBoxFor(x => Model.PostCodeInput, new { id = "PostCodeInput" })
#Html.ActionLink("Find","Find", "Address", new { houseInput = Model.HouseNameInput, postcodeInput = Model.PostCodeInput }, new { htmlAttributes = new { #class = "Find" } })
#Html.DropDownListFor(x => Model.AddressOption, Enumerable.Empty<System.Web.Mvc.SelectListItem>(), "-- Loading Values --", new {id = "AddressOptions"})
And lastly, my Javascript method which is retrieving the data from the controller action but not populating the dropdown list (it displays the results in a new page). It is also not successfully sending the form values to the controller action.
$(function () {
$('.Find').click(function (evt) {
$.ajax({
type: 'POST',
url: '#Url.Action("Find","AddressFormSurface")',
cache: false,
async: true,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: {
houseNameInput: $("#HouseNameInput").value,
postCodeInput: $("#PostCodeInput").value
},
success: function (data) {
if (data.exists) {
var ddl = $('#AddressOptions');
ddl.empty();
data.each(function () {
$(document.createElement('option'))
.attr('value', this.Id)
.text(this.Value)
.appendTo(ddl);
});
}
},
error: function (req) {
}
});
// we make sure to cancel the default action of the link
// because we will be sending an AJAX call
return false;
});
});
You have a number of errors in your script which will cause it to fail.
You specify contentType: "application/json; charset=utf-8", but do
not stringify the data (the option should be removed)
You need to use .val() (not .value) to get the values of the
inputs
The data you receiving does not contain a property named exists so
the if block where you append the options will never be hit
In addition it is unnecessary to generate your link using #Html.ActionLink() (your adding route values based on the initial values of the model). Instead just create it manually
Find
and change the script to
var ddl = $('#AddressOptions'); // cache it
$('#find').click(function () { // change selector
$.ajax({
type: 'GET', // its a GET, not a POST
url: '#Url.Action("Find","AddressFormSurface")', // see side note below
cache: false,
async: true,
dataType: "json",
data: {
houseNameInput: $("#HouseNameInput").val(),
postCodeInput: $("#PostCodeInput").val()
},
success: function (data) {
if (!data) {
// oops
return;
}
ddl.empty();
$.each(data, function(index, item) {
$(document.createElement('option'))
.attr('value', item.Id)
.text(item.Value)
.appendTo(ddl);
// or ddl.append($('<option></option>').text(item.Value).val(item.Id));
});
},
error: function (req) {
....
}
}
});
Side note: Also check the name of the controller. Your Html.ActionLink() suggests its AddressController but your script is calling AddressFormSurfaceController
Question background:
I've implemented a search feature in the header of my MVC site. Its features a input text-box with a 'Search' Button.
The Issue:
Currently I have implemented a AJAX function in the shared master layout.cshtml view that handles the click event of the search button, as shown:
$(document).ready(function () {
$(".searchBtn").click(function () {
var $searchTerm = $("#searchInput").val();
$.ajax({
url: '#Url.Action("ProductSearch", "Product")',
type: 'POST',
data: {
"searchTerm": $searchTerm,
"pageNumber": 0
},
success: function (result) {
window.location.href = result.url;
},
failure: function () {
alert('failed');
}
});
});
});
This is the ProductSearch method of the the Product Controller the AJAX call. The search term along with the page number is sent to the controller method:
public ActionResult ProductSearch(string searchTerm, int pageNumber)
{
if (searchId == 0)
{
searchId = 1;
}
var productDetailHandler = new ProductPageDBHandler(
new ProductDetailSqlServerHandler(new ProductDetailDataSetConvertor()));
var searchList = productDetailHandler.ProductSearch(searchTerm);
return View(searchList.ToPagedList(pageNumber, 3));
}
The problem is that this seems to not be returning the view I've associated with the ProductSearch method. How do I go about correctly redirecting to thw correct view once the user has submitted their search query?
Your ajax function is calling a method that returns a view. Change you success callback to
success: function (result) {
$('#someElement').html(result);
},
This will replace the contents of <div id="someElement"></div> with the returned view.
I know, there are several questions with near the same problem. But i cant figure it out, where my mistake is.
So at first my situation:
I'll load my content with ajax and push it to the site.
$.ajax({
url: _this.url,
contentType: "application/html; charset=utf-8",
type: "GET",
dataType: "html",
data: {
stamp: Date.now()
},
success: function (result) {
_this.app = $("<div id='" + _this.frameId + "' />")
.addClass("place-top-left page")
.css({
"height": "100%",
"z-index": 4000
})
.append($(result))
.appendTo(document.body);
if (loadedCallback) {
loadedCallback();
}
},
error: function (xhr, status) {
alert(xhr.responseText);
}
});
My controller:
public ActionResult Edit(int id)
{
var howToContent = Manager.Get(id);
var howToModel = new HowToModel()
{
MediaID = howToContent.Media.ID,
HowToTitle = howToContent.Title,
Name = howToContent.Name,
BusinessIds = howToContent.BusinessIDs.Select(it => it.ToString()).ToArray()
};
return View(howToModel);
}
And my View:
#model Hsetu.Help.Web.Areas.System.Models.HowTo.HowToModel
#{
ViewBag.Title = "Leitfaden bearbeiten";
Layout = "~/Areas/System/Views/Shared/AppSite.cshtml";
}
#section ScriptCSS{
#Scripts.Render("~/uploadScripts")
}
#using (Html.BeginForm())
{
<fieldset>
<label>
#Html.EditorFor(m => m.Name)
</label>
<label>
#Html.EditorFor(m => m.HowToTitle)
</label>
<label>
#Html.EditorFor(m => m.BusinessIds)
</label>
</fieldset>
}
The Error ill get is:
The model item passed into the dictionary is of type Hsetu.Help.Web.Areas.System.Models.HowTo.HowToModel, but this dictionary requires a model item of type System.Web.Mvc.MvcHtmlString
So it will step through the Controller and the View if i debug the code. I'll also get the right model and values!
Only the result on the success function from ajax is a json return with this error message.
This works for same code but without passing this howToModel into the view.
On the other side i found out, if ill use ajaxcall like this it will also work?! Sorry.. but WTF is the diffrence between this ajaxcalls?????
$.ajax({
url: _this.url,
contentType: 'application/html; charset=utf-8',
type: 'GET',
dataType: 'html',
data: {
stamp: Date.now()
}
})
.success(function (result) {
_this.app = $("<div id='" + _this.frameId + "' />")
.addClass("place-top-left page")
.css({
"height": "100%",
"z-index": 4000
})
.append($(result))
.appendTo(document.body);
if (loadedCallback) {
loadedCallback();
}
})
.error(function (xhr, status) {
alert(xhr.responseText);
});
So my question is, how can i get the first ajaxcall working? I need it with this syntax.. so there is no way around. And maybe someone can explain me the difference between the ajax. Doesnt find a really necessary reason!
Thanks
The difference between the ajax calls is that the .error() call on the returned object of the .ajax() call returns a deferred object, which is chainable, and allows multiple handlers to be set, whereas passing in an error: function can only be done once per ajax() call -- I believe that is an older syntax.
(BTW .error() is deprecated, and you should use .fail() instead -- if you were using that approach)
Regarding your MVC (server-side) error: this is often the kind of error message I get when I attempt to pass in an property value that is null into a strongly-typed helper. Check the values of .Name, .HowToTitle, and .BusinessIds for nulls.
I am using MVC4 along with slickgrid to display data to the user. I am trying to implement the ability to double click on a slickgrid row and have the page go to another view, but all I am able to get is the HTML returned to the client, but not rendered.
I am doing,
grid.onDblClick.subscribe(function (e, args) {
$.get(
"MapSetEdit/Edit/",
{ 'mapSetId': 1 }
);
});
and I have also tried:
grid.onDblClick.subscribe(function (e, args) {
$.ajax({
type: "GET",
url: "MapSetEdit/Edit/",
dataType: 'text',
data: {'mapSetId': 1}
})
.fail(function () {
console.log("Error retreiving map list.");
});
});
All this does is return the html to the browser but never renders it. How do I make a javascript request so that I am able to actually render the view. I think I am missing something obvious here as I am new to javascript and mvc.
You should render the returned HTML with jQuery. For example:
grid.onDblClick.subscribe(function (e, args) {
$.ajax({
type: "GET",
url: "MapSetEdit/Edit/",
dataType: 'text',
data: {'mapSetId': 1}
})
.succes(function(data){
var someemptydiv = $("#myEmptyDiv");
someemptydiv.html(data);
})
.fail(function () {
console.log("Error retreiving map list.");
});
});
I was able to do what I needed with:
grid.onDblClick.subscribe(function (e, args) {
window.location = '/MapSetEdit/Edit/?mapSetId=1'
});