Basic ASP.NET MVC query with parameter - asp.net

I am new in ASP.Net MVC and MVC architecture in general. I'm building a simple application using the Database Code First method.
I have a Recipe Model with a property called cookId which is the id of the user who created the recipe.
Now I want to be able to pass a querystring to my page and get ONLY the recipes where the cookId is the same as the parameter and list i on my View.
How can I achieve this? Where should i put this logic? In my controller or in my view?

Well, asp.net mvc works with routes, or TableRoutes. The default routes is created with this format: {controller}/{action}/{id}.
So, when you get a request on your action, you could retrive this id from id parameter on your Action (at controller) and use this value to hit on your database and get all records you need to show on the View. You could try something liek this:
public ActionResult Recipes(string id)
{
IEnumerable<Recipe> list = _repository.GetRecipeByCookId(id); // this method should return list of Recipes
return View(list); // return your View called "Recipes" passing your list
}
You also could use Request.QueryString["Id"] to get the Id, but it is not a good pratice in asp.net mvc. You can use parameters on your action and use it.
On your View, you could type it with the IEnumerable<Recipe> and show it on a table, something like:
#model IEnumerable<Recipe>
<table>
#foreach(var recipe in Model)
{
<tr>
<td>#recipe.Name</td>
<td>#recipe.CookId</td>
<td>#recipe.OtherProperties</td>
</tr>
}
</table>
To create an link passing this id for the request, you could just use Html.ActionLink, something like on your View:
#Html.ActionLink("Text of You Link", "Action", "Controller", new { id = 5, another = 10 }, new { #class = "css class for you link" });
and asp.net mvc will render an a tag with a apropriated route following routetable setted on global.asax. If you have other parameters to pass in querystring, you also could add it like I did on sample with another parameter.

NEVER put logic in the view. The view should simply display the information provided in the model. Put the logic in the controller.
Controller:
[HttpGet]
public ActionResult Recipes(int cookId)
{
var recipes = /* get recipes based on cook */;
List<RecipeModel> model = recipes
.Select(r => new RecipeModel
{
Id = r.Id,
CookId = r.CookId,
...
})
.ToList();
return View(model);
}
View:
#model List<RecipeModel>
#foreach (RecipeModel item in Model)
{
<div>
<span>Id:</span>
<span>#item.Id</span>
</div>
}

Controller:
[HttpGet]
public ActionResult GetRecipes(int cookId)
{
// model can view a List<Recipe>, logic goes here
var model = SomeQueryThatReturnsRecipesFrom(cookId);
return View(model)
}
View (for example views\yourController\GetRecipes.cshtml), only use this file to show data, its not recommendend to put logic here:
#model List<Namespace.Recipe>
<h2>Recipes</h2>
#foreach(var r in Model)
{
<p>r.Name</p>
}
This will be called with the following querystring:
/Recipes/GetRecipes?cookId=SomeId

You might have a CooksController. That controller would return a list of cooks. That list might include a link for the cook's recipes. Your RecipesController could handle the request for all recipes for a given cookId.
#Html.ActionLink("Recipes", "RecipesByCook", "Recipes", new { cookId = model.cookId }, null};
The above code is used in the view Cooks/Index.shtml. It creates a link that uses the query string to identify the cookId like you want.
The RecipesController would then have a method RecipiesByCook which takes a parameter for the cookId. This method will handle requests for URLs like this, Home/Recipies/RecipeByCook?cookId=4.
Your RecipesController can then return an ActionResult with the correct set of recipes to show. Very simply (as in you may want to add more for the view to show, like information about the cook):
public ActionResult RecipesByCook(int cookId)
{
var recipes = repository.Recipes.Where(r => r.cookId == cookId);
return View(recipes);
}

Related

ASP.NET MVC - Return a JSON object to a view and "format as table"

I have a view that has been developed by another developer in which there is already code for a javascript that should handle the JSON Object and format as an HTML Table.
I'm new into MVC, and from the Controller, I create a JSON Object that contains what I need. The issue is that from my Controller, if I just return the JSON Object, then the browser just shows the raw JSON string to the client.
It seems that when I return a JSON Object, the browser just shows the JSON, without actually calling my view that has the code to handle the JSON and making it user-friendly.
This is my controller:
public JsonResult GetPlayerNameByID(int playerID)
{
var player = GetPlayerByID(playerID);
return Json(player, JsonRequestBehavior.AllowGet);
}
This is called trough the on click event of a Dropdown List.
The view for that page is like this:
#model FirstApp.Models.PlayerViewModel
<div id="container" class="container">
<table class="table player">
<thead>
<tr>
<th class="Name">Name</th>
<th class="Overall">Overall</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
I believe my issue is that the controller doesn't return to that view, but just a raw JSON Object.
Any suggestion on how can I return to that view (this is the view from which the call has been made).
In order to return a view, you need to return a view. Something like:
return View();
Or, if it needs to include a model:
return View(someModel);
But your controller action is just returning JSON data:
return Json(player, JsonRequestBehavior.AllowGet);
You can't return both. So it sounds like you have two options:
Return a view with the player object as its model. In this case the view would render the data from the model directly and there would be no need for JSON or any involvement with JavaScript. (Well, there could be, depending on what your view is and what you need to do. But it wouldn't be at the controller level.)
Have two controller actions. One which returns the view, another which returns the JSON data. The client-side JavaScript code would make an AJAX request to the second action to fetch the JSON data.
Which one you choose is up to you really. But ultimately you can't return both a view and raw JSON from the same action.
To give an example of the second option, you would have two actions:
public ViewResult Player(int playerID)
{
return View(playerID);
}
public JsonResult GetPlayerNameByID(int playerID)
{
var player = GetPlayerByID(playerID);
return Json(player, JsonRequestBehavior.AllowGet);
}
The first method would return your Player view (assuming you have one of course), with just an integer as the model. So the view would start with a declaration for that model type:
#model int
This means that inside the view code you would be able to access the playerID in the variable Model. So in the view's JavaScript code you might capture that into a variable:
let playerID = #Model;
Which, if for example the playerID value were 123, this would render client-side as:
let playerID = 123;
From there you'd use AJAX to make a request to #Url.Action("GetPlayerNameByID") to get the JSON data. How you do this would depend on what JavaScript frameworks/libraries you're using, if any, and there are many examples available online for how to use AJAX in a variety of frameworks and in ASP.NET.

MVC4 Incorrect model error

Hi I getting this error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[DBModel.Telemarketing]', but this dictionary requires a model item of type 'TWeb.Models.LoginModel'
In _Layout.cshtml file i have
#Html.Partial("_LoginPartial")
this partial login view is rendered in div on _layout page (it`s hides/shows with javaScripts )
#model TWeb.Models.LoginModel
Then I have "Telemarketings" controller having view:
public class TelemarketingController : Controller
{
private Entities db = new Entities();
//
// GET: /Telemarketing/
public ActionResult Index()
{
return View(db.Telemarketings.ToList());
}
When I click link in _Layout page
#Html.ActionLink("Telemarketingas", "Index", "Telemarketing", new{area="" },new{ })
It throws an error written in top of the post.
I am new in MVC, please help me.
problem 1) Your Partial requires a model, and you're not passing one.
proper syntax: #Html.Partial("_LoginPartial", Model.LoginModel)
problem 2) _layout, as far as I know, can't have a Model passed
Solution 1:
Use an ActionPartial. AcionPartials are called similarly,
#Html.Action("/Tools/_LoginPartial").
The difference is they have an ActionMethod Associated which can return a Model
public ActionResult _LoginPartial()
{
LoginModel Model= new LoginModel();
//populate Model from whatever
return View(Model);
}
Option 2:
Pass a LoginModel object to a Viewbag
Viewbag.LoginModel = new LoginModel();
and reference the Viewbag in your _layout's Partial
#Html.Partial("_LoginPartial", Viewbag.LoginModel)
Your "_LoginPartial" expects "LoginModel" model, but since you're not giving it any, Razor engine sets its model to the current view model ("db.Telemarketings.ToList()").
All you have to do is somehow set its model, probably like so:
#Html.Partial("_LoginPartial", new LoginModel())
Simplest way was to remove model declaration from Login Div :).
You can use this code
#Html.Partial("Partial page", new ModelFroLogin())

asp.net mvc Serverside validation no return data

I'm building a validation form in my application. In that form there are two buttons. One to accept and one to reject. When the user press reject the rejection reason field must be provided. I check this serverside.
I first check what button is pressed and then if the field is empty I add a moddel error to the modelstate. But, because all fields in the form are readonly, those are not posted back to the server and therefor when I return the view back to usern there is no data. I'm probably missing something obvious, but cant find what to do. (I know I can make all fields in my form hidden, but due to the large amount of fields this would be really ugly)
This is my code.
[HttpPost]
public virtual ActionResult Validate(string action, Record dto) {
if(action == Global.Accept) {
ciService.Store(dto);
return RedirectToAction("Index", "Ci");
} else {
if(string.IsNullOrEmpty(dto.RejectionReason)) {
ModelState.AddModelError("RejectionReason", "REQUIRED!!!!");
return View("Validate", dto);
}
ciService.Reject(dto);
return RedirectToAction("Index", "Ci");
}
}
You need to recreate the model from the database and then change it to match whatever changes are posted in dto. Then use that combined model in the view.
Instead of passing the DTO back from the browser, I would use a hidden HTML field or a querystring parameter containing the ID that identifies the DTO. Then your POST action method would look something like:
[HttpPost]
public virtual ActionResult Validate(string action, int id)
{
// reload the DTO using the id
// now you have all the data, so just process as you did in your question
if (action == Global.Accept) { ... }
...
}
Your GET method might look something like the following then...
[HttpGet]
public virtual ActionResult Validate(int id)
{
// load the DTO and return it to the view
return View();
}
In this way you have all the data you need within your POST action method to do whatever you need.
You need to have hidden fields corresponding to each property displayed in UI.
e.g.,
#Html.LabelFor(m=>m.MyProperty) - For Display
#Html.Hiddenfor(m=>m.MyProperty) - ToPostback the value to server
If I understand right, the problem is because you don't use input.
To solve your problem insert some input hidden in your form with the value you need to be passed to the controller
#Html.HiddenFor(model => model.Myfield1)
#Html.HiddenFor(model => model.Myfield2)
that should fix the values not passed back to your actions
If you don't need these fields on the server side, simply create a new ViewModel
RecordValidateViewModel and this contains only the fields in it that need to be validated. The model binder will populate then and you will have validation only on the fields in that model rather than all the other fields you don't seem to want there.
If you need them to validate, then post them back to the server. Its not 'ugly' if hidden.

ASP.NET MVC 2 actions for the same route?

Suppose I have defined route like that,
context.MapRoute(
"Preview",
"/preview/{id}/{type}",
new { controller = "Preview", action = "Invoice", id = UrlParameter.Optional, type = UrlParameter.Optional }
);
I have controller with action Invoice
public ActionResult(int id, string type)
{
if (type == "someType")
{
// ...
}
else
{
// ..
}
}
I want to get rid of If-Else case inside the action. Is it possible to attribute action somehow, so ASP.MVC would distinguish between both, like:
Just a pseudocode tho show idea?
[HttpGet, ActionName("Action"), ForParameter("type", "someType")]
public ActionResult PreviewSomeType(int id) {}
[HttpGet, ActionName("Action"), ForParameter("type", "someType2")]
public ActionResult PreviewSomeType2(int id) {}
Is something like that possible in MVC2/3 ?
Action method selector
What you need is an Action Method Selector that does exactly what you're describing and are used exactly for this purpose so that's not a kind of a workaround as it would be with a different routing definition or any other way. Custom action method selector attribute is the solution not a workaround.
I've written two blog posts that will get you started with action method selection in Asp.net MVC and these kind of attributes:
Improving Asp.net MVC maintainability and RESTful conformance
this particular post shows an action method selector that removes action method code branches what you'd also like to accomplish;
Custom action method selector attributes in Asp.net MVC
explains action method selection in Asp.net MVC to understand the inner workings of it while also providing a selector that distinguishes normal vs. Ajax action methods for the same request;
I think thats what routing is for.
Just split your single route into two:
context.MapRoute(null, "preview/{id}/SomeType",
new {
controller = "Preview",
action = "Action1",
id = UrlParameter.Optional,
}
);
context.MapRoute(null, "preview/{id}/SomeOtherType",
new { controller = "Preview",
action = "Action2",
id = UrlParameter.Optional,
}
);
Or maybe more general, use the last segment as the action name:
context.MapRoute(null, "preview/{id}/{action}",
new { controller = "Preview",
id = UrlParameter.Optional,
}
);
But i think, optional segments have to appear at the end. So why don't you use the default routing schema
controller/action/id
?

model size check

I've had some success passing query results to my view.
Unfortunately the ASP/Razor code balks in the foreach when the resulting model list has 0 entries.
#foreach (var action in (List<LemonTrader.Models.Lemon>)ViewData["lemons"]) {
<tr>
<td>
#Html.Encode( action.acidity)
If there are no entries it says something about a null exception.
What is the best way to handle the case where the list is empty?
I guess I could put a code block in and have it do an if/then branch. This seems to deviate a bit from the elegant razor one-liner of #foreach.
I guess I could put blank stuff in the controller and then just display something blank.
Those don't seem like very elegant approaches.
Any better ideas?
Try do next:
Create additional model (viewmodel) in Models folder (for example LemonsView.cs) and put there:
public class LemonList
{
public IQueryable<Lemon> AllLemons { get; set; }
}
Create a controller (LemonController.cs)
public ActionResult Lemons
{
var model = new LemonList();
var lemons = db.Lemon;
model.AllLemons = lemons;
return View(model);
}`
In View:
#using LemonTrader.Models.AllLemons
foreach(var item in Model.LemonList){
#item.Some
}
If in result you will have null, it will be a blank page
Have fun!

Resources