Display data from database(Entity framwork) into _Layout.cshtml - asp.net-mvc

I'm developping a web site on MVC3 asp.net and I use entities framwork for data base:
I want to display the logo from database on _Layout.cshtml, and I want to display the texte from database into My home page.
this is my model
public class Theme
{
[Required(ErrorMessage = "ID is required.")]
public string ThemeID { get; set; }
public string path { get; set; }
[AllowHtml]
[Required(ErrorMessage = "Text is required.")]
public string texte { get; set; }
}
I put in the _Layout.cshtml
#Html.Partial("~/Views/Shared/_Header.cshtml")
this is my ThemeController.cs
[ChildActionOnly]
public ActionResult Header(string id)
{
var model = db.Themes.ToList();
return View("~/Views/Shared/_Header.cshtml", model);
}
this is the _Header.cshtml
#model ICollection<DSClient.Models.Theme>
#{
<img src="#Href( #Model.ElementAt(#Model-1).path )" />
}
When I type the url of Theme/index
It's OK, BUT the problem is when I load an other page, I have this exception
Object reference not set to an instance of an object.
Please Ineed your help.

Html.Partial is used to include a partial view. Therefore, when you include _Header.cshtml and the Model is not a ICollection<DSClient.Models.Theme>, you're in trouble.
Since you made a method with a Childaction attribute, I assume you may want to use Html.Action instead of Html.Partial. This would execute the child action of the controller which outputs the _Header.cshtml with the appropriate data.
Remove the string argument in the Header action (it's not used) and in _Layout.cshtml, you may call it like this:
#Html.Action("Header", "Theme")

Related

How to persist an object in view model on post back [duplicate]

I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!

Code reuse in multiple mvc views

I have read this link: https://www.future-processing.pl/blog/view-code-reuse-techniques-in-asp-net-mvc/
I can not use any of those helper ways...
I have to show on multiple mvc sites this string:
1612-1
That is an inquiry number: 16 is the day of month, 12 the month of year and 1 is the database id. I am sure that will not be the final impl but for now we take it as given.
public class MyViewModel
{
public string City { get; set; }
public string PostalCode { get; set; }
public List<string> ActionItemDescriptions { get; set; }
public string InquiryNumber { get; set; }
}
Where would you create the InquiryNumber?
If I put it inside the razor view I cant reuse it.
Seems business logic to me , so it belongs in the business layer.
Then, from within your controller you:
call the business component which returns the inquiry number
store the number in your view model
pass the view model to the view.
One way you could get an inquiry number, without using a helper, is this:
In a controller, have the following action method:
public ActionResult GetInquiryNumber()
{
// TODO : The code to get the inquiry number.
return Content("1612-1");
}
You can then call that method in any view you like, using the following:
#{ Html.RenderAction("GetInquiryNumber", "Home"); }
Obviously you will need to come up with your own method, and controller, names.
This isn't the ideal way of passing data to a view (using a viewmodel is preferable), but the above approach is an option to you.

Basic Umbraco 6.1.1 SurfaceController Questions

I've searched all the available tutorials I can find, and I'm still having trouble with Umbraco Surface Controllers. I've created a bare-bones Surface Controller example which sorta works, but has some issues. Here's my code so far, questions to follow:
ContactformModel1.cs:
public class ContactFormModel1
{
public string Email { get; set; }
public string Name { get; set; }
public string HoneyPot { get; set; }
public string Title { get; set; }
public string Last { get; set; }
public string First { get; set; }
public string Addr { get; set; }
public string Phone { get; set; }
public string Time { get; set; }
public string Comment { get; set; }
}
ContactSurfaceController.cs:
public class ContactSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult Index()
{
return Content("this is some test content...");
}
[HttpGet]
[ActionName("ContactForm")]
public ActionResult ContactFormGet(ContactFormModel1 model)
{
return PartialView("~/Views/ContactSurface/Contact1.cshtml", model);
}
[HttpPost]
[ActionName("ContactForm")]
public ActionResult ContactFormPost(ContactFormModel1 model)
{
// Return the form, just append some exclamation points to the email address
model.Email += "!!!!";
return ContactFormGet(model);
}
public ActionResult SayOK(ContactFormModel1 model)
{
return Content("OK");
}
}
Contact.cshtml:
#model ContactFormModel1
#using (Html.BeginUmbracoForm<ContactSurfaceController>("ContactForm"))
{
#Html.EditorFor(x => Model)
<input type="submit" />
}
ContactMacroPartial.cshtml:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#Html.Action("ContactForm", "ContactSurface")
My Questions:
I'm pretty sure that return ContactFormGet(model) is wrong in the
ContactFormPost method, but everything else I've tried throws an error.
When I try return RedirectToCurrentUmbracoPage(), I get Cannot
find the Umbraco route definition in the route values, the request
must be made in the context of an Umbraco request.
When I try return CurrentUmbracoPage(), I get Can only use
UmbracoPageResult in the context of an Http POST when using a
SurfaceController form.
The routing appears to work correctly (when I put a breakpoint inside ContactFormPost, the debugger stops there). But when the form comes back, I get the exact values I submitted. I don't see the !!! appended to the email address. (Note, this bit of code is just for debugging, it's not meant to do anything useful).
How do I call the "SayOK" method in the controller? When I change the BeginUmbracoForm method to point to SayOK, I still get stuck in the ContactFormPost method.
I'm sure I'm missing something incredibly stupid, but I can't figure this out for the life of me.
I wanted to take a moment to say how I resolved this. After playing around some more, I realized that I didn't really state my problem clearly. Basically, all I'm trying to do is embed an MVC form inside a Partial View Macro, so that it could be used in the content of a page (not embedded in the template).
I could get this solution to work, but I really didn't like how much logic the author put inside the View file. So I adapted his solution this way:
Partial View Macro (cshtml) file:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#using Intrepiware.Models
#{
bool isPostback = !String.IsNullOrEmpty(Request.Form["submit-button"]);
if(isPostback)
{
#Html.Action("CreateComment", "ContactSurface", Request.Form)
}
else
{
#Html.Partial("~/Views/Partials/ContactForm.cshtml", new ContactFormModel())
}
}
Form Partial View (cshtml) file:
#using Intrepiware.Models
#using Intrepiware.Controllers
#model ContactFormModel
<p>
<span style="color: red;">#TempData["Errors"]</span>
</p>
<p>
#TempData["Success"]
</p>
<div id="cp_contact_form">
#using(Html.BeginUmbracoForm("CreateComment", "BlogPostSurface"))
{
#* Form code goes here *#
}
ContactSurfaceController.cs file:
public class ContactSurfaceController : Umbraco.Web.Mvc.SurfaceController
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ubCreateComment(ContactFormModel model)
{
if (processComment(model) == false)
return CurrentUmbracoPage();
else
return RedirectToCurrentUmbracoPage();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateComment(ContactFormModel model)
{
if(processComment(model) == true)
{
TempData["Success"] = "Thank you for your interest. We will be in contact with you shortly.";
ModelState.Clear();
}
return PartialView("~/Views/Partials/ContactForm.cshtml");
}
private bool processComment(ContactFormModel model)
{
// Handle the model validation and processing; return true if success
}
}
The controller is designed so that the form can be embedded either in the template or a Partial View Macro. If it's embedded in a template, the form should post to ubCreateComment; if it's in a macro, post to CreateComment.
I'm almost positive there's a better/more correct way of doing this, but I ran out of time to work on the project. If someone has a better solution, please post it!
One final question/note: You'll notice that the partial view macro posts Request.Form to the ContactSurfaceController.CreateComment, and MVC magically serializes it for me. That's safe, yeah? If so, doesn't MVC rock? :)
You are using a ChildAction because you are specifying #Html.Action("ContactForm", "ContactSurface") and because of this, in your View you need to:
Use Html.BeginForm(...) and not 'Html.BeginUmbracoForm(...)'
Allow the form to post back to the same path and not to the action
If you do this, then the form will post back to itself as expected.
See the documentation here for further help.
Edit:
Just saw the final part to your question. If you intend SayOK to be your 'thank you' message, I would just call it from your HttpPost action instead of returning the initial view.

Asp.net mvc how to use htmlhelper to generate complex type?

I have a complex type License as a view model.
public class License
{
public string Name { get; set; }
// Other Properties
public List<Function> Functions { get; set; }
}
public class Function
{
public string Name { get; set; }
// Other Properties
public List<Unit> Units { get; set; }
}
public class Unit
{
public string Name { get; set; }
// Other Properties
}
Both the Function's view template and Unit's view template are dynamiclly rendered. So the html looks like this:
<!-- LicenseView -->
#model License
#Html.TextBoxFor(m => m.Name) // this is OK
#for(int i=0; i<Model.Functions.Count; i++)
{
#Html.Partial(Model.Functions[i].Name, Model.Functions[i])
}
and the FunctionView may look like this
#model Function
#Html.TextBoxFor(m => m.Name) // the generated html element's name is just 'Name'
#for(int i=0; i < Model.Units.Count; i++)
{
#Html.Partial(Model.Units[i].Name, Model.Units[i])
}
and this is UnitView
#model Unit
#Html.TextBoxFor(m => m.Name) // the generated html element's name is just 'Name'
So my question is, what should I do the make the Name attribute correct?
Thanks a lot
The only change you need to make in the above code is to use Editor instead of partial view.
So basically all you code will look similar to the following
#model License
#Html.TextBoxFor(m => m.Name)
// Editor will take care of the repetition and u don't need to explicitly pass in the name
// Since the model already have the attribute
#Html.EditorFor(Model.Functions)
Then create your editor template folder, "EditorTemplates", under "Shared" folder and name your view file as "Function"
Do the same for Unit class and you will get what you want.
As #Jack said... you can do this using Editors instead of PartialViews.
BUT... if you really want to use PartialViews, you can do it, but the model to pass should be the top one (License). This way is similar of what David Jessee proposed, but splitting the one view in several.
Pardon me for guessing at the problem, but are you asking for the DisplayName attribute?
It will define how the html helpers display your field lables
public class License
{
[DisplayName("License Name")]
public string Name { get; set; }
// Other Properties
public List<Function> Functions { get; set; }
}
public class Function
{
[DisplayName("Fun Name")]
public string Name { get; set; }
// Other Properties
public List<Unit> Units { get; set; }
}
public class Unit
{
[DisplayName("Unit Name")]
public string Name { get; set; }
// Other Properties
}
be sure to have
using System.ComponentModel;
in your model code.
If you want to be able to create all of the inputs for a complex object graph and have the entire graph be reconstituted by the model binder, the easiest way to approach it is to create a single view or partial view that renders the entire graph:
#for(int i=0;i<Functions.Length;i++){
#for(int j=0;j<Units.Length;j++){
#Html.EditorFor(Functions[i].Length[j].Unit)
}
}
The other option would be to find a way to pass the index of your element to the partial views for each leaf on your object graph.
Granted, a lot of people dont like the idea of rendering a complex model inside of a single view. However, your other option is to make the smaller child views for Units, etc. be dependent on having additional data either injected or provided by the context. 6 of one, half dozen of the other. Just about every time I've done the "academically correct" approach of making exactly one view or partial view for each type in an object graph, I ended up with a whole bunch of views that were not reusable to begin with and the only advantage I got was the ability to say, "Look! Lots of small files.....that are totally dependent on each other...why did I do that?"

ASP.NET MVC Masterpage code file

Please can anybody point out where this code might be going wrong. I am trying to create a partial class for the masterpage.
The master pages class:
namespace MuniWeb.Models.SiteMaster{
public class BaseViewData
{
public string Title { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
}
public partial class Site : System.Web.Mvc.ViewMasterPage<MuniWeb.Models.SiteMaster.BaseViewData>
{
public Site()
{
ViewData.Model = new BaseViewData();
}
}}
The master page:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<MuniWeb.Models.SiteMaster.BaseViewData>" %>
The error:
Object reference not set to an instance of an object.
Line 33: <div id="footer">
Line 34: ApplicationID:
Line 35: <%= Model.Title %>
Line 36: </div>
Line 37: </div>
I am just using this as an example. The code I want to use needs to fire for every page to check certain parameters, this is why it is in the master page.
You shouldn't need to make a partial class. What does your controllers action code look like? The error looks like it could be from not handing the View (and therefore it's master page) a model.
Try something like this:
namespace MuniWeb.Website.ViewDataModels {
public class BaseViewData
{
public string Title { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
}
public class SubViewData : BaseViewData
{
public IList<Thing> Things { get; set; }
}
}
Then define your master page like you had:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<MuniWeb.Website.ViewDataModels.BaseViewData>" %>
Now in your controller
public ActionResult Index()
{
SubViewData viewData = new SubViewData();
viewData.Title = "Page Title";
viewData.MetaKeywords = "This, that, and the other";
viewData.MetaDescription = "A really great page about this, that, and the other.";
viewData.Things = _myRepository.GetThings();
return View(viewData);
}
See how that goes...
I would ask why the MasterPage NEEDS strongly type ViewData. I understand that yes, sometimes strongly typed viewdata is needed in masterpages but generally you should be able to get way with just using the ViewData name value collection.
Shouldn't your master page be inheriting Site and not System.Web.Mvc.ViewMasterPage? No where does your master page definition actually reference the Site class.
In your code,
public partial class Site :
System.Web.Mvc.ViewMasterPage<MuniWeb.Models.SiteMaster.BaseViewData>
{
public Site()
{
ViewData.Model = new BaseViewData();
}
}}
ViewData.Model = new BaseViewData(); is not necessary. The BaseViewData should be passed in via the controller. In other words, all of your views should take look for a View that inherits the base view. The Master Page will have that same object cast as the base class BaseViewData. Your controller code appears to be correct in doing just that.
Another thing that appears to be different from my similar code is Inherits=System.Web.Mvc.ViewMasterPage<MuniWeb.Models.SiteMaster.BaseViewData> should be Inherits=my.codebehind.class, then your codebehind would inherit ViewMasterPage<MuniWeb.Models.SiteMaster.BaseViewData>.
I've just not seen the code as such, perhaps it works?

Resources