I'm trying to pass my UserModel to every view, so it can show on top the current user information.
This is my BaseViewModel:
public class BaseViewModel
{
public UserModel currentUser { get; set; }
}
BaseController:
public class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
UserRepository userRep = new UserRepository();
BaseViewModel model = new BaseViewModel
{
currentUser = userRep.getCurrentUser()
};
base.OnActionExecuted(filterContext);
}
}
HomeController: (default)
public class HomeController : BaseController
{
public ActionResult Index()
{
ListRepository listRep = new ListRepository();
return View(listRep.GetAllLists());//returns IEnumerable<ListModel>
}
}
My shared _layout.cshtml:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>#ViewBag.Title - My ASP.NET Application</title>
#Styles.Render("~/Public/css")
</head>
<body>
<header>
#Html.Partial("_Header")
</header>
#RenderBody()
</body>
</html>
and finally, my _Header.cshtml:
<div id="logo">
<h1>Hello #Model.currentUser.username</ h1 >
</div>
But I get this error message when I run it:
'System.Collections.Generic.List' does not contain a definition for 'currentUser'
I thought my BaseViewModel would always get appended
The model in the view is IEnumerable<ListModel> and that is what will be passed to the partial unless you specify a model, for example, using
#Html.Partial("_Header", someOtherModel)
Instead, create a ChildActionOnly method that returns your user data to the _Header.cshtml partial view and in the main view use #Html.Action() to render it
[ChildActionOnly]
public ActionResult _Header()
{
var model = .... // get the model you want to render in the partial
return PartialView(model);
}
and in the main view
#{ Html.RenderAction("_Header", controllerName); }
If you want that in every view, you may consider calling a child action method using Html.Action.
So replace
<header>#Html.Partial("_Header")</header>
with
<header>#Html.Action("Header","YourControllerName")</header>
Now have an action method called Header where you will pass the needed data to it's partial view
[ChildActionOnly]
public ActionResult Header()
{
var vm = new YourHeaderViewModel();
vm.UserName="Shyju"; // Replace with whatever you want.
return PartialView(vm);
}
Where YourHeaderViewModel is your view mode for the header with the properties needed for the header.
public class YourHeaderViewModel
{
public string UserName {set;get;}
}
Now in your Header partial view which is strongly typed to the YourHeaderViewModel class,
#model YourHeaderViewModel
<div id="logo">
<h1>Hello #Model.UserName</ h1 >
</div>
Another possible solution using BaseController.
Instantiate your BaseViewModel model in OnResultExecuting method instead of OnActionExecuted. And save the model in ViewData to access it in _Header partial view.
E.g.
public class BaseController : Controller
{
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
UserRepository userRep = new UserRepository();
BaseViewModel model = new BaseViewModel
{
currentUser = userRep.getCurrentUser()
};
filterContext.Controller.ViewData["customData"] = model;
}
}
Now you need to pass ViewData to _Header partial view.
E.g.
#Html.Partial("_Header", ViewData["customData"])
In _Header partial view, you can simply access the data.
<div id="logo">
<h1>Hello #Model.currentUser.username</ h1 >
</div>
Related
Im trying to pass that data to my view. I allready look around what can i do or which way i can use or something but im still getting null exception error when i call that ViewBag.passData.
Here its my BaseController :
public class BaseController : Controller
{
public BaseViewModel baseViewModel;
public readonly IUnitOfWork<SiteSetting> _unitOfWorkSiteSetting;
public readonly IUnitOfWork<Category> _unitOfWorkCat;
public BaseController(
IUnitOfWork<SiteSetting> unitOfWorkSiteSetting,
IUnitOfWork<Category> unitOfWorkCat)
{
_unitOfWorkSiteSetting = unitOfWorkSiteSetting;
_unitOfWorkCat = unitOfWorkCat;
baseViewModel = new BaseViewModel
{
SiteSettings = _unitOfWorkSiteSetting.RepositorySiteSettings.GetSiteSettingsByID(2),//1-Admin,2-Shopping,3-WebFair
IsFeaturedCategories = _unitOfWorkCat.RepositoryCategory.IsFeaturedCategoriesAsNoTracing(10)
};
ViewBag.passData = baseViewModel;
}
}
as u can see im filling up my BaseViewModel in that BaseController.
im trying to do that because im calling those data in every page.
when i call that ViewBag.passData in my view,im getting null exception error.
Here its my view :
#model IndexViewModel;
#using Microsoft.Extensions.Configuration
#inject IConfiguration Configuration
#{ ViewData["Title"] = "Anasayfa";
Layout = null;
BaseViewModel baseViewModel = new BaseViewModel();
baseViewModel = ViewBag.passData; ***//The point where i am getting that error***
var siteKey = Configuration.GetSection("GoogleRecaptcha").GetSection("RecaptchaV3SiteKey").Value ?? "";
}
<!DOCTYPE html>
<html lang="tr">
<head>
<partial name="_HeadPartial" />
<!-- RECAPTCHA JS-->
<script src="https://www.google.com/recaptcha/api.js?render=#siteKey"></script>
<!-- RECAPTCHA JS-->
</head>
It goes like that....
I allready check it out my BaseController and its working well.
Thanks for any suggestion!
You can do return View(VieModel)
Then in your .cshtml view you can add #model of the type you passed into the view
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!
I have a Controller and a Model:
public class HomeController : Controller
{
public ActionResult Index()
{
var ribbon = new RibbonModel();
ribbon.Link = "_RibbonIndex";
return View(ribbon);
}
}
public class RibbonModel
{
public string Link { get; set; }
}
I want to use it in a view to call a partial view:
#model MyNamespace.Controllers.RibbonModel
#Html.Partial(model => model.Link)
I get an error (bad translated):
'lambda-expression' cannot converted to 'string'...
I tried it also with ViewBag and ViewData, nothing works. Any idea? Is there a better way to achieve this?
Thank you!
im just start learning asp.net and encounter this error
i've tried many solution from google but still get this error (errr...)
Compiler Error Message: CS1061: 'Exercise1.Visitor' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'Exercise1.Visitor' could be found (are you missing a using directive or an assembly reference?)
im using VS2012 with 4.5 Net
this is my code
Model
namespace Exercise1.Models
{
public class Visitor
{
public String Name
{
set;
get;
}
}
}
Controller
namespace Exercise1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Visitor data)
{
ViewBag.Message= "Hi my name is" + data.Name;
return View();
}
}
}
View
#{
Layout = null;
}
#model Exercise1.Visitor
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div>
#using(Html.BeginForm())
{
<p>
#Html.LabelFor(m=>m.Name);
</p>
}
</div>
</body>
</html>
btw, this is my first question hehe
Change model to:
#using Exercise1.Models
#model Exercise1.Models.Visitor
Do you have another Vistor class in your project? it's saying Exercise1.Visitor when it should be complaining about Exercise1.Models.Visitor
make sure you're referencing Exercise1.Models.Visitor
I have a View that allows a user to enter/edit data for a new Widget. I'd like to form up that data into a json object and send it to my controller via AJAX so I can do the validation on the server without a postback.
I've got it all working, except I can't figure out how to pass the data so my controller method can accept a complex Widget type instead of individual parameters for each property.
So, if this is my object:
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
I'd like my controller method to look something like this:
public JsonResult Save(Widget widget)
{
...
}
Currently, my jQuery looks like this:
var formData = $("#Form1").serializeArray();
$.post("/Widget/Save",
formData,
function(result){}, "json");
My form (Form1) has an input field for each property on the Widget (Id, Name, Price). This works great, but it ultimately passes each property of the Widget as a separate parameter to my controller method.
Is there a way I could "intercept" the data, maybe using an ActionFilterAttribute, and deserialize it to a Widget object before my controller method gets called?
Thanks Jeff, that got me on the right path. The DefaultModelBinder is smart enough to do all the magic for me...my problem was in my Widget type. In my haste, my type was defined as:
public class Widget
{
public int Id;
public string Name;
public decimal Price;
}
Notice that the type has public fields instead of public properties. Once I changed those to properties, it worked. Here's the final source code that works correctly:
Widget.aspx:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Widget.aspx.cs" Inherits="MvcAjaxApp2.Views.Home.Widget" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<script src="../../Scripts/jquery-1.2.6.js" type="text/javascript"></script>
<script type="text/javascript">
function SaveWidget()
{
var formData = $("#Form1").serializeArray();
$.post("/Home/SaveWidget",
formData,
function(data){
alert(data.Result);
}, "json");
}
</script>
<form id="Form1">
<input type="hidden" name="widget.Id" value="1" />
<input type="text" name="widget.Name" value="my widget" />
<input type="text" name="widget.Price" value="5.43" />
<input type="button" value="Save" onclick="SaveWidget()" />
</form>
</asp:Content>
HomeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace MvcAjaxApp2.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Title"] = "Home Page";
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
ViewData["Title"] = "About Page";
return View();
}
public ActionResult Widget()
{
ViewData["Title"] = "Widget";
return View();
}
public JsonResult SaveWidget(Widget widget)
{
// Save the Widget
return Json(new { Result = String.Format("Saved widget: '{0}' for ${1}", widget.Name, widget.Price) });
}
}
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Note that (in MrDustpan's solution) the parameter name widget in the MVC Action method must match with the prefix used in the name attribute in the ASPX file.
If this is not the case then the Action method will always receive a null object.
<input type="text" name="widget.Text" value="Hello" /> - OK
<input type="text" name="mywidget.Text" value="Hello" /> - FAILS
Phil Haack has a good blog post about model binding that might be helpful. Not 100% what you're talking about here, but I think it might give you a better overall understand about the DefaultModelBinder.
What you want to do is structure your javascript form object in the same way your backend object is structured:
{ Id : "id", Name : "name", Price : 1.0 }
Then use the toJSON plugin to convert it into the above string. You send this string to your backend and use something like the JayRock libraries to convert it to a new Widget object.