I have an ASP.NET MVC app. This app has a view that shows a list of records. I want to let my users add a record to this list. To do that, I've created a dialog that the user can enter information into. At this time, this dialog is a partial. I'm willing to change the implementation. I just need a dialog for adding. At this time, my views look like this:
Index.cshtml
<table class="table">
<thead>
<tr>
<th></th>
<th>First Name</th>
<th>Last Name</th>
<th>Email address</th>
</tr>
</thead>
<tbody>
#foreach (var user in Model.Users)
{
<tr>
<td></td>
<td>#user.FirstName</td>
<td>#user.LastName</td>
<td>#user.Email</td>
</tr>
}
</tbody>
</table>
<br />
<button type="button" class="btn btn-default" onclick="addPerson()">Add Person</button>
#* Add Person dialog *#
<div id="addPersonModal" class="modal">
<div class="modal-dialog">
#{Html.RenderAction("Add", "Person");}
</div>
</div>
<script type="text/javascript">
function addPerson() {
$('#addPersonModal').modal('show');
}
</script>
Add.cshtml
#model MyApp.Models.AddPersonModel
#{ Layout = null; }
<div class="modal-content">
#using(Html.BeginForm("Add", "Person", FormMethod.Post))
{
<div class="modal-header">
<h4 class="modal-title">Add New Person</h4>
</div>
<div class="modal-body">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-md-6">
#Html.TextBoxFor(m => m.FirstName)
</div>
<div class="col-md-6">
#Html.TextBoxFor(m => m.LastName)
</div>
</div>
<div class="row">
<div class="col-md-12">
#Html.TextBoxFor(m => m.Email)
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create</button>
</div>
#Html.AntiForgeryToken()
}
</div>
PersonController.cs
namespace MyApp.Controllers
{
public class PersonController : Controller
{
public ActionResult Index()
{
var model = new IndexModel();
return View(model);
}
public ActionResult Add()
{
var model = new AddPersonModel();
return View(model);
}
[HttpPost]
public ActionResult Add(AddPersonModel model)
{
if (ModelState.IsValid)
{
model.Save();
}
else
{
// I'm stuck here.
}
var listModel = new IndexModel();
return View("~/Views/Person/Index.cshtml", listModel );
}
}
}
Everything works except for validation. If the user enters some invalid data, the view reloads the entire screen and the data is lost. The dialog is also closed. I need to be able to show the values the user entered and the validation summary in the dialog window if the data is invalid. Yet, I'm not sure how to do this.
Related
I don't know why my "submit" button not responding. When I click on the edit button in each id, it can show a modal like this picture, so data is sent to the modal. But when I try to edit this data and click the button, it is not responding.
Here is my Index.cshtml
<div class="card-body">
<table id="datatablesSimple">
<thead>
<tr style="background-color:gray">
<th>No</th>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
<tfoot>
<tr>
<th>No</th>
<th>Name</th>
<th>Action</th>
</tr>
</tfoot>
<tbody>
#{ var stt = 1;}
#foreach (var item in ViewBag.List)
{
var itemName = "#exampleModalss" + item.id_category;
var itemName1 = "exampleModalss" + item.id_category;
<tr>
<td>#stt</td>
<td>#item.name</td>
<td>
<button type="button" class="btn btn-danger" data-bs-toggle="modal"
data-bs-target="#itemName">Edit</button>
</td>
</tr>
stt++;
<form action="/Category/edit" method="post">
<div class="modal fade" id="#itemName1" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Category Book</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div id="resultss" class="modal-body">
<div class="row">
<div class="col">
<div class="">
<label for="category-film" class="col-form-label" style="font-weight:bold;width : 140px">Name Category: </label>
<input class="form-control" type="text" id="name" name="name" value=#item.name>
<input class="form-control" type="hidden" id="id_category" name="id_category" value=#item.id_category>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button style="width:100px" type="submit" class="btn btn-primary">Edit</button>
</div>
</div>
</div>
</div>
</form>
}
</tbody>
</table>
</div>
My Controller
[HttpPost]
public ActionResult edit(FormCollection form)
{
Category cat = new Category();
cat.id_category = Convert.ToInt32(form["id_category"]);
category.edit(cat);
return PartialView("Index", new { msg = "1" });
}
My DAO
public void edit(Category category)
{
var result = myDb.categories.SingleOrDefault(c => c.id_category == category.id_category);
result.name = category.name;
myDb.SaveChanges();
}
Here is my result when clicking on the Edit button with each id of the category book.
I get stuck in it few days, anyone can help me. I think
Edited for details
FormCollection is for list your form is just about a name and category id
then your model is not appropriate please change your model i mean create model class which contain name and category.
I have an Index page like below and on button click I have some code to save data into database and then it goes on details page.
But I the code should go to the details page only after completing the database save operation; I want to show a loader image until then; how can I do this?
Currently I'm using begin post to post method and bind all model not using any ajax call. How can I show loader image and render before process complete to details page?
Index.cshtml
#model Dev.Models.DeviceReg
#using (Html.BeginForm("AddAsync", "Home", FormMethod.Post))
{
<div class="panel panel-primary">
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<h4 id="aa">Name</h4>
<label>Select</label>
<table>
<tr>
<td>
#Html.DropDownListFor(m => m.Name, (SelectList)ViewBag.Name, "---Select Name---")
</td>
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h4 id="aa">Model</h4>
<label>Select</label>
<table>
<tr>
<td>
#Html.DropDownListFor(m => m.Type, (SelectList)ViewBag.TypeName, "---Select Type---")
</td>
</tr>
</table>
</div>
</div>
<div class="panel-footer" align="left">
<button type="submit" id="save" class="btn btn-success">
<span class="glyphicon glyphicon-arrow-right"></span> save
</button>
</div>
</div>
}
HomeController.cs
public async Task<ActionResult> AddAsync(DeviceReg deviceRegistration)
{
foreach (var deviceId in collection)
{
// save device information into database
Models.Device newDevice = new Models.Device()
{
Id = Guid.NewGuid(),
DeviceTypeId = deviceRegistration.DeviceType,
PlatformId = deviceRegistration.PlatformType,
DeviceId = deviceId,
};
_repository.InsertDevice(newDevice);
_repository.Save();
}
return View("Details", deviceRegistration);
}
Details.cshml
#model Dev.Models.DeviceReg
<body style="background-color:black">
<div class="panel panel-primary">
<div class="panel-heading" align="center">
<h2 class="panel-title">Details</h2>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<h3><label>Current Data</label></h3>
<br />
</div>
</div>
<div class="row">
<div class="col-md-6">
<h4 id="aa">Name</h4>
<label>#Model.Name</label>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h4 id="aa">Type</h4>
<label>#Model.TypeName</label>
</div>
</div>
<hr />
<br />
<label>Your process is running.</label>
<br />
<div class="row">
<div class="col-md-6">
<h3><label>Status</label></h3>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div style="clear: both">
<h2 style="float: left">10</h2>
<h6 style="float: right">Active Number</h6>
</div>
</div>
</div>
</div>
</div>
</body>
Well, if you need to show loader while post form is submitting, you can use javascript functions to show it, like
#using (Html.BeginForm("AddAsync", "Home", FormMethod.Post, new { onsubmit = "showLoader(this);" }))
{
...
}
plus JS
<script>
var showLoader = function(form){
$("<div />").css({
'position' : 'fixed',
'left' : 0,
'right' : 0,
'bottom' : 0,
'top' : 0,
'background' : '#0020ff36',
'z-index' : '99',
'text-align' : 'center'
}).appendTo($("body"))
.append(
$("<img />").attr("src", "https://mir-s3-cdn-cf.behance.net/project_modules/disp/35771931234507.564a1d2403b3a.gif")
);
}
</script>
or by jquery event, like
<script>
$("form").submit(function(){
//show loader
});
</script>
example of this code https://dotnetfiddle.net/gfEVSE
But, regarding to your clarification of the issue in comments, it's impossible to show Details page with progress of saving without Javascript or another additional requests.
Example without ajax but with aditional requests every N seconds
[HttpPost]
public ActionResult AddAsync(SampleViewModel deviceRegistration)
{
Task.Run(()=>
{
//Saving to DB
});
return RedirectToAction("Details", id = deviceRegistration.id);
}
public ActionResult Details(int id)
{
var isObjectExistInDb = checkIfExistInDb(id);
if (!isObjectExistInDb){
return View("ShowLoader", id);
}
return View(object);
}
where in ShowLoader.cshtml you need to reload page every N seconds.
With ajax it will be more clear, pretty code. Please, let me know, if you need example with ajax :)
On my Main View I have 4 partial views.. two are tables.. the others are create forms.
Partial View Table 1
#model IEnumerable<ProjectName.Models.code_AutoMake>
<h3>Auto Make List</h3>
<table id="Auto-Make-Table" class="table table-bordered table-striped">
<thead>
<tr>
<th class="col-md-5">
#Html.DisplayNameFor(model => model.AutoMake)
</th>
<th class="col-md-5">
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.AutoMake)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
#if (!item.Active)
{
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.MakeID }) |
Activate
</td>
}
else
{
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.MakeID }) |
Deactivate
</td>
}
</tr>
}
</tbody>
</table>
Partial View Table 2
#model IEnumerable<ProjectName.Models.code_Funding>
<h3>Funding List</h3>
<table class="table table table-bordered table-striped">
<tr>
<th>
#Html.DisplayNameFor(model => model.Funding)
</th>
<th>
#Html.DisplayNameFor(model => model.Active)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Funding)
</td>
<td>
#Html.DisplayFor(modelItem => item.Active)
</td>
<td>
#Html.ActionLink("Edit", "Edit", "code_Funding",new { id=item.FundID }, null) |
</td>
</tr>
}
</table>
Partial View 1 Create
#model ProjectName.Models.code_AutoMake
#using (Html.BeginForm("Create", "code_AutoMake", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h3>Add Auto Make</h3>
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-sm-12 col-md-3">
#Html.Label("Auto Make")
#Html.EditorFor(model => model.AutoMake, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-sm-12 col-md-3">
#Html.Label("Active")
<div class="checkbox">
#Html.EditorFor(model => model.Active)
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 col-md-3">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Partial View 2 Create
#model ProjectName.Models.code_Funding
#using (Html.BeginForm("Create", "code_Funding", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<h3>Add Funding</h3>
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-sm-12 col-md-3">
#Html.Label("Funding")
#Html.EditorFor(model => model.Funding, new {htmlAttributes = new {#class = "form-control"}})
</div>
<div class="col-sm-12 col-md-3">
#Html.Label("Active")
<div class="checkbox">
#Html.EditorFor(model => model.Active)
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 col-md-3">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
Main View
<div id="AutoMake" class="tab-pane fade active in">
<div id="AutoMake-Index">#{Html.RenderAction("Index", "code_AutoMake");}</div>
<hr/>
#{Html.RenderAction("Create", "code_AutoMake");}
</div>
#*Funding*#
<div id="Funding" class="tab-pane fade">
#{Html.RenderAction("Index", "code_Funding");}
<hr/>
#{Html.RenderAction("Create", "code_Funding");}
</div>
Now here is the scenario.. When I want to create a new autoMake.. I fill out the form and hit submit.. this goes through fine.. until I get back to the Main View.. specifically this line:
#{Html.RenderAction("Create", "code_Funding");}
and I get a runtime error saying:
Child Actions are not allowed to perform redirect actions
I have debugged.. and for some reason.. the HttpPost Create action for code_Funding is being hit.. even when I'm not filling out the create form for code_funding.. How is that possible?
Here are my Create Methods for code_autoMake and code_funding:
code_Funding
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "FundID,Funding,Active")] code_Funding code_Funding)
{
try
{
if (ModelState.IsValid)
{
db.code_Funding.Add(code_Funding);
db.SaveChanges();
return RedirectToAction("EditDDL", "tblNewAutos");
}
}
catch (DbEntityValidationException ex)
{
foreach (var entityValidationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in entityValidationErrors.ValidationErrors)
{
Response.Write("Property: " + validationError.PropertyName + " Error: " + validationError.ErrorMessage);
}
}
}
return RedirectToAction("EditDDL", "tblNewAutos");
}
code_autoMake
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MakeID,AutoMake,Active")] code_AutoMake code_AutoMake)
{
if (ModelState.IsValid)
{
db.code_AutoMake.Add(code_AutoMake);
db.SaveChanges();
return PartialView("~/Views/PartialViews/_AutoMakeCreate.cshtml");
}
return RedirectToAction("EditDDL", "tblNewAutos");
}
Why when I try and create a new automake.. both HttpPost Create methods are hit?
Any help is appreciated.
Well, the problem is following. In your main view you have got this code:
...
#{Html.RenderAction("Create", "code_AutoMake");}
...
Which triggers the Create action which finishes with the following line of code if ModelState.IsValid == false:
return RedirectToAction("EditDDL", "tblNewAutos");
That is obviously a bad idea. Why? You are already in a process of rendering a parent view. Child actions might be a bit confusing at first because they are not real actions - no client/server communication. You are still on the server side. Therefore no redirect is allowed in the child action.
Solutions
First of all, I am not quite sure what you want to achieve so my solution recommendation might be a bit off, but let's see.
Option 1
You may want to use two different actions. One that is called on submit of the form and another one that is called from your main view. The latter one should not make a redirect - instead it should wisely choose which view to render based on the ModelState.IsValid if this is really what you need.
Option 2
There is a hack way which allows you to make redirect from a child action. Instead of making a redirect, only store information about required redirect for instance in HttpContext.Items collection. Then, implement an ActionFilter and in its OnResultExecuted event, check if the redirect request was set to the HttpContext.Items. If so, make a redirect. The ActionFilter should be applied on the parent action, not on the child action.
#{Html.RenderAction("Create", "code_Funding");}
in this RenderAction Method is call the GET request but, your controller you written only post method. and you write the
[ChildActionOnly]
public ActionResult Create(string parm)
{
reurn view()
}
and use [ChildActionOnly] this attribute is allowing restricted access via code in View.
Check and reply me..
I have a page which list all Countries in a tabular form and view has a model like below:
public class CitySet
{
public IEnumerable<CityObject> Cities { get; set; }
[Required]
public string City { get; set; }
[Required]
public string CityCode { get; set; }
}
On the same page I have a link that allows the user to create a new Country via a modal popup(defined within that page)
Modal Popup section of the page is shown below:
#model CMS.Models.CitySet
<div class="box-header well" data-original-title="">
<h2>
<a operation="add" id="btnNewCity" data-toggle="modal" data-target="#myModal" href="#">Add New City</a>
</h2>
</div>
<div class="box-content">
#*Below table is to show all the added Cities in the tabular format on the page itself*#
<table id="myDataTable" class="table table-striped table-bordered ">
<thead>
<tr>
<th>City</th>
<th>Country </th>
<th>Creation Date</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Cities)
{
<tr id="#item.CityID">
<td>#item.City</td>
<td>#item.CountryName</td>
<td>#item.CreatedDate</td>
<td>
Edit
Delete
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
#using (Html.BeginForm("ManageCity", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Add City</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="City"> City </label>
#Html.TextBoxFor(model => model.City)
#Html.ValidationMessageFor(model=>model.City)
</div>
<div class="form-group">
<label for="CityCode"> City Code </label>
#Html.TextBoxFor(model => model.CityCode)
#Html.ValidationMessageFor(model=>model.CityCode)
</div>
<button type="submit" id="addNewCity" class="btn btn-primary">Save changes</button>
}
</div>
</div>
</div>
The javascript that triggers the modal popup is :
$(".model_edit").click(open_model_form);
function open_model_form(e) {
e.preventDefault();
if ($(e.currentTarget).attr("operation") == "add") {
//open popup
$('#myModal').modal('show');
}
}
[HttpPost, ValidateInput(true)]
public ActionResult ManageCity(FormCollection formData)
{
//Some data saving Operation
}
The behavior that I am seeing is when both the textboxes are empty and user presses Submit Button it shows Validation message against both, but if I enter value in any ONE of them, and press SUBMIT, it start posting the form, i.e. to Action "ManageCity" and does not check for Client side validation on the other textbox.
I have already included both Validate.js and UnObstrusive.js files on my layout page of this form page
I have a (simple) ASP MVC view:
<div class="row">
<div id="dashboard-left" class="col-md-8">
#using (Html.BeginForm("ConfigureOffers", "Offers", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div style="padding-bottom: 10px;">
<p style="font-size: large;"><strong>Available</strong></p>
</div>
<div class="accordion" id="accordion2">
<div class="widget" style="background:#fff !important">
#{
int i = 0;
}
#foreach (var prod in Model.allProducts)
{
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="accordion2" href="#collapse#(i)">
<h4 class="widgettitle" id="QuickTitle">> #prod.Description_VC</h4>
</a>
</div>
<div id="collapse#(i)" class="accordion-body collapse" style="height: 0px;">
<div class="accordion-inner" style="margin-left: 10px;">
<div style="padding-bottom: 10px;"><strong>Total product:</strong> #{
for (int k = 0; k < Model.OfferHeaders.Count(); k++)
{
if (Model.OfferHeaders[k].Product_ID == prod.Product_ID)
{
#Html.TextBoxFor(o => o.OfferHeaders[k].Amount_DEC);
break;
}
}
}
</div> #*prod div*#
</div> #*accordion-inner*#
#{i++;}
</div>
<div style="clear:both;"></div>
</div>
}
<input type="submit" class="btn btn-default" value="Save Changes" />
</div> #*widget*#
</div> #*accordion*#
}
</div>
<!-- col-md-4 -->
</div>
<!--row-->
The basis of this view was taken from another (working page), but for some reason, clicking the submit button doesn't trigger the server action detailed in the BeginForm element. I get no errors from Visual Studio, nor any JavaScript errors from the browser console, and nothing seems to happen server-side.
One possible reason for Action not happening is mainly when you put
the action in the View and also created the page. but i think you
have forgot mentioning the ActionResult in the controller.
Another possible option is you have to use [HttpPost] above your
method in controller where you specified the ActionResult
[HttpPost]
public ActionResult ConfigureOffers(ModelClass instance)
{
...
}
Other possible reasons
The routes are not correct
The Form Action property is not correct
You are using nested FORM tags