Controller as ascx factory - bad idea? - asp.net-mvc-2

I'm trying to create something like *.ascxs' factory.
Scenario:
I would like to render controls which depends on model, which i've passed to partialView.
I'd like to achieve something like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyAbstractModel>" %>
<%= Model.Property1 %>
<!-- other more sophisticated displays on model -->
<% Html.RenderAction("RenderControl", "Factory", new { model = Model}); %>
FactoryController:
public ActionResult RenderControl(object model) {
if (model.GetType() == typeof(Model1) {
return RenderPartial("Partial2", model);
} else {
return RenderPartial("Partial1", model);
}
}
I'd like to know is there any better way to cope with such situation. I suppose It's not the most efficient method to build web page in ASP.MVC 2.
If this method is acceptable, how can i restrict access to such controller? I would like to use this class only on server side and only by ascxs' pages

Use the ChildActionOnly() attribute to restrict access to your actions.
What you are trying to do is already builtin to MVC: Html.DisplayFor()
See: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

Related

Best way to access ViewData value from jQuery method in ASP.NET MVC2

I want to access a public property called UserType from within jQuery method which is filled with the data from database call in a controller method.
The same property value needs to be set from multiple controller methods. I filled the data in ViewData and tried to access it in jquery method as mentioned below:
TestController.cs:
[Authorize(Roles = "Root")]
public ActionResult Index()
{
var user = _dbService.GetUser(_profile.UserName);
ViewData["UserType"] = user.UserType;
}
index.aspx:
<% var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); %>
<script type="text/javascript">
$(function () {
var userType = <%= serializer.Serialize(ViewData["UserType"]) %>;
alert(userType);
});
</script>
I am able to access the UserType value successfully, but I need to use the below mentioned code in all the controller action methods.
ViewData["UserType"] = user.UserType;
which is not a good design practice.
Can anyone help me know any other best alternative to manage the above mentioned change with some sample code?

Html.RenderPartial & Multiple controls with same id error

I have made myself a small user control for consuming a feed, the code for said user control looks like this
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SyndicationFeed >" %>
<%foreach (var rss in ViewData.Model.Items)
{
Response.Write("<div id={0}><a href={1} target=\"_blank\" /> <strong>{2}</strong></div>",
rss.Links[0].Uri.OriginalString, rss.Title.Text, rss.Title.Text);
Response.Write("<div>" + rss.Summary.Text.Truncate(100) + "</div>")
}%>
The code for the RssController looks like this
public virtual ActionResult Index()
{
string feedUrl = #"http://wdfw.wa.gov/news/newsrss.php";
using (XmlReader reader = XmlReader.Create(feedUrl))
{
SyndicationFeed rss = SyndicationFeed.Load(reader);
return View(rss);
}
}
And I call it in Site.Master like so
<%Html.RenderPartial("Index", Model);%>
I've also tried
<%Html.RenderPartial("Index", ViewData.Model);%>
All resulting in this this error:
Multiple controls with the same ID
'ctl00' were found. Trace requires
that controls have unique IDs.
Can someone help me figure out where I'm going wrong please :)
Can you try to use Html.RenderAction("Index","Rss") instead Html.RederPartial? When Html.RenderAction is called by ViewEngine, all code in your Index action is executed and Model with rss data goes to Index view to be generated to markup. And why do you use Response.Write, in view you can put html tags and this markup will be inserted where you are calling Html.RenderAction. Hope it will be helpful for you, if not, sorry, at least I've tried ))
Best regards,
Dima.

Behavior while passing objects inside objects in asp.net mvc?

I am sure everyone has come across this but I thought will ask this anyways. So here is what I have -
public class ABC
{
public int x;
public int y;
public XYZ obj;
}
public class XYZ
{
int x1;
int y1;
}
public ActionResult Test1()
{
ABC model= new ABC();
model.x=1;
model.y=2;
ABC.obj= new XYZ();
model.x1=12;
obj.y2=34;
return View(model);
}
[HttpPost]
public ActionResult Test1(ABC model)
{
//does not get XYZ obj
}
View-
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Models.ABC>" %>
<% using (Html.BeginForm())
{%>
//stuff here
<%:Html.HiddenFor(model => model.obj)%>
<%}%>
If I do the hidden fields for explicitly for XYZ's fields x1 and y1 then I get back those values. Like this -
<%:Html.Hidden("Model.obj.x1",Model.obj.x1)%>
I guess this is expected behavior but am I missing anything here ?
Well, for one thing, your "inherits" attribute is wrong. Instead of
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="Models.ABC" %>
It should be
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Models.ABC>" %>
If you want to use Models.ABC as your model. For another, the action methods you posted aren't even compilable, so it's difficult to tell what the real problem might be.
Sending composite objects like this works just fine for me, so there is most likely an issue with your implementation.
Update
Values for any persisted model properties have to be POSTed back from the editor page, which means they need to be stored in form fields. If the page generator isn't creating fields for those values (and I'm not sure it should - it would make more sense to me to include a partial view for nested objects), you'll need to add fields that are either editable or hidden.

Creating a generic edit view with ASP.NET, MVC, and Entity Framework

In my database, I have 40 tables that contain only an ID number and a name. My database is accessed using Entity Framework. While I have no trouble editing them each by generating a strongly-typed view and postback methods for each object, I would like to create a more generic method and view for viewing and editing these objects.
I am currently using the following code to access each object. In this case, it is for an object of 'AddressType':
public ActionMethod EditAddressType(int ID)
{
var result = database.AddressType.Single(a => a.ID == ID);
View(result);
}
[HttpPost]
public ActionMethod EditAddressType(int ID, FormCollection formValues)
{
var result = database.AddressType.Single(a => a.ID == ID);
UpdateModel(result);
database.SaveChanges();
return View("SaveSuccess");
}
The view 'EditAddressType' is strongly typed and works fine, but there's a lot of repeated code (one instance of this for each object). I've been told that I need to use reflection, but I'm at a loss for how to implement this. My understanding is that I need to retrieve the object type so I can replace the hardcoded reference to the object, but I'm not sure how to get this information from the postback.
I've had success binding the information to ViewData in the controller and passing that to a ViewPage view that knows to look for this ViewData, but I don't know how to postback the changes to a controller.
Thanks for any help you can give me!
If you are going to edit the object you don't need to refetch it from the database in your POST action. The first thing would of course be to abstract my data access code from the controller:
public class AddressesController: Controller
{
private readonly IAddressesRepository _repository;
public AddressesController(IAddressesRepository repository)
{
_repository = repository;
}
public ActionMethod Edit(int id)
{
var result = _repository.GetAddress(id);
return View(result);
}
[HttpPut]
public ActionMethod Update(AddressViewModel address)
{
_repository.Save(address);
return View("SaveSuccess");
}
}
You will notice that I have renamed some of the actions and accept verbs to make this controller a bit more RESTFul.
The associated view might look like this:
<% using (Html.BeginForm<AddressesController>(c => c.Update(null))) { %>
<%: Html.HttpMethodOverride(HttpVerbs.Put) %>
<%: Html.HiddenFor(model => model.Id) %>
<%: Html.TextBoxFor(model => model.Name) %>
<input type="submit" value="Save" />
<% } %>
As far as the implementation of this IAddressesRepository interface is concerned, that's totally up to you: Entity Framework, NHibernate, XML File, Remote Web Service call, ..., that's an implementation detail that has nothing to do with ASP.NET MVC.

Where and how to load dropdownlists used in masterpage

I'm new to MVC!
I am trying to use two DropDownLists (Cities, Categories) in a PartialView that will be used in MasterPage, meaning they will be visble all the time.
I tried to load them in HomeCOntroller, but that didn't work. I got an Exception.
I read something about making a baseController that the other controllers will inherit from, I have tried that, kind of, but I guess i'm doing something wrong.
This is the only code I got today:
Masterpage
<% Html.RenderPartial("SearchForm"); %>
PartialView (SearchForm.ascx)
<% using (Html.BeginForm("Search", "Search")) { %>
<% } %> // dont know why I need two BeginForms, if I dont have this the other form won't trigger at all! Weird!
<% using (Html.BeginForm("Search", "Search", FormMethod.Get)) { %>
<%= Html.DropDownList("SearchForm.Category", new SelectList(ViewData["Categories"] as IEnumerable, "ID", "Name", "--All categories--")) %>
<%= Html.DropDownList("Search.City", Model.Cities, "--All cities--") %>
<input name="search" type="text" size="16" id="search" />
<input type="submit" id="test" title="Search" />
<% } %>
Two question:
Where and how to load the DropDownLists is the problem. I have tried to load it in the HomeController, but when go to another page then it says that the DDLs is empty and I get a Excecption.
Why do I have to use two forms for the ActionMethod to trigger ?
Hope anyone can help me out!
It sounds like you're only setting the property for a single action result. The Model.Cities data will have to be populated for every single view that needs to use it.
One solution would be to move the population of it to an ActionFilter
public class CityListAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var result = filterContext.Result as ViewResult;
result.ViewData.Model = //populate model
base.OnActionExecuted(filterContext);
}
}
and then add the filter to your controller
[CityList]
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
}
As for the two forms issue, there should be no reason that i can think of that you need an empty form.
Take a look at the html that's being output and make sure it's ok. Also check the action is being generated correcly
Better way to do this, is to create something like MasterController and have action method on it like this:
[ChildActionOnly]
public ActionResult SearchForm()
{
//Get city data, category data etc., create SearchFormModel
return PartialView(model);
}
I recommend you create strongly typed view (SearchForms.ascx of type ViewUserControl<SearchFormModel>). Also it may be a good idea to have a model like this:
public class SearchViewModel
{
public IList<SelectListItem> Cities { get; set; }
public IList<SelectListItem> Categories { get; set; }
}
and use a helper like this: http://github.com/Necroskillz/NecroNetToolkit/blob/master/Source/NecroNet.Toolkit/Mvc/SelectHelper.cs to convert raw data to DDL friendly format beforehand.
In any case, you now use Html.RenderAction() instead of Html.RenderPartial() and specify you want "SearchForm" action from "MasterController".