I have a working modal window that saves an entity to the database, my question is how to send files from the modal?
Here as my current JavaScript function:
$('#btn-save').on('click', function () {
$.ajax({
url: '#Url.Action("CreateModal", "Suggestions")',
type: 'POST',
data: $('#modal-form').serialize(),
success: function (data) {
if (data.success == true) {
$('#suggestion-modal').modal('hide');
location.reload(true);
} else {
$('.modal-content').html(data);
$('#suggestion-modal').modal('show');
}
}
});
});
This part does not send data, but works fine when not using modal and using standard MVC Create template:
<form id="modal-form" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.UserId)
<div>
#Html.LabelFor(model => model.Title)
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title, "")
</div>
<div>
#Html.LabelFor(model => model.Images, "Images")
<input type="file" name="upload" multiple />
</div>
<div>
<input id="btn-save" type="button" value="" />
</div>
</form>
I've left the rest of the partial view out as that all works correctly.
EDIT: Just added where my button was in the form. It was there, I just removed much of the code not relevant to the question - should have left the button in. Also added extra model properties - These must be sent with the file, including validation token.
EDIT: Many thanks to Jasen. I've included the JavaScript below for anyone else struggling with this.
$('#btn-save').on('click', function () {
var formData = new FormData($('form').get(0));
$.ajax({
url: '#Url.Action("CreateModal", "Suggestions")',
type: 'POST',
processData: false,
contentType: false,
data: formData,
success: function (data) {
if (data.success == true) {
$('#suggestion-modal').modal('hide');
location.reload(true);
} else {
$('.modal-content').html(data);
$('#suggestion-modal').modal('show');
}
}
});
});
Related
I have this action that returns different partial view based on the selected value from the drop down list.
Controller:
[HttpPost]
public ActionResult Foo(SomeViewModel VM)
{
var model = VM
if (Request.IsAjaxRequest())
{
if (model.SelectedValue == 1 || model.SelectedValue == 2 || model.SelectedValue == 3)
{
// ...
return PartialView("PartialView1", model);
}
else if (model.SelectedValue == 4)
{
// ...
return PartialView("PartialView2", model);
}
else (model.SelectedValue == 5)
{
// ...
return PartialView("PartialView3", model);
}
}
return View(model);
}
Main View:
<script src="~/Scripts/jquery-3.2.1.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<div>
<h2>Paint Computation</h2>
#using (Ajax.BeginForm("Foo", "Controller",
new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "Result"
}))
{
<div class="col-md-10">
<h5>Type of Paint</h5>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.DropDownListFor(m => m.SelectedValue, new SelectList(Model.PaintType, "Value", "Text"),
"Please Select", htmlAttributes: new { #class = "form-control" })
</div>
<br />
// Some HTML helpers
<input type="submit" value="Compute" class="btn btn-default" id="Submit" />
</div>
}
</div>
//This is how I render my partial view using jQuery in my main View:
<div id="Result">
<hr />
</div>
<script type="text/javascript">
$(document).ready(function () {
$('#Submit').click(function () {
$('#Result').load('/Controller/Foo');
});
});
</script>
Whenever I clicked the button, the partial view appears, but when I clicked it again for the 3rd or 4th time, the main view content stacks on the same main view. I tried to use the inspect element and that's how I determined that it stacks the same main view elements.
Is my way of calling the partial view is right? As much as possible I want to use ajax for calling the partial view every time the button is clicked. Please guide me to correct it. Thanks.
Here's the of the problem.
<script type="text/javascript">
$(document).ready(function () {
$('#Submit').click(function () {
$.ajax({
type: 'POST',
url: '/Controller/Foo',
cache: false,
contentType: "application/html; charset=utf-8",
dataType: 'html',
success: function (result) {
$('#Result').html(result);
}
});
});
});
</script>
Now it works. I changed my code and use the code above. I use .html() rather than .append() or .replaceWith(). Now every time i click the button, it changes the <div id = "Result> content.
I have a view which asks for user input. View is as below -
#(Html.Input(m => m.SchoolName).Id("SchoolName"))
#(Html.Input(m => m.Address).Id("Address"))
#(Html.Input(m => m.Phone).Id("Phone"))
<button class="btn btn-primary" name="btnSchoolSave" id="btnSave">
Submit
</button>
Then I have a javascript function, which handles the click event of the button -
function () {
$("button[name='btnSchoolSave']").on('click', function () {
$.ajax({
url: '/School/SaveSchool', //School is my controller and 'SaveSchool' is the method in the controller.
contentType: 'application/html; charset=utf-8',
type: 'POST',
dataType: 'html'
})
.success(function (result) {
alert("saved")
})
.error(function (xhr, status) {
alert(status);
})
})
};
My Controller method is like below. I have not implemented the method yet.
public void SaveSchool(Models.School model)
{
//TODO
}
My idea is - I want to get all the values inputted by the user in the View, get all those Model values, and pass it to Javascript function and javascript function in return passes the Model to the controller method and save it.
Now, I know that I can directly call my Controller action method from the view and get the saving of data taken care of. But, my requirement is to pass the data to javascript and from javascript call the method and save user input.
How can I do that?
Thanks
#model XXX.YourViewModel
<form id="your-form" style="margin: 0px;">
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.LabelFor(m => m.SchoolName)
#Html.TextBoxFor(m => m.SchoolName)
#Html.ValidationMessageFor(m => m.SchoolName)
#Html.LabelFor(m => m.Address)
#Html.TextBoxFor(m => m.Address)
#Html.ValidationMessageFor(m => m.Address)
#Html.LabelFor(m => m.Phone)
#Html.TextBoxFor(m => m.Phone)
#Html.ValidationMessageFor(m => m.Phone)
<button id="btnSchoolSave" name="edit" type="button">Save</button>
</form>
$("#btnSchoolSave").on('click', function () {
//get the form
var form = $("#your-form");
//validate form
if (!form.valid()) {
return;
}
//serialize the form
serializedForm = form.serialize();
//ajax post
$.ajax({
url: "#Url.Action("CompanyEdit", "CV")",
type: "POST",
data: serializedForm
.........
......
Now the serializedForm will be posted to your controller parameter as the ViewModel
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveSchool(YourViewModel modal)
{
//find school
var school = repository.FindSchool(modal.Id)
//map value from modal
school.SchoolName = modal.SchoolName;
..........
repository.SaveScool(school);
}
I am using asp.net mvc application and trying to upload an image.
This is uploading image function
$(document).ready(function () {
TableDatatablesEditable.init();
$("#UploadImg").change(function () {
var data = new FormData();
var files = $("#UploadImg").get(0).files;
if (files.length > 0) {
data.append("MyImages", files[0]);
}
$.ajax({
url: "#Url.Action("UploadFile")",
type: "POST",
processData: false,
contentType: false,
data: data,
success: function (response) {
$("#HeaderInput").text('/Upload/' + response);
$("#imgPreview").attr('src', '/Upload/' + response);
},
error: function (er) {
alert(er);
}
});
});
});
Here i am displaying the uploaded image
#Html.Label("Logo Upload", new { #class = "col-md-3 control-label" })
<div class="col-md-7">
<input type="file" class = "col-md-3 control-label" id="UploadImg" value="#Model.FundDetail.ImagePath" name="#Model.FundDetail.ImagePath" /> <br /><br />
<br />
</div>
</div>
The image uploading is working fine but values are not assigning to the model's property "#Model.FundDetail.ImagePath"
How can i assign value from javascript. Can anyone suggest me how to do this.
Thanks in advance
You cannot set the value attribute of a file input for security reasons. The only way it can be set is by the user selecting a file in the browser.
In your form, include a hidden input for the file name
#Html.HiddenFor(m => m.FundDetail.ImagePath)
and change the file input to
<input type="file" class = "col-md-3 control-label" id="UploadImg" />
Then in the ajax call where you upload the file and return the saved path, update the value of the hidden input in the success callback
$.ajax({
....
success: function (response) {
$("#HeaderInput").text('/Upload/' + response);
$("#imgPreview").attr('src', '/Upload/' + response);
// Set the value of the hidden input
$('#FundDetail_ImagePath').val(response);
},
....
});
Now when your submit the form, the ImagePath property will be populated with the saved path of the file which can then be saved to the database.
anti-forgery form field “__RequestVerificationToken” is not present
when using jQuery Ajax and the Html.AntiForgeryToken()
How to make ajax request with anti-forgery token in mvc
AJAX Posting ValidateAntiForgeryToken without Form to MVC Action Method
All the answers above did not help me. I get this error in my request with Jquery Ajax call:
"The required anti-forgery form field "__RequestVerificationToken" is
not present"
If I comment [ValidateAntiForgeryToken] attribute at POST action method it is working fine. I want to know why I am getting this error.
#using (Html.BeginForm("Save", "AddPost", FormMethod.Post, new { id = "CreateForm" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>GropPost_Table</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Body, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Body, new { id = "Bf" })
#Html.ValidationMessageFor(model => model.Body)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="btnAdd" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Save([Bind(Include = "Body")] GropPost_Table groppost_table)
{
if (ModelState.IsValid)
{
groppost_table.GroupID = 1;
groppost_table.ID = 1;
groppost_table.PostDate = System.DateTime.Now;
db.GropPost_Table.Add(groppost_table);
db.SaveChanges();
return Json(groppost_table);
}
else
{
return Json("we Couldent add your post");
}
}
<script type="text/javascript">
$("#btnAdd").click(function () {
var GropPost_Table = {
"Body": $("#Bf").val()
};
var token = $('#CreateForm input[name=__RequestVerificationToken]').val()
var headers = {};
headers['__RequestVerificationToken'] = token;
$.ajax( {
type: "POST",
url: "#Url.Action("Save","AddPost")",
data: JSON.stringify(GropPost_Table),
contentType: "application/json;charset=utf-8",
processData: true,
headers:headers,
success: function (dataR) {
$("#Bf").val('');
},
error: function (dataR) {
$("#Bf").val('');
alert(dataR.toString());
}
});
});
</script>
I've alway included the Request Verification Token in the data of the POST and not the headers. I would approach it like this:
First add type="submit" to your input button so it will submit the form when clicked. Then in your javascript:
// Listen for the submit event on the form
$('#CreateForm').on('submit', function(event) {
var $form = $(this);
$.ajax({
// Html.BeginForm puts the url in the
// "action" attribute
url: $form.attr('action'),
// Serializing the form will pick up the verification
// token as well as other input data
data: $form.serialize(),
success: function(dataR) {
$('#Bf').val('');
},
error: function(dataR) {
$('#Bf').val('');
alert(dataR.toString());
}
});
// Preventing the default action will keep the form
// from doing a full POST.
event.preventDefault();
});
I've been trying to get autocomplete to work for my partial view using JQuery ui. The partial view is updated using AJAX.
The problem is that the autocomplete only works up until the point where the partial view is updated.
This is my partial view
<div id="tagList">
#using (Ajax.BeginForm("AddToTagList", new { urlLocation = Model.UrlLocation }, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "tagList" }))
{
if (Model.TagList != null)
{
<div class="form-group">
#Html.LabelFor(model => model.Tag.Text, "Tags", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-xs-10">
<div class="input-group" style="max-width: 300px;">
#Html.EditorFor(model => model.Tag.Text, new { htmlAttributes = new { #class = "form-control", #id = "search_term" } })
#Html.ValidationMessageFor(model => model.Tag.Text, "", new { #class = "text-danger" })
<div class="input-group-btn">
<button type="submit" value="Add Tag" class="btn btn-default">+</button>
</div>
</div>
</div>
</div>
}
}
</div>
This is my JavaScript
$(document).ready(function () {
$("#search_term").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Tag/SearchAutoComplete",
type: "POST",
dataType: "json",
data: { term: request.term },
success: function (data) {
response($.map(data, function (item) {
return { label: item.Text, value: item.Text };
}));
}
});
},
});
});
and this is my autocomplete search action
public JsonResult SearchAutoComplete(string term)
{
using (IDocumentSession session = RavenDbConfig.RavenDBDocumentStore.OpenSession())
{
var results = session.Query<Tag>().Where(x => x.Text.StartsWith(term)).ToList();
return Json(results,JsonRequestBehavior.AllowGet);
}
}
So, my question is, how can i make this work even after the partial view has been updated once?
Your problem is when you reload your PartialView you basicaly delete some part of DOM in your html document and create new one. And all your bindings that you add in $(document).ready() event will be lost.
One of the posible solutions for this problem is to place your autocomplete init code in addition to .ready() event in jquery .ajaxSuccess() global event becouse you reload your PartialViews via Ajax. Like this:
$(document).ajaxSuccess(function() {
$("#search_term").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Tag/SearchAutoComplete",
type: "POST",
dataType: "json",
data: { term: request.term },
success: function (data) {
response($.map(data, function (item) {
return { label: item.Text, value: item.Text };
}));
}
});
},
});
});
That way you will init your autocomplete every time you reload PartialView.