I am confused with this:
I have an action ,say Parent ,and in the corresponding view file ,I have called a child action ,say Child ,both Parent and Child actions are in the same controller.
and I need the Child action and the Parent action to share some data in the ViewBag.Now ,what I should do ?Here is my question:
when I call the Child action in parent's view file ,I pass the viewbag to it like this:
#Html.Action(ViewBag).
in my child action ,I do this:
public PartialViewResult Child(Object ViewBag)
{
//using the data in ViewBag
}
Is this the right way ? Does the viewbag object passed by reference or it is a different object then the original viewbag(more memory needed)?
Or if the Child action is sharing the viewbag with its calling parent Action by default?
From Darin Dimitrov's answer ,I knew that I can't do something like this:#Html.Action(ViewBag)
But I really need to pass the child action muti-parameters,what can I do ?
Child actions follow a different controller/model/view lifecycle than parent actions. As a result they do not share ViewData/ViewBag. If you want to pass parameters to a child action from the parent you could do this:
#Html.Action("Child", new { message = ViewBag.Message })
and in the child action:
public ActionResult Child(string message)
{
...
}
There is a way, but you have to create a custom abstract class as the base class for your razor views. Then expose whatever you need to from parent to child actions.
This is how I get the root controller's ViewBag inside a class inheriting from WebViewPage
private dynamic GetPageViewBag()
{
if (Html == null || Html.ViewContext == null) //this means that the page is root or parial view
{
return ViewBag;
}
ControllerBase controller = Html.ViewContext.Controller;
while (controller.ControllerContext.IsChildAction) //traverse hierachy to get root controller
{
controller = controller.ControllerContext.ParentActionViewContext.Controller;
}
return controller.ViewBag;
}
Related
I have two actions in my controller which are sharing part of logic responsible for selecting the view. How Can I make this part common accross actions. Example:
Controller Document
Action Open
if there is one document found and is type X, display it using OpenX View
if there is one document found and is type Y, display it using OpenY View
if there are more than documents found, display list using List View
if there are no documents found, display error using Error View
Action OpenMetaData
if there is one document found, display it using OpenMetaData View
if there are more than documents found, display list using List View
if there are no documents found, display error using Error View
As you can see, points 3,4 are the same as 2,3
I would like to create something like
public DocumentController
{
public ActionResult Open( ... )
{
var dataFromWebService = service.GetData( ... );
return ViewSelector.GetLaunchView(dataFromWebService);
}
public ActionResult Open( ... )
{
var dataFromWebService = service.GetData( ... );
return ViewSelector.GetOpenMetaData(dataFromWebService);
}
}
public class ViewSelector
{
public static ActionResult GetLaunchView(DataFromWebService dataFromWebService)
{
if( dataFromWebService contains document type X)
return new ViewResult("OpenX",data);
if( dataFromWebService contains document type Y)
return new ViewResult("OpenY",data);
return CommonLogic(dataFromWebService);
}
public static ActionResult GetOpenMetaData(DataFromWebService dataFromWebService)
{
......
}
private static ActionResult CommonLogic(DataFromWebService dataFromWebService)
{
.... Common logic
}
}
I would like to do this to make my Controller as clean as possible.
Can I create ViewResults outside controller, attach data to them are return them in the action ?
Is this good or bad design ?
Maybe someone have better idea how to handle this
If you don't need to access any of the contexts of the controller you can create the results outside the controller. In your case I would consider making the GetOpenMetaData() and GetLaunchView methods private methods of the controller.
If you need to share it accross multiple contollers you could also consider putting it into an abstract BaseController and let your controllers inherit from it.
I am a complete novice at MVC, and can't seem to get my head around a very basic concept.
I have a parent object, that contains a collection of child objects. I want to create a new child object, and link it to the parent object, persisted in the database via EF4
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
So far, what happens in my very basic application is this. My user goes to a page displaying the details of a parent object, which includes a list of the current children. There is also a link on that page to add a new child. That link points to a Create action on the child Controller, passing the parent Id, which in turn displays a view to create a new child. And this is where I've got stuck. I don't know how to persist the supplied parent Id so that when I click Save, I can retrieve that parent object and add my new child object to its collection.
I'm probably approaching this in completely the wrong way, but I can't seem to find any basic tutorials on how to add new child items to a parent, which is odd considering how common a scenario it is.
Any help is really appreciated!
Many thanks
Gerry
[Update 1]
I have two CreateChild actions, initially generated by MVC and then modified by myself. I can see just by looking at them that they don't do what I need, but I'm not at all sure how they need to change. Specifically, I store the parent ID within the ViewBag but between the calls to the Create actions, it gets lost, and so is not available when the second Create is called to persist the new child item to the database.
public ActionResult Create(int parentId)
{
Parent parent = db.Parents.Find(parentId);
ViewBag.ParentId = parent.Id;
return View();
}
[HttpPost]
public ActionResult Create(Child child)
{
if (ModelState.IsValid)
{
Parent parent = db.Parents.Find(ViewBag.ParentId);
parent.Children.Add(child);
db.Children.Add(child);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(child);
}
Thanks again
Gerry
When you pass the ParentId to the add child action, it looks like you are doing it with a route parameter.
Instead of storing it in the ViewBag, write it as a hidden field in your child form. Then, when someone submits the form, the ParentId will be submitted to your HttpPost action method.
You can do this by making ParentId a property on your Child viewmodel.
public class Child
{
public int ParentId { get; set; }
}
public ActionResult Create(int parentId)
{
Parent parent = db.Parents.Find(parentId);
var model = new Child { ParentId = parent.Id };
return View(model);
}
In your view, render it like this:
#Html.HiddenFor(m => m.ParentId)
Then, during your HttpPost, Child will already contain ParentId -- the default model binder will get it from the hidden field on your form.
[HttpPost]
public ActionResult Create(Child child)
{
if (ModelState.IsValid)
{
Parent parent = db.Parents.Find(child.ParentId);
parent.Children.Add(child);
db.Children.Add(child); // don't think you need this line
db.SaveChanges();
return RedirectToAction("Index");
}
return View(child);
}
Update after posting answer
Looking at your HttpPost code, it may be incorrect to add the child to the db twice. If you are using EF 4.1 or 4.2, I am pretty sure this is incorrect, but I'm not sure about previous EF versions. Adding the child to the parent.Children collection should be enough -- I don't think you should also add it to the db.Children set.
ViewBag is not ViewState (ASP.NET MVC doesn't have any built-in equivalent to ASP.NET WebForms ViewState) - it will not keep ParentId between calls. It will just allow you passing ParentId to view (in your first action) so you can for example store it in hidden field.
I am new to MVC frame work. And i am making one page where we can see details of department by clicking on details link button.
While User click link button it fetch the all the records of the particular department in List Collection and redirect to Details View.Data has been fetched in List but while going to Details view it Generates following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[DocPageSys.Models.Interfaces.DepartmentInfo]', but this dictionary requires a model item of type 'DocPageSys.Models.Interfaces.DepartmentInfo`'.
I understood the error but confusion to solve it.And stuck with this problem...
Since your Details view is strongly typed to DepartmentInfo:
#model DocPageSys.Models.Interfaces.DepartmentInfo
you need to pass a single instance of it from the controller action instead of a list:
public ActionResult Details(int id)
{
DepartmentInfo depInfo = db.Departments.FirstOrDefault(x => x.Id == id);
return View(depInfo);
}
So make sure that when you are calling the return View() method from your controller action you are passing a single DepartmentInfo instance that you have fetched from your data store.
To make it run fine initially you could simply hardcode some value in it:
public ActionResult Details(int id)
{
var depInfo = new DepartmentInfo
{
Id = 1,
Name = "Sales",
Manager = "John Smith"
}
return View(depInfo);
}
Oh, and you will notice that I didn't use any ViewData/ViewBag. You don't need it. Due to their weakly typed nature it makes things look really ugly. I would recommend you to always use view models.
Passing a list instead of a single item
This error tells you, that you're passing a list to your view but should be passing a single entity object instance.
If you did fetch a single item but is in a list you can easily just do:
return View(result[0]);
or a more robust code:
if (result != null && result.Count == 1)
{
return View(result[0]);
}
return RedirectToAction("Error", "Home");
This error will typically occur when there is a mismatch between the data that the controller action passes to the view and the type of data the view is expecting.
In this instance it looks as if you're passing a list of DepartmentInfo items when your view is expecting a single item.
I have a view that is strongly typed. Inside this view i have jqueryui tabs, that when clicked call my Controller and return a partial view
("#tab0").load('#Url.Action("ProfileImage", "User")');
public ActionResult ProfileImage()
{
return PartialView("_ProfileImage");
}
What I'd like to do is pass the model from the "parent" view to the controller which can then bind it to the partial when it is returned:
("#tab0").load('#Url.Action("ProfileImage", "User", new {model=model})');
public ActionResult ProfileImage(UserViewModel model)
{
return PartialView("_ProfileImage", model);
}
Is this possible? how is this normally done? Where you have the model data in one view and you'd like to pass it to a asynchronously loaded partial view?
You could create a ToJson method on your viewmodel, which could be something like this:
public IHtmlString ToJson()
{
return MvcHtmlString.Create(Json.Encode(this));
}
It just serializes the viewmodel into a json. The IHtmlString returntype makes sure the output isn't encoded in your view.
The call to your controller would be something like this:
("#tab0").load('#Url.Action("ProfileImage", "User", new {model=model.ToJson()})');
The json modelbinder can recreate the viewmodel on the serverside. You probably run into some problems along the way, but nothing unsolvable I guess.
Is it possible to set "UpdateCheck" to "LastUpdatedOn" field of parent object while updating children?
I am very confused by this question. The ColumnAttribute UpdateCheck can only be set to one of the following : Never, WhenChanged, Always.
If you are trying to timestamp a parent object when a property in a child object is changed, you can use partial methods to capture the change event and run other statements there.
public partial class MyObject
{
partial void OnMyPropertyChanging(string value)
{
// fire set other linq against parent here
}
}