Multiple forms rendered in one page - asp.net

In my Index view, I render(action) 3 other views, Create, Edit and List. The Create and Edit views are both forms to manipulate data in a database. When I push edit button in the List View the Create form should disappears and the Edit form should appear. So Create and Edit shouldn't be shown at the same time.
When I don't render the views in one view, everything works fine. But because I don't want to switch pages, I want to render the views in one page. Here is the problem. My Create form and List view works fine when I render them in one page. But when I also want to render my Edit form, I get errors.
One thing that happens is that the Create form puts de data in the database 2 times. Also when I don't put validated data in my form, the edit form appears at the same time that the create form is shown. The third problem is when I put the edit button in my List. The create form disappears and the edit form apears. So this works, but when I want to save the changed data, I get a hex string error. When I restart the application, the data isn't changed, but there is new record with the changed data.
I guess there is a problem with the id string. But I can't fix it.
So I have 4 views (index, create, list and edit)
Create, list and edit all have their own model.
And I use one controller for the views / models
The controller (CarsController):
public class CarsController : BaseController
{
public ActionResult Index()
{
return View();
}
public ActionResult List()
{
List<Car> carsInDb = CarRentalContext.Cars.FindAll().ToList();
return View(carsInDb.ConvertAllToViewModels());
}
[HttpGet]
public ActionResult Create(string id)
{
if (String.IsNullOrEmpty(id))
{
return View();
}
return null;
}
[HttpPost]
public ActionResult Create(InsertCarViewModel insertCarViewModel)
{
if (ModelState.IsValid)
{
Car car = insertCarViewModel.ConvertToDomain();
CarRentalContext.Cars.Insert(car);
Response.Redirect("Cars", true);
}
return View(insertCarViewModel);
}
[HttpGet]
public ActionResult Edit(string id)
{
if (String.IsNullOrEmpty(id))
{
return null;
}
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
return View(car.ConvertToUpdateViewModel());
}
[HttpPost]
public ActionResult Edit(UpdateCarViewModel updateCarViewModel)
{
if (ModelState.IsValid)
{
Car modifiedCar = updateCarViewModel.ConvertToDomain();
CarRentalContext.Cars.Save(modifiedCar);
return RedirectToAction("Index");
}
return View(updateCarViewModel);
}
}
The index view:
#model MvcApplication1.ViewModels.UpdateCarViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#{Html.RenderAction("Create", Model);}
</div>
<div>
#{Html.RenderAction("Edit", Model);}
</div>
<div>
#{Html.RenderAction("List", Model);}
</div>
</body>
</html>

Instead of show/hide the views for create and edit views try to replace one with another, using ajax to get the result of the action. This way you will have only one view in the page.
Do something like this:
<script type="text/javascript">
function renderActionInDiv(idcar, action)
{
$.ajax({
type: "POST",
url: action, //or "/Cars/Edit"
data: { id = idcar},
success: function (ajxData) {
if (typeof callback !== 'undefined') {
$("#div-editOrCreate").html(ajaxData);
}
},
async: asyncCall
});
}
</script>
<a onclick='renderActionInDiv(3, "Edit")'>edit car<a/>
<a onclick='renderActionInDiv(0, "Create")'>create new car<a/>
<div id="div-editOrCreate">
#{Html.RenderAction("Create", Model);}
</div>

Related

ASP.NET MVC Redirect To Action does not render final View

I'm trying this code:-
If no query string supplied to the Index Method then render a Branch Locator View. When a Branch Id is selected in that View, post back to a Redirect To Route Result OR Action Result method and then redirect back to Index with a query string of the selected Branch Id.
I can run through the code successfully without and then with the query string.
I even run through the Index View and can see the Model working however, the Index View does not render, the Branch Selector View remains. Network developer tools shows the correct URL with query string correctly in place when doing the Redirect.
(NOTE: Both methods are on the same controller).
If I add the same query string directly in the Browser address bar it works fine!
I have this code:
[HttpGet]
public ActionResult Index()
{
var querystringbranchId = Request.QueryString["branchId"];
if(!string.IsNullOrEmpty(querystringId))
{
....do stuff like build a model using the branchId...
return View(Model);
}
return View("BranchSelector")
}
[HttpPost]
public RedirectToRouteResult BranchDetails(FormCollection formCollection)
{
var querystringBranchId = formCollection["BranchList"];
var branchId = int.Parse(querystringBranchId);
return RedirectToAction("Index", new { branchId });
}
Try using strongly typed model on the post, and specifying the param as an actual param - Using View models is going to be much better for you.
I have tested the below - It seemed to work as expected for me:
[HttpGet]
public ActionResult Index(int? branchId)
{
if (branchId.HasValue)
{
return View(branchId);
}
return View("BranchSelector");
}
[HttpPost]
public RedirectToRouteResult BranchDetails(MyModel myModel)
{
return RedirectToAction("Index", new { myModel.BranchId });
}
public class MyModel
{
public int BranchId { get; set; }
}
The View:
<div>
#using (Html.BeginForm("BranchDetails", "Home", FormMethod.Post))
{
#Html.TextBox("BranchId","123")
<input type="submit" value="Go"/>
}
</div>
#MichaelLake Thanks to your post I found the problem. I tried your code and sure enough it works as expected. I didn't mention I was using a Kendo Combobox control (!) loaded with the branches. I didn't mention that as the actual data I needed was available in the post method so, thought the issue was with the Controller methods. I had the Kendo control name as BranchList, I changed it to BranchId and it now works with the original code as expected! The Kendo name becomes the element Id and has to match to work.
Many Thanks!
This will work for you. Cheers :D
return RedirectToAction("Index", "ControllerName", new { branchId = branchId});

Partial refresh in ASP using ajax

How do you do a partial refresh of a page using Ajax in cshtml?
As I understand it, Ajax is required. In my scenario, in my index page I have a table where each row has a program (a batch file) and a Run button. Beneath the table I have the a space for the program output. I would like this to populate (I'm happy to wait for the program to finish just now) without having to refresh the rest of the page.
Code is below, but in summary I have one model for the table data, one model for the selected program log/output. The controller for the index page creates both and passes them in to a view model, which is passed to the view. When the Run button is hit an Index overload method in the controller handles the running of the program and 'getting' the output. It also populates the appropriate model in the VM (possibly not ideal and I'm open to suggestions to improve it).
The overloaded method currently returns a PartialViewResult and the output/logging has it's own PartialView (as I'll want to reuse it elsewhere). This is also why it has a separate model. In the PartialView break points are hit, but it doesn't appear on the page in the browser.
I'm using ASP.NET-MVC-4 with Razor.
View (Index.cshtml)
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
#model ViewModels.UpdateTestViewModel
#{ ViewBag.Title = "Update Test"; }
#{
<table>
#* Headers *#
<tr>
<td>Programs</td>
</tr>
#* Data *#
<tr>
<td>#Model.ProgramName</td>
<td style="min-width:75px"><input id="btnRun" type="button" value="Run" /></td>
</tr>
</table>
<div id="log">
#Html.Partial("ScriptLog", Model.Log)
</div>
<script>
$("input[type=button]").on("click", function () {
var NAME = ($(this).parent().siblings(":first")).text();
$.post("/UpdateTest/Run", { input: NAME });
});
</script>
}
Partial View
#model Models.ScriptLog
#if (Model != null && Model.Log.Any(x => !string.IsNullOrWhiteSpace(x)))
{
<fieldset>
<legend>Log</legend>
#foreach (string entry in Model.Log)
{
<p>#entry</p>
}
</fieldset>
}
Script Log
public IEnumerable<string> Log { get { // returns log } }
ViewModel
public class UpdateTestViewModel
{
public string ProgramName { get { return "My Program"; } }
public ScriptLog Log { get { return _log; } }
private readonly ScriptLog _log;
public UpdateTestViewModel(ScriptLog log)
{
_log = log;
}
}
Controller
public ActionResult Index()
{
if (SessionFacade.CurrentUpdateTestLog == null)
{
ScriptLog log = new ScriptLog();
SessionFacade.CurrentUpdateTestLog = log; // Store in Session
}
UpdateTestViewModel vm = new UpdateTestViewModel(SessionFacade.CurrentUpdateTestLog);
return View(vm);
}
[ActionName("Run")]
public PartialViewResult Index(string input)
{
ExecuteScript.ExecuteUpdateTestScript(input); // Run batch file
UpdateTestLog(input); // Get log and update in Session
return PartialView("ScriptLog", SessionFacade.CurrentUpdateTestLog);
}
Since you are making an $.post() you need to decorate your /UpdateTest/Run action with [HttpPost].
You also don't define a success handler so, while you are making the request, you never do anything with it.
$.post("/UpdateTest/Run", { input: NAME })
.done(function(partialResult) {
$("#log").html(partialResult);
})
.fail(function(jqxhr, status, error) {
console.log(jqXhr, status, error);
});
With much help and patience from #Jasen I got a working solution which was to extend the existing Ajax, so that it looks like:
$("input[type=button]").on("click", function () {
var NAME = ($(this).parent().siblings(":first")).text();
$.post("/UpdateTest/Run", { input: NAME })
.done(function (partialResult) {
$("#log").html(partialResult);
})
});
Note I also have added the [HttpPost] attribute in the controller

asp.net mvc3 disable masterpage for view

How can I disable the usual _Layout.cshtml for one view.
I try to use the content of the view aboutuser.cshtml to populate a div using ajax. Do you think this is a correct approch?
$('#SelectUser').click(function () {
$('#userPlace').html('<strong>Loading...</strong>');
$.post('InfoFor?userSelect=' + ($('#usersoption:selected').val()), function (data) {
$('#userPlace').html(data);
});
});
Inside this view/partial:
#{
Layout = null;
}
or from the controller action serving it return a PartialView instead of a View:
public ActionResult Foo()
{
return PartialView();
}

A Contact page with ASP.NET MVC 3

I have a contact page and this page shall either show a form or a success message or a failure message, so basically something like this:
#model MyApp.Models.ContactData
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>
...Some static content...
If page was opened the first time
-> Render a form here
Else If form was posted and data successfully processed
-> Render a success message here
Else If form was posted but error occurred during processing
-> Render a failure message here
...Some static content...
</div>
I don't know what's the best way to achieve this with MVC 3. Do I create three completely separate views (which is something I'd like to avoid because of the static content which would be the same for all three views)? Or could I create three partial views and then decide based on an additional flag I could put into the model class which partial view to render? Or can I inject somehow the partial views dynamically from the controller into the view?
The controller I have so far looks like this:
public class ContactController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
return ?; // What do I return now? It must somehow depend on result.
}
else
return View(contactData));
}
}
I had a similar page and behaviour with ASP.NET WebForms and the solution was there to put the three variable blocks of markup into asp:Panel controls and then switch on or off the Visible flag of those panels from code-behind. I guess I need quite another approach with ASP.NET MVC to reach the same goal.
What is the best way?
Thank you for suggestions in advance!
You can try this way:
[HttpPost]
public ActionResult Index(Contact contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
if (service.Process(contactData))
{
TempData["Success"] = "Your success message.";
return RedirectToAction("Index");
}
else
{
TempData["Error"] = "Your fail message.";
}
}
return View(contact);
}
Perhaps use the ViewBag to help achieve all this. Of course it's a dynamic, so you can add & check for any prop you want/need/expect.
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
ViewBag.ContactSuccess = true;
}
else
{
ViewBag.ModelStateErr= "some err";
}
return View(contactData));
}
Then in your View:
if (ViewBag.ContactSuccess !=null && ((bool)ViewBag.ContactSuccess))
{
//thanks for posting!
}
else
{
if (ViewBag.ModelStateErr !=null)
{
//show that we have an err
}
else
{
//we have no err nor a 'true' contact success yet
//write out the form
}
}
Looks like that you can issue an ajax call on the client side, and based on the Json result, you can render different content from the client side.
I'd suggest coding up three different Views
index.cshtml
contactSuccess.cshtml
contactFail.cshtml
Then in your Controller, you'll have similar code as before
public class ContactController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
return View("contactSuccess.cshtml");
}
else
return View("contactFail.cshtml", contactData);
}
}
This way each view has an independent and you don't have a big inline IF block in the middle of your markup.
Alternatively (and this is how I'd do it) you can have the index.cshtml contain three partials...
_ContactForm.cshtml
_ContactSuccess.cshtml
_ContactFail.cshtml
and then you can load the partial views into the index view, and even swap them out dynamically using AJAX.

how to send a message to a view ajax-like

I want to know about a pattern to do this. I have a controller with method1()
method1(){
return View();
}
[httpost]
method1(string something){
return View(object);
}
[httpost]
method2(int? id){
return redirectToaction("method1");
}
View:
<div>
beginform(){
textfield
submit button
}
</div>
<div>
if(viewbag.something != null){
beginform("method2", "controller", new{id=number}){
<span>#model.info</span>
submit button
}
}
</div>
and this displays a view this view has a form a text field and submit and this calls method1() but with the HTTPPOst and in the same view i display another form which will call method2() my question is how can i display a message in the view? like "user has been deleted" without having to create another view. Is there another way of doing this in asp mvc or do i have to include ajax?
I tried setting a viewBag inside method2, but since method2 redirectsaction to method1 it somehow does not stay, and it is not displayed in the view.
Thanks!
You could use TempData which is preserved between a single redirect:
public ActionResult method1()
{
return View();
}
[HttpPost]
public ActionResult method1(string something)
{
return View(something);
}
[HttpPost]
publicActionResult method2(int? id)
{
TempData["message"] = "User has been deleted";
return RedirectToAction("method1");
}
and in the view display the message:
<div>#TempData["message"]</div>
If there is no message in TempData it will simply display an empty div. You could further check if TempData["message"] != null in the view if you didn't want the empty div.

Resources