I'm making a website where a moderator can at a certain pc part. This is just a form on my website. I want the form to post trough AJAX so it doesn't refreshed after a part has been added. I'm making the application with ASP.net Core 2.0
This is my view:
#model PcBuildAddViewModel
#{
ViewData["Title"] = "Add";
}
<div class="form-container">
<form id="AddPcForm" class="form-wrapper" method="post">
<p>Name:</p>
<input asp-for="#Model.PcPart._Name" type="text" class="form-control"/>
<br/>
<p >Type:</p>
<div class="form-select">
<select asp-for="#Model.PcPart._Type" asp-items="#Model.AllTypes">
</select>
</div>
<br/>
<p>Info:</p>
<input asp-for="#Model.PcPart.Information" type="text" class="form-control"/>
<br/>
<p>Properties:</p>
<div class="form-select">
<select asp-for="#Model.Properties" asp-items="#Model.AllProperties" multiple="multiple">
</select>
</div>
<br/>
<p>Image:</p>
<input asp-for="#Model.Image" type="file" class="inputfile inputfile-1"/>
<label asp-for="#Model.Image">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" viewBox="0 0 20 17">
</svg> <span>Choose a fileā¦</span>
</label>
<br/>
<button class="btn btn-1 btn-1e" type="submit">
<span>Add</span>
</button>
</form>
</div>
My viewmodel that i want to post trough AJAX:
public class PcBuildAddViewModel
{
public List<SelectListItem> AllProperties { get; } = new List<SelectListItem>();
public List<SelectListItem> AllTypes { get; } = new List<SelectListItem>();
public IFormFile Image { get; set; }
public PcPart PcPart { get; set; }
public List<int> Properties { get; set; }
public PcBuildAddViewModel()
{
}
public PcBuildAddViewModel(List<Propertie> allProperties, List<string> allTypes)
{
foreach (string type in allTypes)
{
SelectListItem listItem = new SelectListItem
{
Text = type,
Value = type
};
AllTypes.Add(listItem);
}
foreach (Propertie propertie in allProperties)
{
SelectListItem listItem = new SelectListItem
{
Text = propertie._Value,
Value = propertie.Id.ToString()
};
AllProperties.Add(listItem);
}
}
}
My Action Post method where I want to receive the form:
[HttpPost]
public IActionResult AddPcPart(PcBuildAddViewModel viewModel)
{
return RedirectToAction("Add");
}
And finnaly my AJAX:
$('#AddPcForm').on('submit', function(event) {
var viewModel = $(this).serialize();
ajaxPost("/PCBuild/AddPcPart", viewModel);
event.preventDefault();
});
function ajaxPost(ul, dt) {
$.ajax({
type: "POST",
url: ul,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: dt,
success: ajaxSucces,
error: function(result) {
console.log(result);
}
});
}
I hope that someone can help me. I've tried this without AJAX and it worked fine but when I call trough AJAX. I get a empty viewmodel or when I put [Frombody] in front of my parameter in the controller i get a null viewmodel.
Thanks to Stephen Muecke that posted the answer. The problem is that the contentType needs to be the default so I just removed the contentType parameter in my AJAX post. After that I removed the [FromBody] because that wasn't needed anymore.
Related
I faced this problem here , but I can not solve it . I searched a lot and tried every solution I found on the similar post . so if there is any help to my case . my part of my app which I found the problem in , first here is my view , I have Categories dropdown when I choose a category I will load the property of that value in a table.
#model Demo.Models.ViewModel.DeviceVM
<form method="post">
<input hidden asp-for="#Model.device.ID" />
<div class="border p-3">
#*putting the page label*#
<div class="row">
<h3 class="text-info pl-3 pb-3">Create Device</h3>
</div>
#*fifth Row => Category List*#
<div class="form-group row">
#*field Label*#
<div class="col-4">
<label asp-for="#Model.device.CategoryID"></label>
</div>
#*field Text*#
<div class="col-8">
<select asp-for="#Model.device.CategoryID" asp-items="#Model.CategoryDropDown" class="form-control"
id="CategoryList">
<option value="">-- Select Category --</option>
</select>
<span asp-validation-for="#Model.device.CategoryID" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label>Category Properties</label>
</div>
<div class="col-8">
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tbody id="plist">
</tbody>
</table>
</div>
</div>
#*Seventh Row => Buttons*#
<div class="form-group">
<div class="col-8 offset-4 row">
#*Save Button*#
<div class="col">
<input type="submit" value="Save" asp-action="Create" class="btn btn-info w-100" />
</div>
#*Back Button*#
<div class="col">
<a class="btn btn-success w-100" asp-action="Index">Back</a>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
#section Scripts
{
<script>
$(function ()
{
$('#CategoryList').on("change", function () {
var _category = $('#CategoryList').val();
var obj = {CategoryID:_category};
AjaxCall("GetCategoryProperty", JSON.stringify(obj), 'POST').done(function (response) {
console.log(JSON.stringify(obj));
if (response.length > 0)
{
console.log("i'm here ");
}
}).fail(function (error) {
alert(error.StatusText);
});
});
});
function AjaxCall(url, data, type) {
return $.ajax({
url: url,
type: type ,
data: data ,
contentType: 'application/json; charset=utf-8',
dataType:'json'
});
}
</script>
}
here is my Category Model
public class Category
{
[Key]
public int ID { get; set; }
[Required,MaxLength(15),Display(Name ="Category Name")]
public string CatName { get; set; }
public virtual ICollection<Category_Property> categoryprperties {get;set;}
}
here is my Function in the view which always receive 0 in it's parameter
[HttpPost]
public JsonResult GetCategoryProperty([FromBody]int CategoryID)
{
DeviceVM obj = new DeviceVM();
var _CategoryProperty = (from cp in _db.Category_Property
join p in _db.Property on cp.PropertyID equals p.ID
where cp.CategoryID == CategoryID
select new { cp.CategoryID, p.Description, cp.PropertyID });
return Json(_CategoryProperty );
}
I opened the inspect in the browser I it did not reach the message inside the if block because ajax always send 0 for the category id , which I asking for a help to get work.
Two ways you can achieve your requirement.
The first way you can post the id by form like below:
1.Change JSON.stringify(obj) to obj and remove contentType: 'application/json; charset=utf-8',:
$(function ()
{
$('#CategoryList').on("change", function () {
var _category = $('#CategoryList').val();
var obj = {CategoryID:_category};
//change here...
AjaxCall("/home/GetCategoryProperty", obj, 'POST').done(function (response) {
console.log(JSON.stringify(obj));
if (response.length > 0)
{
console.log("i'm here ");
}
}).fail(function (error) {
alert(error.StatusText);
});
});
});
function AjaxCall(url, data, type) {
return $.ajax({
url: url,
type: type ,
data: data ,
//contentType: 'application/json; charset=utf-8',
dataType:'json'
});
}
2.Remove [FromBody] or add [FromForm]:
public class HomeController : Controller
{
[HttpPost]
public JsonResult GetCategoryProperty(int CategoryID)
{
//...
}
}
The second way you can post it by body, you need create a model like below then reserve your js code:
public class Test
{
public int CategoryID { get; set; }
}
Change your action:
public class HomeController : Controller
{
[HttpPost]
public JsonResult GetCategoryProperty([FromBody] TestM model)
{
//...
}
}
I trying to achieve to following: let's say I have a Create VM form. This is all very standard with stuff:
Model
[BindProperty]
public NewVM NewVM { get; set; }
NewVM Model (part of):
public string Name { get; set; }
public int SelectedTemplateId { get; set; }
public List<Templates> Templates { get; set; }
Template Model (part of):
public int Id{ get; set; }
public string Name { get; set; }
Form (part of)
<form method="post">
<div class="form-group row">
<label for="vmname" class="col-sm-4 col-form-label col-form-label-sm">Virtual Machine Name</label>
<div class="col-sm-8">
<input type="text" class="form-control form-control-sm">
</div>
</div>
</form>
A part of the Create VM form is selecting a template. This is dependent of the Cluster selected. This a presented as a dropdown box
<div class="form-group row">
<label class="col-sm-4 col-form-label col-form-label-sm">Cluster</label>
<div class="col-sm-8">
#Html.DropDownListFor(x => Model.ClusterId, new SelectList(Model.Clusters, "Id", "Name"), "Please select", htmlAttributes: new { #class = "form-control form-control-sm", id = "clusters" })
#Html.ValidationMessageFor(x => x.Tenant.ClusterId, "", new { #class = "text-danger" })
</div>
</div>
So far so good. When a cluster is selected the following Ajax call is made to retrieve the templates available for that cluster:
Javascript:
$(function () {
$("#clusters").change(function () {
var host = $(this).val();
$.ajax({
type: "POST",
url: "/VM/AddVmDC2?handler=GetTemplates",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
data: { "host": host },
success: function (response) {
$("#templatebody").html(response);
},
failure: function (response) {
alert(response.responseText);
},
error: function (response) {
alert(response.responseText);
}
});
});
});
Backend
public async Task<IActionResult> OnPostGetTemplates(int clusterid)
{
_apilocation = _config["apiurl"];
var templates = await helper.QueryApi(_apilocation, "/api/inventory/template?clusterid=" + clusterid);
NewVM.Templates = JsonConvert.DeserializeObject<List<Templates>>(templates);
return Partial("_Templates", Tenant);
}
Partial View
#model NewVM
#Html.DropDownListFor(x => x.SelectedTemplateId , new SelectList(Model.Templates, "Id", "Name"), "Please select", htmlAttributes: new { #class = "form-control form-control-sm", id = "templates" })
#Html.ValidationMessageFor(x => x.SelectedTemplateId , "", new { #class = "text-danger" })
Partial view is called in Form
<div class="form-group row">
<label class="col-sm-4 col-form-label col-form-label-sm">Templates</label>
<div class="col-sm-8" id="templatebody">
#{
<partial name="_Templates" for="NewVM"/>
}
</div>
</div>
The problem is that when I post the form SelectedTemplateId is always null. What am I doing wrong here. Can someone help me explain why SelectedTemplateId is always null.
Thanks !
You ajax data is
data: { "host": host },
and your handler is
public async Task<IActionResult> OnPostGetTemplates(int clusterid)
you need to change int clusterid to int host,so that you can get the data in ajax.
And if your SelectedTemplateId is still null,can you share your Tenant structure,and check the SelectedTemplateId in it.
I have this form:
#model CupCakeUI.Models.CupCakeEditViewModel
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "createFrm" }))
{
<div class="form-group">
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
<input type="text" id="Name" name="Name" value="#Model.Name" />
</div>
<div class="editor-label">
#Html.LabelFor(model => model)
</div>
<div class="editor-field">
<input type="text" id="Price" name="Price" value="#Model.Price" />
</div>
<div class="col-md-offset-2 col-md-10">
<input type="button" id="btnCreate" value="Create" class="btn btn-default" />
</div>
</div>
}
I am trying to use ajax post to send data to the Action Method, however its always receiving empty object. I have done that several times in the past, and now i tried different ways which not working, The code:
$(document).ready(function () {
$("#btnCreate").click(function () {
var name = $("#Name").val();
var price = $("#Price").val();
var cupCakeEditModel = { "CupCakeId": 0, "Name": name, "Price": price };
var json = JSON.stringify(cupCakeEditModel);
$.ajax({
type: 'POST',
url: "/CupCake/Create",
data: JSON.stringify(cupCakeEditModel),
contentType: 'application/json',
success: function () {
alert("succes");
},
error: function () {
alert("error");
}
});
})
})
Its showing this in the console when logging:
This is the Action Method and Class used:
[HttpPost]
public JsonResult Create (CupCakeUI.Models.CupCakeEditViewModel cupCakeEditModel)
{
var cupCake =
CupCakeData.Save(cupCakeEditModel);
return Json("cupCake",
JsonRequestBehavior.AllowGet);
}
This the class:
public class CupCakeEditViewModel
{
public int CupCakeId;
[Display(Name = "CupCake Name")]
public string Name;
public string Price;
}
I have also used this, but not working:
$("#btnCreate").click(function () {
var cupCakeEditModel =
$("#createFrm").serialize();
$.ajax({
url: "/CupCake/Create",
type: "POST",
data: cupCakeEditModel,
success: function (response) {
alert("Success");
},
error: function (response) {
}
});
})
And several answers i found on the forum, but it seems something weird!
You model contains only fields, and the DefaultModelBinder does not bind fields, only properties. Change the model to
public class CupCakeEditViewModel
{
public int CupCakeId { get; set; }
[Display(Name = "CupCake Name")]
public string Name { get; set; }
public string Price { get; set; }
}
I am new to MVC3 and I would like to create a form with input column and file upload.
The problem comes when I try to do both thing at the same time.
Here is my code
...
[HttpPost]
public ActionResult About(string inputStr)
{
string local = inputStr;
string[] word = inputStr.Split(':');
return View();
}
[HttpPost]
public ActionResult GetFile(string inputStr, HttpPostedFileBase file)
{
string filename = file.FileName;
return RedirectToAction("About");
}
These two are my controllers
#using (Html.BeginForm("GetFile", "Home", (new { inputStr = "111" }), FormMethod.Post, new { enctype = "multipart/form-data" })){
<div class="editor">
<input type="file" name="file" />
<input type="submit" value="OK" id="submitFile" class="testingSubmit"/>
</div>
}
This code works well for uploading files, and sending string "111" to the controller.
Here is another jQuery function
$('.testingSubmit').click(function () {
var totalString="";
$('.editor-field :input').each(function () {
alert($(this).val());
totalString += $(this).val().toString() + ":";
});
$('form').submit();
/* $.post("About", { inputStr: totalString}, function (data) {
});*/
});
Here, what I am trying to do is the get the user input and put it on string totalString.
I was able to post the totalString to the controller by using $.post
My questions are:
1. Am i on the right track? i.e. Is that possible to do those two tasks together with one post?
2. If not, what are the possible solution for this?
Thank you very much for your attention and hopefully this can be solved!
I think something like this should work:
#using (Html.BeginForm("GetFile", "Home", (new { inputStr = "111" }), FormMethod.Post, new { #id = "frmGetFile", enctype = "multipart/form-data" })){
<div class="editor">
<input type="file" name="file" />
<input type="hidden" name="totalString" id="totalString"/>
<input type="submit" value="OK" id="submitFile" onclick="submitGetFileForm()" class="testingSubmit"/>
</div>
}
JavaScript:
function submitGetFileForm(e)
{
e.preventDefault();
var total = //build your total string here
$('#totalString').val(total);
$('#frmGetFile').submit();
}
Controller:
[HttpPost]
public ActionResult GetFile(string totalString, HttpPostedFileBase file)
{
// your action logic..
}
I have a form submit with an AJAX post. It works fine.
But when I delete the item by clicking the delete link, I have an issue, a get request not post.
But from my javascript function, you can see I use jQuery css selctor to detect the link clicked or not, so I am confused.
Here is my code
My controller:
public class SessionsController : Controller
{
private SessionRepository _repository;
public SessionsController() : this(new SessionRepository()) { }
public SessionsController(SessionRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
var sessions = _repository.FindAll();
//for ajax requests, we simply need to render the partial
if (Request.IsAjaxRequest())
return PartialView("_sessionList2", sessions);
//return View("_sessionList", sessions);
return View(sessions);
}
[HttpPost]
public ActionResult Add(Session session)
{
_repository.SaveSession(session);
if (Request.IsAjaxRequest())
return Index();
return RedirectToAction("index");
}
[HttpPost]
public ActionResult Remove(Guid session_id)
{
_repository.RemoveSession(session_id);
return RedirectToAction("index");
}
}
The session view:
#model IEnumerable<MyMVCDemo.Models.Session>
<h2>Hijax Technique</h2>
<div id="session-list">
#{Html.RenderPartial("_sessionList2");}
</div>
<p>
</p>
#using (Html.BeginForm("add", "sessions", FormMethod.Post, new { #class = "hijax" }))
{
<fieldset>
<legend>Propose new session</legend>
<label for="title">Title</label>
<input type="text" name="title" />
<label for="description">Description</label>
<textarea name="description" rows="3" cols="30"></textarea>
<label for="level">Level</label>
<select name="level">
<option selected="selected" value="100">100</option>
<option value="200">200</option>
<option value="300">300</option>
<option value="400">400</option>
</select>
<br />
<input type="submit" value="Add" />
<span id="indicator" style="display:none"><img src="../../content/load.gif" alt="loading..." /></span>
</fieldset>
}
<label>
<input type="checkbox" id="use_ajax" />
Use Ajax?
</label>
<script src="../../Scripts/Common.js" type="text/javascript"></script>
My Partial View:
#model IEnumerable<MyMVCDemo.Models.Session>
<table id="sessions">
<tr>
<th>Title</th>
<th>Description</th>
<th>Level</th>
<th></th>
</tr>
#if(Model.Count() == 0) {
<tr>
<td colspan="4" align="center">There are no sessions. Add one below!</td>
</tr>
}
#foreach (var session in Model)
{
<tr>
<td>#session.Title</td>
<td>#session.Description</td>
<td>session.Level</td>
<td>
#Html.ActionLink("remove", "remove", new { session_id = session.Id }, new { #class = "delete" })
</td>
</tr>
}
This is my javascript which call the ajax post:
$('.delete').click(function () {
if (confirm('Are you sure you want to delete this item?')) {
$.ajax({
url: this.href,
type: 'POST',
success: function (result) {
$("#session-list").html(result);
}
});
return false;
}
return false;
});
$("form.hijax").submit(function (event) {
if ($("#use_ajax")[0].checked == false)
return;
event.preventDefault(); //prevent the actual form post
hijack(this, update_sessions, "html");
});
function hijack(form, callback, format) {
$("#indicator").show();
$.ajax({
url: form.action,
type: form.method,
dataType: format,
data: $(form).serialize(),
completed: $("#indicator").hide(),
success: callback
});
}
function update_sessions(result) {
//clear the form
$("form.hijax")[0].reset();
//update the table with the resulting HTML from the server
$("#session-list").html(result);
$("#message").hide().html("session added")
.fadeIn('slow', function () {
var e = this;
setTimeout(function () { $(e).fadeOut('slow'); }, 2000);
});
}
It looks to me like you do not rebind the click event after you update the partial.
What happends is that you replace the DOM(which the events are bound to) when you make the ajax call. So after you update post the form all your events are gone.
In jquery there is a live event that would help you here.
The code below is not tested some there might be some problems with it but it should give you an idea.
var sessionList = $('#session-list');
$('.delete', sessionList).live('click', function () {
if (confirm('Are you sure you want to delete this item?')) {
$.ajax({
url: this.href,
type: 'POST',
success: function (result) {
sessionList.html(result);
}
});
return false;
}
return false;
});
The selector $('.delete', sessionList) is to give the live function a context so it doesn't bubble events all the way to the top.