I wanted to create a simple paginated search for my project. I'm having trouble with my 'advanced search' page, where I have several textbox inputs that the user would fill with the appropriate data (basically just several filters).
My view is strongly-typed with the paginatedList class similar to the NerdDinner tutorial.
In my controller, I wanted to pass the PaginatedList as a parameter since my view contains several bits of info from the PaginatedList model. The PaginatedList was null (as parameter), then I changed added the route; the object itself is not null anymore, but the values are.
View:
<%= Html.TextBox("Manufacturers", Model.Manufacturers) %>
<%= Html.TextBox("OtherFilters", Model.FilterX) %>
//...etc etc
Controller:
public ActionResult AdvancedSearchResults(PaginatedList<MyModel> paginatedList) {
//...
}
Any ideas? Am I even going about this correctly? Should I create a ViewModel that encapsulates the paginatedList info as well as the additional info I need instead?
You may want to create SearchCriteria class which contains user's input data for filtering.
Controller will have the Action as below:
public ActionResult AdvancedSearchResults(SearchCriteria criteria)
{
PaginatedList<MyModel> result = SearchService.Search(criteria);
return View(result);
}
Related
In MVC 5 I am attempting to use the controller to render a partial view only if the (Windows Authenticated) user belongs to one or more of a list of Active Directory groups. There are over 30 distinct groups I need to account for, so the "hello world" examples don't fit my needs. After playing scavenger hunt on the web, I managed to collect this much. No compile or runtime errors, but the content is showing for all users rather than the specific users. So the desired outcome is not yet achieved.
While I can achieve the desired outcome using if-then logic in the view, it creates a lot of unnecessary duplication and encourages spaghettification. So I'm trying to do this in the controller.
Summary of Desired Outcome:
When the user loads the viewpage, the partial view should only render if the Windows Authenticated user belongs to one or more of a list of groups defined in the controller action. If the user is not authorized, then the partial view is not included.
Controller Block:
[ChildActionOnly]
[Authorize(Roles="Domain\\GroupA,Domain\\GroupB")]
public ActionResult MonitorCSU()
{
return PartialView("MonitorCSU");
}
View Block:
<div class="rowWithCols3">
#Html.Partial("MonitorCSU")
Unsuccessful Iterations:
In the controller block I tried (unsuccessfully) to use an if-then block, the else case being another partial view with no content.
[ChildActionOnly]
public ActionResult MonitorCSU()
{
if (User.IsInRole("Domain\GroupA")) {
return PartialView("_MonitorCSU");
}
else
{
return PartialView("_Unauthorized");
}
}
In Razor, I tried using HTML.Action but when I tried run the page the browser hung in an infinite loop.
#Html.Partial() returns a partial view without calling a controller method. In order to call your controller method, you need to use
#Html.Action("MonitorCSU")
or
#{ Html.RenderAction("MonitorCSU") }
Note this assumes that the MonitorCSU() method is in the same controller as the method that generates the main view (other wise you also need to include a parameter for the controller name)
Refer documentation
While you've found a solution, you're going to have other problems with it. I would suggest a different approach, which is to use EditorTemplates and create a separate model for the html you want to render. Then, at runtime you would check whether the user is in the groups you specify, and if they are, you create an instance of the model, and if they are not you leave the model null. In this way, when the view is rendered with EditorFor(), it will ignore and not render the template for users who do not have access.
Thanks to #Stephen Muecke and and a commenter whose entry has mysteriously vanished, I have the missing pieces.
I was able to test this code with several real users and verified the desired behavior happens consistently.
Controller Block:
Main difference: take out authorization and use an if-then block send one of two partial views.
[ChildActionOnly]
public ActionResult MonitorCSU()
{
if (User.IsInRole("DOMAIN\\GroupA"))
{
return PartialView("MonitorCSU");
}
else
{
return PartialView("Unauthorized");
// this is an empty page
}
}
View Block:
The key difference is using HTML.Action
<div class="rowWithCols3">
#Html.Action("MonitorCSU")
I looked around and couldn't find an easy solution.
I've tried #GetUserName which doesn't work.
I've tried # { GetUserName which doesn't work.
There has to be an easy way to call a method from the razor view engine.
It is within a foreach loop.
I need GetUserName(item.userID)
The below code is in my controller:
[ChildActionOnly]
public string GetUserName(int userID)
{
ProPit_User user = db.ProPit_User.Find(userID);
return user.username;
}
Trying to call a controller action method directly from your view is usually a sign of bad design.
You have a few options, depending on what you are trying to do:
Avoid calling the method, instead put the data in your model, and render the model
Use Html.RenderAction
Put the method in another class and use normal function syntax.
(1) is usually my default approach, often incorporating DisplayTemplates and EditorTemplates
For (3), e.g.
public static class Util
{
public string MyUtilMethod(int blah)
}
And the view:
#Util.MyUtilMethod(1)
Although you can obtain the controller instance from your view, doing so is plain wrong as it violates the whole MVC (and MVVM) paradigm, as the view should not be aware of its controller.
(The only possible reason I can think of where this would be useful would perhaps be for testing the controller from a mocked view, although even here, it should be possible to test the exposed controller functionality directly from unit tests):
#{
var controller = ViewContext.Controller as MyController;
var userName = controller.GetUserName(123);
}
The better way to have arrived at this result is for the controller to pre-populate, and pass all the data needed by the View, such as the userName, to a custom ViewModel (as typed by the #model directive at the top of the Razor page), or to the ViewBag dynamic.
Simple question, but I cannot find solution anywhere.
I have 1 Get ActionResult, 1 Post ActionResult and 1 View.
In the Get method I initialize some part from the model.
After that in the View I initialize the other part.
When the model comes in Post method, it is not initialized well.
How to pass the data throught View and Methods?
I don't want to use Temporary objects.
Example:
[HttpGet]
public ActionResult Register(Guid product_id)
{
RegistrationModel model = new RegistrationModel();
model.Product = **someProduct**;
return View(model);
}
[HttpPost]
public string Register(RegistrationModel model, Product Product)
{
Here the objects comes initialized only with the parts from the view...
}
You need to understand how the MVC works.
There is a concept called Model Binding which is responsible for Mapping a Model with suitable HTML.
When I say suitable I mean that it needs to be formatted according to specific rules.
In Order to simplify the things, MVC has inbuilt HtmlHelpers which handles the transition between a Model's property to an HTML Element.
From your [HttpGet] method you may return:
1) a model-less view return View();
2) a view with a Blank model return View(new Product());
3) a view with a model which contains some data return View(product);
Inside the View, you should decide:
1) If you only want to display the model you could use (may not be wrapped in a form):
HtmlHelpers like #Html.DisplayFor(x => x.Name)
Simple Model calls like <h1>#Model.Name</h1>
2) If you want some data to be posted back to your [HttpPost] you should
Take care to "Map" a Model's property to an HTML element specifically (everything wrapped inside a form ):
Through HtmlHelpers like #Html.TextBoxFor(x => x.Price), which will generate a "suitable" HTML output:<input id="Price" name="Price" value="">52.00</input> (recommended)
Through well formatted HTML (suitable ^_^ ):
<select id="SelectedLanguageId" name="SelectedLanguageId">
<option value="1">English</option>
<option value="2">Russian</option>
</select>
To summarize:
1)If you want to receive something back to a [HttpPost] method you should have a suitable HTML element inside your .
2)If you want only to display some data you could simply call model's property
3)Use HTML helpers instead of raw HTML code.
Note!
Model binding on complex Models is achieved through EditorTemplates, Hidden inputs inside Loops, Different kind of Serialization etc.
Still Learning MVC3, EF. For now I am connecting to MySql but I believe that will not be relevant. For simplicity, I decided to use one database for my test application and I have included a category to differentiate the data. For eg I have a news, events,info and pages categories. Now when it comes to listing contents in views for example at the homepage, I want to list latest 5 news items(news category), latest 5 events(events category), welcome text(info category). i have been able to create partialViews to list these at the different sections of the homepage. But I feel am doing this wrongly since in each of these PartialViews I am querying the same table over and over and just filtering with where cat=....in the LINQ query.
Can you please confirm if that should be the practice or there is a better way to do this.
You could do the following:
Controller:
public ActionResult Home()
{
IEnumerable<MyDateRecords> myData = LinqQueryToGetAllTheDataUnFiltered();
ViewData.Model = new MyViewData { MyData = myData; }
return View();
}
ViewModel class:
public class MyViewData
{
List<MyDataRecords> MyData { get; set; }
List<MyDataRecords> News { get { return MyData.Where(m => m.Category = "News"); } }
List<MyDataRecords> Events { get { return MyData.Where(m => m.Category = "Events"); } }
}
View:
#model MyViewModel
#Html.Partial("NewsPartial", Model.News)
#Html.Partial("EventsPartial", Model.Events)
Partial:
#model IEnumerable<MyDataRecord>
This way we only queried for the data once and just passed a different set to each partial
For an uncomplicated way of presenting this type of data, what you are doing is fine. You should look at the OutputCacheAttribute for any PartialView Method you use on your Controller.
This is pretty inefficient. But it's good that you noticed this because querying that database is often the bottleneck in any given request.
For starters you should retrieve that data into a dictionary or model and then pass it to the partial views to render similar to what Bassam outlined. Ideally, this should be taken care of in the Controller to stick to the MVC paradigm and then passed to the main view, which would then pass the appropriate data to the partial views.
Once you get more advanced/familiar with ASP.NET MVC, you can start looking into caching. I'd stay away from caching for right now since it get somewhat tricky if you have data that is rapidly changing since you need to start worrying about updating/synchronizing/etc.
I have a List View which has a strongly typed ViewModel which includes the entity list I am working with together with some other session-type stuff I am carrying aruound.
On clicking an item in the list (an Html.ActionLink) to go to the Details view I can easily pass the entity id. But I also want to pass the rest of the ViewModel from the View.
I can build the ActionLink with various QueryString parameters and then a custom ModelBinder can pick them up and hydrate the ViewModel object for me again. However, I donĀ“t like this.
I can get the custom ViewModel to rehydrate when it is POSTed back to the same page etc., but how can I get the ViewModel into a Controller Action using a GET to another View without using a ModelBinder and by simply placing the ViewModel object as a parameter in the target Action method?
I don't think you can do what you want, which from what I gather is the following:
While rendering the List action, you want to create a link to another action (potentially on another controller, but that's not key here)
This action should, when fired, have access to the original ViewModel that existed when the ActionLink method was first executed.
Unfortunately, items #1 and #2 are completely disconnected from each other and so there is no real mechanism to pass the current ViewModel to a link that will be executed in a different session.
That's not to say there aren't workarounds, of course:
You can generate the action link like so:
<%=
Html.ActionLink(
"Label",
"Action",
"Controller",
new {Parameter1 = Model.Data1, Parameter2 = Model.Data2},
null
)
%>
Within your linked action method, you can instantiate the ViewModel using the parameters passed to that action method.
I just tried this, and it seemed to work. Also tried the without a form and it worked also. Not sure if this is exactly what you wanted though.
Action
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index(TestModel model)
{
ViewData["Message"] = model.Test1;
return View();
}
Model
public class TestModel
{
public string Test1 { get; set; }
public string Test2 { get; set; }
}
View
<% using (Html.BeginForm("Index","Home",FormMethod.Get))
{ %>
<%=Html.TextBox("Test1")%>
<%=Html.TextBox("Test2")%>
<input type=submit value=submit />
<% }%>