Is it possible from a Controller to show a view, and then dependant on what that user selects in dropDownList - render another different view back in the original calling controller? Kind of a "daisy-chaining" effect.
The thinking behind this - is a user selecting a vehicle type - (associated with an ID number) in a view, back in the Controller dependant on what was chosen will render another view immediately displaying HTML according to the vehicle type they chose e.g. an HTML page for car or a boat or aeroplane etc...
If this is possbile can someone point me to a code examaple?
Actual Database Model below - but it is for documents, not vehicles!
check the method paremetares of your action method and return different views baed on that . Something like this.
public ActionResult GetInfo(string id,string vehicleTypId)
{
if(String.IsNullOrEmpty(vehicleTypeId))
{
var vehicle=GetVehicleType(vehicleTypId);
return View("ShowSpecificVehicle",vehicle) ;
}
var genericVehicle=GetVehicle(id);
return View(genericVehicle);
}
EDIT : Saying so, I seriously think you should keep those in 2 seperate Action methods. That makes your code clean and better readable. You may move the common functionality to a function and call if from bothe the action methods id needed. So i would do it in this way
Assuming you have a ViewModel for the first page( displays all vehicletypes)
public class VehicleTypesViewModel
{
//other relevant properties
public IEnumerable Types { set;get;}
public int SelectedTypeId { set;get;}
}
Your GET request for the initial view will be handled by this action result.It gets all the Vehicle types and return that to your view in the ViewModels Types property.
public ActionResult VehicleTypes()
{
VehicleTypesViewModel objVM=new VehicleTypesViewModel();
objVM.Types=dbContext.VehicleTypes.ToList();
return View(objVM);
}
and in your View called VehicleTypes.cshtml,
#model VehicleTypesViewModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(Model.SelectedTypeId,new SelectList(Model.Types,"Text",Value"),"Select")
<input type="submit" value="Go" />
}
Another Action method to handle the form post. You have the selected type id here and you can get the specific details here and return a different view
[HttpPost]
public ActionResult VehicleTypes(VehicleTypesViewModel model)
{
// you have the selected Id in model.SelectedTypeId property
var specificVehicle=dbContext.Vehicles.Where(x=>x.TypeId=model.SelectedTypeId);
return View("SpecificDetails",specificVehicle);
}
Alternatively you can do a Get request for the specific vehicle using RedirecToAction method. I would prefer this approach as it sticks with the PRG pattern.
[HttpPost]
public ActionResult VehicleTypes(VehicleTypesViewModel model)
{
int typeId=model.SelectedTypeId;
return RedirectToAction("GetVehicle",new {#id=typeId});
}
public ActionResult GetVehicle(int id)
{
var specificVehicle=dbContext.Vehicles.Where(x=>x.TypeIdid);
return View(specificVehicle);
}
With Javascript : You can do a get call to the new view from your javascript also. without the HTTPpost to controller. You should add some javascript in your initial view for that
#model VehicleTypesViewModel
//Include jQuery library reference here
#Html.DropDownListFor(Model.SelectedTypeId,new SelectList(Model.Types,"Text",Value"),"Select")
<script type="text/javascript">
$(function(){
$("#SelectedTypeId").change(){
window.location.href="#Url.Action("GetVehicle","Yourcontroller")"+"/"+$(this).attr("id");
});
});
</script>
I think to get a better user experience create a partial view, and load that partial view in a div in the same page via an ajax call.
public ActionResult GetVehicalInfo(string id, string vehicleType)
{
var vehicle = GetVehicleType(id, vehicleTypId);
return PartialView("vehicle);
}
Related
I am new to MVC and Web Api, and getting stuck.
Please Note: I am using ApiControllers, not Controllers!
Situation
I have two partial views. The first has drop down lists and a submit button. The second is just a confirmation view showing what the user selected after user submits. On clicking submit, I want to send a string (containing the user's combined selections) to the second partial view and update it.
Question
When I press submit on the first partial view, I want to send a string to the second partial view and update the second partial view. How can I do this? The sending of the string can be indirect, of course, like view to controller then back to view. I just want to know at least one way.
Again, note that I am using Web Api Controllers, NOT Controllers. Most people use normal Controllers, which doesn't apply to me. They are different.
Things I tried / What I believe I cannot do from other StackOverflow answers:
I cannot use a Controller method to return a Partial View, like in this very similarly asked question Same question but not with ApiControllers.
I cannot store any data into a class, so I cannot strongly type a view with #model ExampleClass to access stored contents with #Model.ExampleProperty.
Since I cannot store data into a class or return a model from a controller method like View(model), I also could not use Html.DropDownListFor(model => model.SelectedValue) to store the SelectedValue into my model and access it in the other partial view. Instead to populate my drop down lists, I manually added a div, called a GET method that returned a list, and populated the lists by appending to them.
So, what exactly CAN I do? I also tried ViewData, but the stored data would not last after a Controller method finished. I am out of ideas. I hope I am mistaken about something and missed a method, because it feels it should not be this hard. If you could even briefly describe the flow of how I would transfer a string and/or update the second partial view after pressing submit, that would be greatly appreciated!
Thanks so much in advance!
Model class
public class Vehicle
{
public class Make
{
public int MakeId { get; set; }
public string MakeName { get; set; }
}
public class Model
{
public int ModelId { get; set; }
public string ModelName { get; set; }
}
}
ApiController class. The ViewData seems to not save its contents. I wanted to store the user's selections into ViewData with the first POST method, and GET the selections with the second method, but the contents of ViewData have become null already.
public class VehicleController : ApiController
{
public ViewDataDictionary ViewData = new ViewDataDictionary();
[HttpPost]
public void StoreMakeAndModel(string user_selections){
ViewData["message"] = user_selections;
}
[HttpGet]
public string ConfirmationMessage(){
ViewData["message"] = "You selected the " + ViewData["message"].ToString();
return ViewData["message"].ToString();
}
//This returns a list to populate a drop down list
[HttpGet]
public IEnumerable<Vehicle.Make> Makes(){
//This function reads from xml and returns List<Vehicle.Make>
...
return makesList;
}
//This returns a list to populate a drop down list
[HttpGet]
public IEnumerable<Vehicle.Model> Models(int id){
//This function reads from xml the car models that match the make id
...
return modelsList;
}
}
First partial view It has 2 drop down lists and submit button. There is a hidden div that shows once the form submits that renders the second partial view. I hoped that it would load only when it is shown so that I can use $(document).ready(function()) to display my string there, but it loads when the main page loads even though it's hidden. So I don't know how to update it with the string after the first partial view submits.
<select required id="makes_DDL" name="makes_DDL"></select>
<select required id="models_DDL" name="models_DDL"></select>
<input type="submit" id="submit_button" value="Submit Form" onclick="formSubmit()" />
<div id="thank_you_div" style="display:none;">
#Html.Partial("_ThankYou");
</div>
function formSubmit() {
//Combines the make and model into one string, Ex: Chevrolet Malibu
var parameter = $("#makes_DDL").children(":selected").text() + " "
+ $("#models_DDL").children(":selected").text();
var uri = "api/vehicle/storemakeandmodel/" + parameter;
$.ajax({
url: uri,
type: "post",
updatetargetid: "thank_you_div",
success: function (data) {
$("#thank_you_div").show();
},
});
}
My list population looks like this; this is my first list. I could not get #Html.DropDownListFor to work because I could not store anything since I cannot return something like View(model) from a controller method.
$(document).ready(function () {
$.getJSON("api/vehicle/makes", function (makes) {
$("#makes_DDL").append("<option value= ''>Select a make</option>");
$.each(makes, function (id, make) {
$("#makes_DDL").append("<option value=" + make.MakeId + ">" + make.MakeName + "</option>");
});
});
});
Second partial view It tries to get the stored message in ViewData, but it is already gone and GETS null.
<div id="thank_you_div">Thank you!</div>
<script type="text/javascript">
$(document).ready(function () {
alert("Thank You Partial View ready!");
$.ajax({
url: "api/vehicle/confirmationmessage",
type: "get",
updatetargetid: "thank_you_div",
success: function (message) {
$("#thank_you_div").html(message)
}
});
})
</script>
Take a look at your ApiController action methods.
[HttpPost]
public void StoreMakeAndModel(string user_selections)
{
ViewData["message"] = user_selections;
}
[HttpGet]
public string ConfirmationMessage()
{
ViewData["message"] = "You selected the " + ViewData["message"].ToString();
return ViewData["message"].ToString();
}
Looks like you are trying to store data to ViewData when the first method is called and read it in the second method when you make your second ajax call. But that will not work! Because http is stateless. Your second call has no idea what your first call did. You cannot store data in ViewData to access between multiple http requests. It does not make sense to use ViewData in an api controller at all. Api controller endpoints should be purely to return data.
In your case, what you should be doing is, simply have your first method return the string message you want. In the success handler of the first ajax call,you will get this string (Response from the api method) in the success call back. You can use this to update the DOM as needed.
There is no need to have the partial view as well. All you need is a container div element to show the response.
<select required id="makes_DDL" name="makes_DDL"></select>
<select required id="models_DDL" name="models_DDL"></select>
<input type="submit" id="submit_button" value="Submit Form" onclick="formSubmit()" />
<div id="thank_you_div" style="display:none;"></div>
Now in your first ajax call's success callback, update the DOM with the response coming back.
Also $.ajax does not have a setting property called updatetargetid !
function formSubmit() {
//Combines the make and model into one string, Ex: Chevrolet Malibu
var parameter = $("#makes_DDL").children(":selected").text() + " "
+ $("#models_DDL").children(":selected").text();
var uri = "api/vehicle/?user_selections=" + parameter;
$.ajax({
url: uri,
type: "post"
}).done(function(res) {
$("#thank_you_div").html(res).show();
}).fail(function(x, v, e) {
console.log(e);
});
}
Assuming your api controller method returns the string you want
[HttpPost]
public string StoreMakeAndModel(string user_selections)
{
return "You selected the "+user_selections;
}
How can I send DropDownList's SelectedValue to the Controller from View with BeginForm?
Here's my code:
#using (Html.BeginForm(new { newvalue=ddl.SelectedValue}))
{
#Html.DropDownList("categories",
(List<SelectListItem>)ViewData["categories"],
new { onchange = "this.form.submit()", id = "ddl" })
Do not use ViewData or ViewBag in place of your model. It's sloppy, prone to error and just an unorganized way of giving your view data.
{ newvalue=ddl.SelectedValue} is going to do nothing for you when placed on the form itself. You need to understand that everything you're writing is evaulated on the server before being sent down the client. So if newvalue resolves to 1 it will continue to stay 1 forever unless you have javascript that changes it on the clientside (which you're not doing and you shouldn't be doing).
First you need a model:
public class CategoryModel()
{
public IEnumberable<SelectListItem> CategoriesList {get;set;}
public int SelectedCategoryId {get;set;}
}
Controller
public class CategoryController()
{
public ActionResult Index()
{
var model = new CategoryModel();
model.CategoriesList = new List<SelectListItem>{...};
return View(model);
}
public ActionResult SaveCategory(CategoryModel model)
{
model.SelectedCategoryId
...
}
}
View
#model CategoryModel
#using(Html.BeginForm("SaveCategory","Category"))
{
#Html.DropDownListFor(x=> x.SelectedCategoryId, Model.CategoriesList)
<button type="submit">Submit</button>
}
What's happening here is SelectList is being populated from the IEnumerable and it's form name is SelectedCategoryId, that's what is posed back to the server.
I'm not sure where your knowledge of http and html ends, but you should not be using any framework until you understand how http and html work and then what these helpers such as begin form and Html.DropDownList are actually doing for you. Understand how the screw works before you try to use the screw driver.
I'm working with ASP.NET MVC5, but very new to it.
Is there a way to pass a specific instance of a model between views and the controller multiple times without using a form POST... for example...
When the controller is handling the request for the "FirstView" page request, the CarModel class is instantiated and an API call is made on the object to retrieve values from an API and set the object's properties.
The object is then passed to the "FirstView" page as the model and the properties are displayed for the user to view.
If the user is happy with the properties, they can then click the ActionLink to save the properties to a database.
I want to pass the entire model object back to the controller so that the work can be done to do the database insert... which I have shown in my controller as "SecondView".
If I am totally off track in terms of how this should be done, please could someone point me in the right direction and link me some reading materials... THANK YOU!
Here is my model...
public class CarModel
{
public string Make { get; set; }
public string Colour { get; set; }
public void GetModelDataFromAPI()
{
Make = "BMW";
Colour = "Black";
// many other properties, but ommitted for brevity
}
public int InsertPropertiesIntoDatabase()
{
// code to do the database insert anbd return an ID
return 1;
}
}
Here is my FirstView.cshtml...
#model CarsApp.Models.CarModel
<html>
<body>
<h1>First View</h1>
Make: #Model.Make, Colour: #Model.Colour
#Html.ActionLink("Add model properties to database", "SecondView", "Home")
</body>
</html>
Here is my controller...
namespace CarsApp.Controllers
{
public class HomeController : Controller
{
public ActionResult FirstView()
{
CarsApp.Models.CarModel objCarModel = new Models.CarModel();
objCarModel.GetModelDataFromAPI();
return View("FirstView", objCarModel);
}
public ActionResult SecondView()
{
// need to get a reference to the model from the first view
// then call the insert method of that model, with the properties
// holding the values from the API
return Content("ID:" + objCarModel.InsertPropertiesIntoDatabase());
}
}
}
I am fairly new to MVC and had a question about a form I am creating. The page has a form at the top and a grid at the bottom. As people enter data into the form and click the button, the form data is added to the grid below.
My plan is to use a BeginForm and send the form to an HttpPost controller method for processing and then bounce back to the view. Currently, I am using this for the form on the view:
#using (Html.BeginForm("AddRefund", "Refund", FormMethod.Post))
In the controller, I have this:
[HttpPost]
public ActionResult AddRefund(RefundModel refund)
{
if (ModelState.IsValid)
{
(etc...)
My problem is that the "refund" object in controller always arrives from the view empty. From my research, it seems that the model reference in the controller is just there to provide model structure, and NOT to receive the actual model from the view. I don't understand why this is, however, as it would seem very valuable to be able to send a populated viewmodel from the view to a controller.
Also, how would you guys handle the code for this problem? How would you collect all of these form submissions from the user, present them to the user in the grid below the form, and then ultimately submit the page and insert all of the items in the grid into the database?
edit: here is my view
#model RefundsProject.Models.RefundModel
#using (Html.BeginForm("AddRefund", "Refund", FormMethod.Post))
{
(all of the form elements are here)
<input id="button-add" type="submit" value=" Add Refund to List " />
}
Eventually, there will be another button at the very bottom of the view that will submit all of the items the user entered into the grid to the database.
From my research, it seems that the model reference in the controller is just there to provide model structure, and NOT to receive the actual model from the view.
This is completely the opposite of the way ASP.Net MVC was designed. ASP.Net comes with default ModelBinders that are used to Bind data from a Form, Querystring, Ajax (Json and XML) to a strongly typed object for a Controller Method.
My problem is that the "refund" object in controller always arrives from the view empty.
This is most likely due to a lack of knowledge or a misunderstand of how model binders work.
Also, how would you guys handle the code for this problem?
I would Ajax Post the RefundModel back to the controller to validate the refund. If it is valid, then dynamically create fields in the form that will eventually model bind back to an IEnumerable/List on a new method that will then verify all the refunds, one at a time (to validate the data again).
Here is an Extremely broken down example (probably needs some work, but the important parts are there):
Classes:
public class AddRefundsViewModel
{
public RefundModel Refund { get; set; }
}
public class RefundModel
{
public string Reason { get; set; }
public Decimal Amount { get; set; }
}
Methods:
public ActionResult AddRefunds()
{
var model = new AddRefundsViewModel()
model.Refund = new RefundModel();
return this.View(model);
}
[HttpPost]
public ActionResult ValidateRefund(AddRefundsViewModel model)
{
var result = new { isValid = modelState.IsValid };
return this.Json(result);
}
[HttpPost]
public ActionResult ValidateRefunds(IEnumerable<RefundModel> model)
{
var isRefundsValid = true;
foreach (var refund in model)
{
isRefundsValid = TryValidateModel(refund);
if (!isRefundsValid )
break;
}
if (isRefundsValid)
{
}
else
{
// either someone hacked the form or
// logic for refunds changed.
}
}
Views:
#model AddRefundsViewModel
// assuming RefundController
#using (Html.BeginForm("Refund", "ValidateRefunds", FormMethod.Post))
{
#html.EditFor(m => m.Refund.Reason)
#html.EditFor(m => m.Refund.Amount)
<input type="button" id="addRefundButton" name="addRefundButton" value="add"/>
<input type="submit" id="submitRefundButton" name="submitRefundButton" value="submit all"/>
}
<!-- jquery -->
$(document).ready(function()
{
$('#addRefundButton').on('click', function()
{
$.ajax({
url: '/Refund/ValidateRefund',
data: $("addRefundForm").serialize(),
success: function(result)
{
if (result.isValid)
{
// create new hidden imput elements, and grid
$("addRefundForm")[0].reset();
}
else
{
// Refund isn't valid
}
}
});
});
});
From my research, it seems that the model reference in the controller is just there to provide model structure, and NOT to receive the actual model from the view. I don't understand why this is, however, as it would seem very valuable to be able to send a populated viewmodel from the view to a controller.
Your a bit wrong. There is a difference between ViewModel and Domain Model. View Model is a class that you use to process the logic between views and your domain (business).
Then there is Domain Model (in .net) this is usually some data container objects (POCO). This is anemic. Based on DDD there is a little difference.
So what is the best practive?
It is always good to use a ViewModel object to transfer data between your views and controller.
Then in controller you can use a mapper (automapper or valueinjecter) to transform them.
Now you have your domain object that you can process.
Using ViewModels to pass data both up and down between controllers and views is completely acceptable.
To help with your model coming up empty issue, inputs, such as <input id="FirstName" type="text" /> need to have name attributes for the MVC model binder to map what you posted into your RefundModel object. In your View code you shared, you only showed a submit button, so it is unclear if your other elements you expect to get mapped have names or not.
To fix my above example of an input tag, you would do <input id="FirstName" name="FirstName" type="text" /> or use a Razor helper: #Html.TextBoxFor(m => m.FirstName)
Lets say that i have an URL that looks something like this: localhost/userdetails/5 where 5 is the users ID. Is there any way to make use of the ID directly in the view (razor viewengine) and show the details? Or do i handle it in the default action in the controller?
To keep things simple now, focusing on getting the id to the view, you basically want to use the id to populate your model with data and then pass that to the view. So in your controller:
public ActionResult Index(int id)
{
UserModel model = db.Users.Where(u => u.Id == id).SingleOrDefault();
return View(model);
}
The view (very simplified) might look like this:
#model MyProject.Models.UserModel
#Html.DisplayFor(m => m.Id)
#Html.DisplayFor(m => m.Username)
This is very basic though. Eventually, you'll get to a point where you realise you should use viewmodels for your views instead of a domain model that's come directly from the data source. That in itself gives you another problem to solve in the form of mapping properties from the domain model onto your viewmodel. Typically, AutoMapper or ValueInjecter are used for that. For now though, it's probably best to just focus on passing data to a view and getting it back into a controller so that you can do something with it.
Update
This is a simple scenario which demonstrates how to get the data back into the controller. So basically, you'd have a form which you would submit:
#using (Html.BeginForm("Index", "Home"))
{
// Form elements and submit button
}
That would post the data to this action method for you to do whatever you wish with the data:
[HttpPost]
public ActionResult Index(UserModel inputModel)
{
// Check to see if the model's data was valid.
if (ModelState.IsValid)
{
// Do something in the database here.
// Then redirect to give the user some feedback.
return RedirectToAction("Thanks");
}
// The model validation failed so redisplay the view.
return View(inputModel);
}
you can use this in both the controller or in the View as an extension method.
Example: asuming your routes id holder has the default values in global.asax
public int IdFromAdress(HttpContext httpContext)
{
RouteData rd = httpContext.Request.RequestContext.RouteData;
string stringId = (string)rd.Values["id"];
return int.Parse(stringId);
{
You can get the id with this
#HttpContext.Current.Request.RequestContext.RouteData.Values["id"].ToString()
But I would reccomend to use a ViewMdoel to pass the value to the view and not the ViewBag or accessing directly from the view
You should use the model (i.e. the model passed back to your view). A ViewBag is another option but since the ID is part of the model itself, it wouldn't make any sense to do that.
View
#model User
#{
ViewBag.Title = "User Details";
}
#Model.Id;
Controller
public ActionResult UserDetails(int id)
{
return View("UserDetails", (object)id);
}
Yes you can. There is more than one way to do it, but since you've tagged your post MVC, assume you'll want to do it the 'MVC way', which means (imo) using a view model.
So you write a view model
public class MyViewModel()
{
public int ID {get; set;}
}
You populate the model in the controller and pass it to the view
public ActionResut MyView (int id)
{
var viewModel = new MyViewModel {ID = id};
return View (viewModel);
}
Then you have a strongly typed view (strongly typed to the MyViewModel, that is)
and you can reference the model's properties
#Model.ID
Then to make this useful, you can add whatever other properties you're wanting to work with to your view model. Then you can populate them in your controller before rendering the view (to show user info, for example), or let the user populate them for you in the view (using textboxes and such wrapped in a form). Then you can collect the user input in the post action in the controller like so
[HttpPost]
public ActionResult MyView(MyViewModel viewModel)
{
//do stuff with the data from the viewModel
}