two models in one action - c#

I get the feeling I am violating MVC best practise here, but I don't see how.
I have a form with two buttons with different names so when it gets posted to the action, I can do different things based on which button is pressed.
#using (Html.BeginForm("Display", "PlayBoard")
// form properties
<button class="btn btn-primary" type="submit" name="Display">Filter PlayBoard</button>
<button class="btn btn-primary" type="submit" name="Report">Report View</button>
then in the controller:
[HttpPost, FlexAuthorize]
public ActionResult Display(FormInput filter )
{
if (Request["Report"] != null)
{
var model = queryRepository.Load<FormInput , ReportViewPlayBoardView>(filter);
return View(model);
}
if (Request["Display"] != null)
{
var model = queryRepository.Load<FormInput , DisplayPlayBoardView>(filter);
return View(model);
}
When I click the report button, it runs through the query ok but then comes back with the exception: "The model item passed into the dictionary is of type '...ReportViewPlayBoardView', but this dictionary requires a model item of type '...DisplayPlayBoardView'."
I don't really follow why this has to be a DisplayBoardView model that gets returned.
edit: fixed it, I just had to change the view command on the report branch to: return View("ReportView", model);

fixed it, I just had to change the view command on the report branch to:
return View("ReportView", model);

Related

Incorrect Model.ID being passed to Delete Action from View in MVC

I am using a single MVC action controller called "Edit" to allow me to Edit, Create or Copy records. However, the View model for this action doesn't submit Model IDs consistently when I try to delete records. Here is the button that calls the delete action.
<input class="btn btn-info btn-danger" asp-controller="Bags" asp-action="Delete" asp-route-id="#Model.Id" onclick="alert(#Model.Id);" type="submit" value="Yes" />
That line of code lets me delete an Edited or Created record, but when I try to delete a newly Copied record, the ID that's passed to the Delete action is the ID of the original record, not the copied record.
Here is the code for the Edit Action that creates/edits/copies records and calls the View.
// GET: Bags/Edit/5
[HttpGet]
public async Task<IActionResult> Edit(int? id, bool? copy)
{
Bagsmvc bag = new Bagsmvc();
ViewBag.Copy = false;
// When id is not null, we are editing or copying. Otherwise, we're creating
// EDIT a bag
if(id != null)
{
bag = await _db.Bags.FirstOrDefaultAsync(x => x.Id == id);
} else
// CREATE a new blank bag
{
await _db.Bags.AddAsync(bag);
await _db.SaveChangesAsync(); ;
}
// COPY a bag
// This is a real hack. Once the button is hit, the bag is copied and saved to the db. Setting Id = 0 somehow
// indicates to the Entity Framework to add the record in with a newly generated ID.
// Also, since we don't want an exact copy, bring user to edit screen to make the changes.
if (copy != null)
{
ViewBag.Copy = true;
bag.Id = 0;
await _db.Bags.AddAsync(bag);
await _db.SaveChangesAsync(); ;
}
return View(bag);
}
Now here are some screen shots where you can see that the bag number is correct in the view. But when I submit "Yes" the debugger confirms that the old ID number is passed to the delete action.
I click on "Copy Bag" for old record #3079. The code creates new item #3088, which I am going to delete.
I confirm deletion and somehow the Delete action thinks I'm deleting the original record (id = 3079 in debugger) not the copy 3088 (which is what #Model.Id should be bound to)
Any idea what's going on?
OK, I figured out how to fix it, but I still don't know why it was a problem. The issue was created by code I didn't post. Earlier in the View model code, the form with the Delete button has these lines:
<h1>ID: #Model.Id</h1>
<input type="hidden" asp-for="#Model.Id" />
For some reason, the h1 #Model.Id is set to 3088, but the hidden button somehow remains bound to 3079. Why would it do this? What am I missing?
As a workaround, I eliminated the hidden field for my Copy operation so that the asp-route-id is used instead
<h1>ID: #Model.Id</h1>
#if (ViewBag.Copy == false)
{
<input type="hidden" asp-for="#Model.Id" />
}
...
<input class="btn btn-info btn-danger" asp-controller="Bags" asp-action="Delete" asp-route-id="#Model.Id" type="submit" value="Yes" />
If anyone has insight to this, I'd love to know why the Model is binding incorrectly in the hidden field. Is there some weird persistence that's happening that I don't know about?

Next button returns current record

I am trying to implement a Next button in my view to go to the next record.
When I click the button, it just keeps calling the Details action in my controller for the current item rather than the next.
So if I am looking at Sample 1 and click Next, I can see that sampleNumber is still 1 when calling Details in SampleController.
Here is the code for the button in Details.cs.html:
#{
if (Model.Pallet.Samples.Count > Model.SampleNo)
{
using (Html.BeginForm("Details", "Sample"))
{
#Html.Hidden("sampleNumber", Model.SampleNo + 1)
<input type="submit" value="Next" class="pull-right" />
}
}
}
And the Details method has the signature:
public ActionResult Details(int sampleNumber)
Any advice is much appreciated
You need to declare a new variable with
#{
var next_record = Model.SampleNo + 1;
}
and change
#Html.Hidden("sampleNumber", Model.SampleNo + 1)
to
#Html.Hidden("sampleNumber", #next_record)
By all appearances you're posting to the action Details however I'm not sure how you're passing the value to it.
So I see 2 parts to the solution.
1) You should just pass the view model to the controller action.
This has the advantage of being able to get the values, and increment the counter at the same time, removing that logic from the view.
Below, ViewModel will be the same type that is used for the #model part of the view.
public ActionResult Details(ViewModel vm)
{
// Other logic to get details
vm.SampleNo++;
return View(vm);
}
2) Your hidden field for sampleNumber is not bound to the View Model, so when you do your post, the value that is passed is Model.SampleNo not Model.SampleNo + 1 as I suspect you expect it to be.
You should change the #Html.Hidden... to #Html.HiddenFor(Model.SampleNumber) for the correct binding.

ASP.NET Model Binding can I give submit button a value and retrieve that value when its clicked?

Is it possible to use Model binding to get value of a button in the POST action method when its clicked on. I have a complex type and I wanted to have the user click on a button and retrieve the value of that button so I can use it to update the value of the complex type in the DB.
Note that at this point I have already saved the entity into the database and all that is left is to get a way to update properties of the complex type.
If there is a recommended way to do that am willing to adopt that.
Thanks in advance.
You can use multiple submit buttons with different values to specify the way of update model.
#using (Html.BeginForm("MultipleCommand", "Home", FormMethod.Post, new { id = "submitForm" }))
{
.
.
.
<button type="submit" id="btnSave" name="Command" value="create">Save</button>
<button type="submit" id="btnSubmit" name="Command" value="update">Submit</button>
}
public ActionResult(ComplexModel model, string Command)
{
if(Command == "create")
{
}
else if(Command == "update")
{
}
else
{
// Default action
}
}
For more info read Handling multiple submit buttons on the same form - MVC Razor.
Do something like this
public ActionResult Index(string submit)
////Your action while clicking the button and in the view button name should be submit
{
//// The string submit will have the value of the button
}

Value passed NULL from a View to another in ASP MVC 3

I am developing an ASP .Net MVC 3 application using C# and SQL Server 2005.
I am using also Entity Framework and Code First Method.
In a view Index, I have a DropDownList Gamme. I define its item selected in my view, like this :
public string SelectedProfile_Ga { get; set; }
In this view, I have a button Appliquerthat took me to another view Application.
<input type="button" value="Appliquer" id="appliquer" onclick="window.location = 'ProfileGa/Application'"/>
In the view Application, I have a button submit Appliquer.
<input type="submit" value="Appliquer" id="appl" />
When I click on Appliquer, I want save the value selected in my DropDownList Gamme in my base.
The problem is that this value is passed NULL when i change the view (exit page Index and open Application).
I find that with Debugging.
The Controller action :
[HttpPost]
public ActionResult app(FlowViewModel model)
{
Famille fam = new Famille();
fam.ID_Gamme = model.SelectedProfile_Ga;
db.Familles.Add(fam);
db.SaveChanges();
return RedirectToAction("Application");
}
Note :
I didn't forget this in the Application:
<% using (Html.BeginForm("app", "ProfileGa")) { %>
ProfileGa is the name of my controller.
For starters, your dropdown is in the Index view, and the selection is happening there. Then you're redirecting to ProfileGa/Application and leaving this information behind.
I would change this button:
<input type="button" value="Appliquer" .. etc
to a <submit>, and wrap the code with the dropdown in one of these:
using (Html.BeginForm("Application", "ProfileGa")) {
and add a Post version of Application
[HttpPost]
public ActionResult Application(FlowViewModel model)
{
// Do whatever
return View(model);
}
Then when you get to the Application view, it should still have the same information as it left Index with.
To check this is working, put a breakpoint at return View(model); and look at the model's contents.
However, posting null from the view probably means that something is wrong inside your <% using (Html.BeginForm("app", "ProfileGa")) { %> statement, so if the above doesn't do anything, post the code from your `Application' view.

How to execute 3 different controller methods from the same view

I have created a C# ASP.NET MVC application. In the Index view, i have added 3 buttons, when each button is clicked i want to execute 3 different functions from the Index controller.
Index View that resides in the Home folder
#using (Html.BeginForm()) {
<input type="submit" value="b1" />
<input type="submit" value="b2" />
<input type="submit" value="b3" />
}
Home Controller
public ActionResult Button1Click()
{
return View();
}
public ActionResult Button3Click()
{
return View();
}
public ActionResult Button2Click()
{
return View();
}
When each button is clicked how can i write code to execute the correct controller method ?
If you are posting then you can put each button in a separate form:
#using (Html.BeginForm("Button1Click","Index")) {
<input type="submit" value="b1" />
}
#using (Html.BeginForm("Button2Click","Index")) {
<input type="submit" value="b2" />
}
#using (Html.BeginForm("Button3Click","Index")) {
<input type="submit" value="b3" />
}
If there is no data to post, as shown in your method, and you still want to have all buttons in the same form then you can do an ajax post (this does not make sense though but hey I'm basing it on the code you gave in your question), with this though you may want to change your buttons from a submit into a button (input type="button").
$("#b1").click(function(){
$.post('/index/button1click', function() {
});
});
$("#b2").click(function(){
$.post('/index/button2click', function() {
});
});
$("#b3").click(function(){
$.post('/index/button3click', function() {
});
});
If you want to do a GET instead of a post then just replace .post with .get.
In MVC you need to remove the (Asp.Net) idea of linking button clicks to actions. ASP.Net is event driven MVC uses the classic HTTP REST approach.
So the buttons aren't actions, the buttons submit actions. The action that is submitted is controlled by your form. So your form POSTs data to the controller, using a HTTP post.
Now it's not clear what your trying to achieve here. You appear to be returning different views from each action. So using the REST idea, you should be a GETing not a POSTing (your getting HTML). So the simplest idea is to turn your input(submit) into Anchor tag, i.e. a HTTP GET:
#Html.ActionLink("Button1Click")
etc.
MVC doesn't work like Webforms where you have a ButtonClick event.
Do you want to post any values to the controller?
If not, you can use a link that you can style like a button. Use the buildin Html extensions.
//For links
#Html.ActionLink("Button1Text","Button1Click")
#Html.ActionLink("Button2Text","Button2Click")
#Html.ActionLink("Button3Text","Button3Click")
//If you need more styling options
Button1
Button2
Button3
That way you don't need any javascript or multiple forms in your view. You'll have to add some styling in your CSS files.
One easy way to execute different actions on different button within the same form is to distinguish button click by their name:
Example code is:
View:
#using (Html.BeginForm("MyMethod","Controller"))
{
<input type="submit" value="b1" name="b1" />
<input type="submit" value="b2" name="b2" />
<input type="submit" value="b3" name="b3" />
}
Controller:
[HttpPost]
public ActionResult MyMethod(string b1, string b2, string b3)
{
if (b1 != null)
{
return Button1Click();
}
else if (b2 != null)
{
return Button2Click();
}
else
{
return Button3Click();
}
}
public ActionResult Button1Click()
{
return RedirectToAction("Index");
}
public ActionResult Button3Click()
{
return RedirectToAction("Index");
}
public ActionResult Button2Click()
{
return RedirectToAction("Index");
}

Categories