I have two projects, d2admin and PartyWeb.
d2admin is the actual UI, it will have all necessary css, js and views etc., and also controllers if required.
PartyWeb is having controllers for each table in Party.
Say I have a table called - Organization. This table's controller will be in PartyWe/Controllers folder.
I will have the views in d2admin.
Now my problem is how can I invoke the OrganizationController exists in PartyWeb from the view Organization.cshtml exists in d2admin?
I tried with Html.RenderAction, this is working for the controllers exists in same, when I call the controller of diff project I am getting - missing method exception.
I found your problem interesting and decided to test for myself. I created two MVC projects (but one of them could be a class library as well, I was lazy though). The first MVC project became the main one with routes and views, the second project got the model and the controller. It worked like a charm from start and here is how I did it.
I created the model in the second project, named Car in my example (the name UsersContext is left from the default files because I wanted to change as little as possible).
namespace PartyBiz.Models
{
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<Car> Cars { get; set; }
}
[Table("Cars")]
public class Car
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CarId { get; set; }
public string CarName { get; set; }
}
}
I then built the project and created a controller with EF connections to Car (by right clicking on the Controller folder and select MVC controller with read/write actions and views, using Entity Framework)
The controller looked like this when done (many lines have been removed to keep the example short)
namespace PartyBiz.Controllers
{
public class CarController : Controller
{
// UsersContext is a left over from the default MVC project
private UsersContext db = new UsersContext();
public ActionResult Index()
{
return View(db.Cars.ToList());
}
// Many other actions follows here...
}
}
The views that were created in the second project (PartyBiz) I copied over to the first project (d2admin) by drag and drop. I then deleted the views from the second project to make sure they weren't used there.
I also had to add a reference from the first project (with the views) to the second project (model and controller). After that it worked just fine to run the first project.
I continued to enable migrations in the model-controller-project and got a database connection without any problems. I could see that the controller managed to save data even though it was located in a different project.
I hope this can help you on the way...
EDIT:
Using the following code in the views from the first project (d2admin) worked fine even though the Car controller referred to exists in the second project. This link was used in the home (controller) / index (view) in the first project.
#Html.ActionLink("Go to the cars", "Index", "Car")
EDIT2:
This is the index view for the Car controller. The view is in d2admin and is referencing a controller in the PartyBiz project.
#model IEnumerable<PartyBiz.Models.Car>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.CarName)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CarName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CarId }) |
#Html.ActionLink("Details", "Details", new { id=item.CarId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CarId })
</td>
</tr>
}
</table>
I acknowledge this is an old question with an already accepted answer; however, I ran into the same problem and was able to solve it and would like to share my experience.
From what I understand the following things are true:
d2admin is the code that handles the front end of the web site, and the controllers are used to drive the views and/or view models.
PartyWeb is used as an API at a domain level to interact with some datasource.
OrganizationController is the controller you're using to get data from the datasource to the d2admin project (and vice-versa)
With all of that in mind, arise the power of partial views!
Let's use the very simple View that would be located in d2admin/Views/SomeController.cshtml where SomeController is the folder that reflects the controller associated with these views.
<h3>A Very Basic View</h3>
#Html.Partial("_SomePartialView", OrganizationController.GetOrganizations())
Notice that this view has no model, and calls a partial and it's model is populated right there... and that's it! Now how would we write _SomePartialView.cshtml?
We will put it in the d2admin/Views/Shared folder, so the full path would be: d2admin/Views/Shared/_SomePartialView.cshtml. The file will look like
#model IEnumerable<PartyWeb.Models.Organization>
<div>
#foreach(var o in Model){
#Html.DisplayFor(modelItem => item.Id)
#Html.DisplayFor(modelItem => item.Name)
<br/>
}
</div>
As we can see this view will display some basic info assuming the following is our model found at PartyWeb/Models/Organization.cs
public class Organization
{
public int Id {get; set;}
public string Name {get; set;}
// some additional properties
}
and for the final bit of magic...
Within OrganizationController.cs we need to add the static action that will enable us to get the data to bind to our partial view's model. So we would add the following:
public class OrganizationController : ApiController
{
// Some Other Actions
[HttpGet]
public static List<Organization> GetOrganizations()
{
var dataSource = GetDataSource(); // Some Method that exposes the datasource
return ReadAllOrganizations(dataSource); // Some method that allows us to read all of the organiztions from the dataSource, i.e. some sql that executes against a database.
}
}
Related
I have a Model having more than 50 fields and I want to display list of those fields but not all of them.
So I have created two viewbag having list of display field and all values.
Now I have,
<table>
<tr>
#foreach (var v in #ViewBag.columnList)
{
<th>#v</th>
}
</tr>
#foreach (var u in #ViewBag.userlist)
{
<tr>
#foreach (var c in #ViewBag.columnList)
{
<td>#u.c</td>
}
</tr>
}
I want to assign #u.#c to select column to display in table.
public abstract class UserBaseModel
{
public string Name {get;set;}
public string City {get;set;}
}
public class UserModel : UserBaseModel
{
public string State {get;set;}
}
public class AdminModel : UserBaseModel
{
public string Gender {get;set;}
}
These classes show the different user types having different Properties. You could then have a view stongly typed with the base class
#model UserBaseModel
This way you can pass different models to the view depending on the user. You then have a choice how to access those Properties. It might allow you to select out the Property even though there won't be intelisense, I haven't tried.
You could also have partial views for user type, then depending on type of user in questions, load the relevant partial. Point being, you can pass either User to Admin to the view in the example and both will work.
Update Binding hidden fields of a viewmodel.
Let me try to explain my situation. I may be completely wrong but this is what I believe causing issue to me.
I have a ViewModel
Project Create View Model
[Bind(Exclude="List")]
public class ProjectCreateViewModel : ProjectViewModelBase
{
public CourseViewModelBase CourseVM { get; set; }
public ProjectCreateViewModel()
: base()
{
this.CourseVM = new CourseViewModelBase();
}
}
Project View Model Base is the base viewModel for a project and all associated actions derive from this so that I don't need to write property names again and again.
Create View Model Base is similar to ProjectViewModelBase(handled or used by ProjectController) but for a course (handled by CourseController).
Now I've created a form for "Create New Project" which uses ProjectCreateViewModel. In Form post action however CourseVM is always null.
Create New Project .cshtml
#model LMSPriorTool.ViewModels.ProjectCreateViewModel
#* --- labels and other stuff -- *#
#using (Html.BeginForm("CreateNewProject", "Project",
FormMethod.Post, new { #class = "form-horizontal",
name = "createNewProjectForm" }))
{
<!-- Hidden Fields -->
#Html.HiddenFor( x => x.ProjectId)
#Html.HiddenFor( x => x.CourseVM) // CourseVM is null in post action
#Html.TextBoxFor(x => x.CourseVM.CourseNumberRoot) // This is displayed properly
}
ProjectController
[HttpGet]
public ActionResult CreateNewProject(CourseViewModelBase courseVM = null)
{
ProjectCreateViewModel projectCreateViewModel = new ProjectCreateViewModel
{
CourseVM = courseVM,
};
// OTHER STUFF
return View("CreateNewProject", projectCreateViewModel);
}
Error
In HTTPPOST action I'm getting CourseVM as null, though I have provided it as a hidden field in form.
Possible Issue I belive issue is with the Constructor of ProjectCreateViewModel as when HTTPPOST action occur, view will try to create new instance of ProjectCreateViewModel and instantiate the CourseVM as null. Then same instance is passed to the HTTPPOST method in which CourseVM is appearing as null.
UPDATE: ISSUE ROOT CAUSE Complex objects cannot be bind to a viewmodel using Hidden Fields.
Any suggestions or thoughts appreciated.
You don't need that HiddenFor of CourseVM. MVC will create the class automatically for you because you are binding CourseVM.CourseNumberRoot
At the moment, you are attempting to bind CourseVM, which is a complex object, from a hidden input which MVC can't do, so it is returning null.
I'm building an Orchard CMS module, where I want to eager load data, but can't work out how to do this.
For example a Client has many Events, so I have a ClientRecord & EventRecord for these:
public class ClientRecord {
private IList<EventRecord> _eventRecords;
public virtual int Id { get; set; }
public virtual string Company { get; set; }
public virtual IList<EventRecord> EventRecords {
get { return _eventRecords ?? (_eventRecords = new List<EventRecord>()); }
set { _eventRecords = value; }
}
}
WhenI load a Client in my ClientController
var clientRecord = _clientRepository.Get(id);
and then display the Events in my View
<ul>
#foreach (var eventRecord in Model.EventRecords) {
<li>
#eventRecord.Name (#eventRecord.StartDate.ToShortDateString())
</li>
}
</ul>
the Events are displayed and MiniProfiler shows a separate query to lazy-load the Events.
I've tried putting an [Aggregate] attribute on the EventRecords collection in the ClientRecord, but this didn't have any effect.
I'm not that familiar with NHibernate, so hopefully this is something simple, but how do I specify I want to eager load the EventRecords when the ClientRecord is retrieved?
[EDIT]
In Orchard CMS the NHibernate mappings are created for you, based on the convention that the class is called xxxRecord and there is a database table in place with the same name.
So you don't (as far as I know) have a mapping file that you can specify this in. If I'm right about that, then the question is whether there is any way to specify you want eager loading in the query you use to retrieve the Client (rather like Entity Framework's "include" method).
You must specify eager loading in your nHibernate mapping file, using Fluent nhibernate this would look (something) like this:
HasMany(x => x.EventRecords).KeyColumn("CompanyId").Not.LazyLoad().Fetch.Select();
The Fetch.Select() will execute a second select statement to load all of the related Event records.
The Not.LazyLoad() tells nHibernate to execute this second select immediately, if you remove this execution would be deferred until the collection is accessed.
In response to your comments you can specify eager loading in your query using fetch also (LINQ example shown)
NHSession.Query<ClientRecord>().Fetch(c => c.EventRecords).ToList();
Can we have a single razor view for both Create and Edit operations?
If yes, how do we achieve this?
I don't recommend it.
This should be a rather long answer, because there's a lot of things involved in the process, request and workflow of a normal MVC GET/POST workflow. I will try to answer your question with the minimum information required and why I do not recommend the use of the same view.
First, why?
You don't have control over the views, which may have over-posting;
No flexibility;
Not reusable views or parts;
Hard to maintain the views (one change in the view must be tested on both actions).
My suggested approach would be to have different actions/views but share common code:
Create both views as normal.
You will have duplicated code, but not all code is the same, for example, you may not want to send an ID on the create action, this is not directly related to your question, but using the same view implies you are also sending the same data, and this is not recommended, especially for over-posting or mass assignment. More info about mass assignment here (an Architectural Approach is what I'm using here).
So let's start from what are you going to receive in your controllers.
In this case I used inheritance but it's not the only strategy.
Binding models
public class UpdateBindingModel : CreateBindingModel {
// since we are not using the same binding model,
// we can have a "real" validation rules on our update binding and view.
[Required]
public int? Id {get;set;}
}
public class CreateBindingModel {
// no id here prevent overposting.
[Required]
public string Name {get;set;}
[Required]
public int? CountryId {get;set;}
}
That will make sure the data you send to your Create and Edit is the minimum needed and nothing else.
Let's then see the View Models that will be sent to the View, for this example I will include a List that will be used to select some value but should not be posted (the list) to the controller, only the selected value.
View models
public class CreateViewModel : CreateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
public class UpdateViewModel : UpdateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
As you can see, this gives you lot of flexibility but still have some duplicated code (the extra information needed on view model for both views) which can be mitigated in several ways (depending the needs/context):
Have an action to retrieve the common data and using #Html.Action("GetCountryList");
Use the same View Model aka CreateUpdateViewModel and discarding extra UpdateBindingModel properties in the view but still posting the corresponding model on POST.
Having your binding models as properties and select one or the other in the specific view. (better use #Html.EditorFor instead of partials so Model Binder will work with no additional change on code)
The controller actions will look like:
Controller
[HttpGet]
public ActionResult Create(){
ViewData.Model = new CreateViewModel();
return View();
}
[HttpPost]
public RedirectToRouteResult Create(CreateBindingModel binding) {
// check valid model state and create data
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult Update(int id) {
var objectToEdit = service.GetObjectToEdit(id);
ViewData.Model = new UpdateViewModel(objectToEdit);
return View();
}
[HttpPost]
public RedirectToRouteResult Update(UpdateBindingModel binding) {
// check valid model state and update data
return RedirectToAction("Index");
}
And your views:
Views
Update.cshtml
<form action="Update">
#Html.HiddenFor(Model.Id);
#Html.Partial("EditFieldsPartial")
<button>delete</button> // no delete button on create.
<button>create new</button> // you can have a create new instead of update.
</form>
Create.cshtml
<form action="Create">
#Html.Partial("EditFieldsPartial")
</form>
Note: code is incomplete and didn't use helpers in most cases for brevity and clarity. Do NOT copy paste :D
Sure you can.
On post, check in your controller whether the primary key has value 0 then Insert, otherwise Update.
View should be the same for Create and Edit.
Just remember to include:
#Html.HiddenFor(model=>model.ID)
In your view
For example:
Model:
public class DescriptionModel
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
}
CreateEdit.cshtml:
#model DescriptionModel
#using (Html.BeginForm("CreateEdit"))
{
#Html.HiddenFor(model=> model.ID)
#Html.EditorFor(model=> model.Description)
<input type="submit" value='Submit' />
}
DescriptionModel controller:
public ActionResult Create()
{
return View("CreateEdit", new DescriptionModel());
}
public ActionResult Edit(int id)
{
return View("CreateEdit", db.DescriptionModels.Find(id));
}
// Submit and add or update database
[HttpPost]
public ActionResult CreateEdit(DescriptionModel model)
{
if (ModelState.IsValid)
{
// No id so we add it to database
if (model.ID <= 0)
{
db.DescriptionModels.Add(model);
}
// Has Id, therefore it's in database so we update
else
{
db.Entry(model).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
A View can definitely be shared for create and edit operations, using the same model. However, i would strongly recommend to think about it twice. In many cases, you will want to have a different view for edit operations(eg. hide some inputs that should not be editible) as well as the model could be slightly different, altought it might share some (or most) values. These difference will lead to some conditions in the view, checking whether you are creating or editing - which could make the code chaotic.
Conclusion: before deciding whether to have a shared view, try to think of how much is the edit screen gonna differ from create screen, then you may decide.
You certainly can, but usually that's something I will try to avoid. If the create and edit actions are virtually the same then you end up duplicating a lot of code in the controller. Usually in this situation I will have only a few fields on my 'Add' controller, and then once the item has been added I redirect the user to the edit page where they can fill in the rest of the information.
I wouldn't recommend that approach but you could have the main form be loaded into both views from a partial
[HttpGet]
public ActionResult myFun(int id = 0)
{
MyClass cls = new MyClass();
if (id == 0)
{
//Insert mode ... no data will be shown to textboxes , when primary key ie. id=0
//Display whole data
}
else
{
//Update mode... if id is not 0 ,data will be shown to textboxes
}
return View(cls);
}
Using MVC-3, Razor:
-- MyController --
public ActionResult Index(String message) // where message = "hello"
{
ViewModel.Test1 = "This is a test";
ViewModel.Test2 = "This is another test. " + message;
}
-- Index.cshtml --
#Html.Label((string)View.Test1)
<br />
#Html.Label((string)View.Test2)
Why will it only render out the following?
<label for="This is a test">This is a test</label>
<br />
It's been driving me absolutely crazy over the past few days and seems to make no sense. There has to be a reason for it.
I can debug this and step through thew view. In the view, I watch as this line is processed and the value of View.Test2 is "This is another test. hello".
I have cases where I am doing the following and it works fine.
(ex)
ViewModel.Something = this.readDataService.GetSomething();
What's the difference?
Thanks,
Rob
Looks like you are using a pre-RC2 version of ASP.NET MVC 3. ViewModel was changed to ViewBag in RC 2 (see the this post by Scott Guthrie).
With earlier previews of ASP.NET MVC 3 we exposed this API using a dynamic property called “ViewModel” on the Controller base class, and with a dynamic property called “View” within view templates. A lot of people found the fact that there were two different names confusing, and several also said that using the name ViewModel was confusing in this context – since often you create strongly-typed ViewModel classes in ASP.NET MVC, and they do not use this API.
With RC2 we are exposing a dynamic property that has the same name – ViewBag – within both Controllers and Views.
And it does look like you are trying to use ViewModel as the strongly typed model for your view. Instead, create a class to use as your model and then use #Html.LabelFor:
public class PersonModel
{
public string Name { get; set; }
}
in the controller:
PersonModel model = new PersonModel { Name = "John" };
return View(model);
in the view:
#Html.LabelFor(model => model.Name): #Html.TextBoxFor(model => model.Name)
which renders:
<label for="Name">Name</label>: <input id="Name" name="Name" type="text" value="John" />
HTH