I'm a newbie about ASP.NET MVC 3, but I have a simple question.
Is it possible to call a Controller method from an CSHTML (Razor) page?
Example:
xxxControl.cs:
public String Bla(TestModel pModel)
{
return ...
}
index.cshtml:
#Bla(Model) <-- Error
Thanks.
Update:
Thanks #Nathan. This isn't a good idea to do this on this way.
The goal is: I need some formatting string for a field of the Model. But where I put the code that return a formatting String in the case?
It is considered bad practice for a view to call methods located on a controller. Usually it is a controller action which populates a model and passes this model to the view. If you needed some formatting on this model you could write an HTML helper.
public static class HtmlExtensions
{
public static IHtmlString Bla(this HtmlHelper<TestModel> htmlHelper)
{
TestModel model = htmlHelper.ViewData.Model;
var value = string.Format("bla bla {0}", model.SomeProperty);
return MvcHtmlString.Create(value);
}
}
and in your view:
#Html.Bla()
That would make unit-testing your mvc site very difficult.
Are you needing a partial view maybe? (what are you actually trying to do?)
Yes it's possible.
#using Nop.Web.Controllers;
#
var _CatalogController = EngineContext.Current.Resolve<CatalogController>();
var _model = new ProductModel();
_model = _CatalogController.PrepareProductOverviewModel(p, true, true);
}
Set the method to public if it is private.
Even the services you can call in the same manner.
var _productService = EngineContext.Current.Resolve<IProductService>();
if (Model.SubCategories.Count > 0)
{
foreach (var SubCategories in Model.SubCategories)
{
int subcategoryid = SubCategories.Id;<br>
IPagedList<Product> _products = _productService.SearchProducts(subcategoryid,0, null, null, null, 0, string.Empty, false, 0,null,ProductSortingEnum.Position, 0, 4);
}
i++
}
Simply do it like this:
xxxControl.cs action method:
public ActionResult YourView(TestModel pModel) {
//pMomdel code here
ViewBag.BlaResult = Bla(pModel);
return View(pModel);
}
index.cshtml:
#ViewBag.BlaResult
Related
I was wondering what's the best approaching to read data in ASP.NET.
Method 1:
In the controller:
public ActionResult CatPerfomanceReportCreate(string dateStart, string dateEnd)
{
ViewBag.DateStart = dateStart;
ViewBag.DateEnd = dateEnd;
return View();
}
and the View:
#{
var readData = MyDataClass.GetData(ViewBag.DateStart, ViewBag.DateEnd)
}
..
..
..
<table>,,,,,<table>
Method 2:
In the controller:
public ActionResult CatPerfomanceReportCreate(string dateStart, string dateEnd)
{
ViewBag.Data = MyDataClass.GetData(dateStart, dateEnd);
return View();
}
and the View:
#{
List<MyDataClass> data = ViewBag.Data;
}
..
..
..
<table>,,,,,<table>
Method 1 read the heavy data in the View and Method 2 in the controller.
Thanks
Neither.
Method2 is slightly better than Method1 because it comply with the Dumb View method. Both of those method make use of the ViewBag dynamic which can but not intended to save information about your information you want to present to the user. Strongly Type elements in most cases are much preferred than dynamics and giving you the full force of the compiler behind it.
Let me suggest Method3 that is much better suited method for MVC. It is creating a Model of the data you want to use in your View and use it.
Model:
public class CatPerfomanceReport
{
public string DateStart { get; set; }
public string DateEnd { get; set; }
}
Controller:
public ActionResult CatPerfomanceReportCreate(string dateStart, string dateEnd)
{
var model = new CatPerfomanceReport
{
DateStart = dateStart,
DateEnd = dateEnd
};
return View(model);
}
Partial of View:
#model CatPerfomanceReport
<h1>Start: #Model.DateStart</h1>
<h1>End: #Model.DateEnd</h1>
I am in the habit of using nested loops in classic. Data from the first record set is passed to the second record set. How would I accomplish the same thing in MVC? As far as I can tell, I can only have one model passed to my view.
<%
rs.open "{Call usp_SalesOrder}",oConn
Do While (NOT rs.EOF)
%>
<div><% = rs("SalesOrderNumber")%></div>
<%
rs2.open "{Call usp_SalesOrderLines(" & rs("SOKey") & ")}",oConn
Do While (NOT rs.EOF)
%>
<div><% = rs2("SalesOrderLineNumber")%></div>
<%
rs2.MoveNext
Loop
rs2.close
%>
<%
rs.MoveNext
Loop
rs.close
%>
My suggestion would be to build a more robust model. It is true that you can only pass one model to your view, but your model can contain the results of multiple data sets, provided you have gathered those data sets in your controller and assigned them to the model.
I also suggest staying away from the ViewBag. It's an easy trap to fall into. Trust me when I say you'll regret it later.
For your example, maybe a model defined like this:
public class MyModel
{
public List<SalesOrder> SalesOrders = new List<SalesOrder>();
}
public class SalesOrder
{
public string SOKey = string.Empty;
public List<SalesOrderLine> SalesOrderLines = new List<SalesOrderLine>();
}
And the code to populate the sales orders in the controller:
public Action Index()
{
MyModel model = new MyModel();
model.SalesOrders.AddRange(CallUspSalesOrder());
foreach (SalesOrder salesOrder in model.SalesOrders)
{
salesOrder.SalesOrderLines.AddRange(CallUspSalesOrderLines(salesOrder.SOKey));
}
return View(model);
}
That way, you have access to all sales orders (and their sales order lines) within the view.
I would say that Nathan's post is a good start. Here is what I would do from beginning to end.
This is how I would do my model:
public class SalesOrderModel
{
public List<SalesOrderLines> SOLines = new List<SalesOrderLines>();
public List<SalesOrder> SOHeader = new List<SalesOrder>();
}
My Controller would then do this:
public ActionResult Index()
{
List<SalesOrder> SalesOrder = callSalesOrderUSP.ToList();
List<SalesOrderLines> SalesOrderLines = new List<SalesOrderLines>();
foreach (var thing in SalesOrder)
{
SalesOrderLines.AddRange(callSalesOrderLinesUSP(thing.SOKey).ToList());
}
SalesOrderModel salesOrderModel = new SalesOrderModel
{
SOHeader = SalesOrder,
SOLines = SalesOrderLines
};
return View(salesOrderModel);
}
Then in your view you can do this:
#foreach(var something in Model.SOHeader)
{
foreach (var thing in Model.SOLines.Where(i => i.SOKey == something.SOKey))
{
//display info here
}
}
You can use ViewBag to pass elements not relevant to your model. Also do not be afraid of creating your own ModelView objects that can work between your View and Controller. Your views should not be restricted to what your model has to offer.
Take a look at this for how you can implement a ViewModel in MVC.
And perhaps look at this to see how you can use ViewBag to pass values to your view, not relevant to your model.
I have a view that loads a record with a certain record number. Once the page is loaded, it gives the user an opportunity to login for additional information. Once the login logic is performed, I need to return to that same view with the same record number intact. I am passing the record number to the action using a hidden input in the form. What I can't seem to figure out is how to return to that same view and provide it with that record #. The code I am trying below is not working. I know this is MVC 101 stuff but a hint in the right direction would be appreciated, or feel free to scrap my method and suggest something better!
Form in view:
<form action="/MyView/Authenticate/#item.ID" method="post" enctype="multipart/form-data">
<input name="form_id" type="hidden" value="#item.ID">
.....
Form action:
[HttpPost]
public ActionResult Authenticate()
{
int myid = Convert.ToInt16(Request["form_id"]);
.....
return View("Index", new { id = myid } );
}
EDIT:
It turns out that the correct view is being returned, but it is expecting a model item type of "JobSummaryModel" per the Index action result below. So the question I actually need answered is, how do I pass both the record id and this view model to it?
public ActionResult Index(int id = 0)
{
List<JobSummaryModel> jdata;
ViewBag.IsResults = false;
if (id != 0)
{
ViewBag.IsResults = true;
}
jdata = db.Jobs.Where(c => c.ID == id).Select(c => new JobSummaryModel() { ID = c.ID, Name = c.Name, City = c.City, PostalCode = c.PostalCode, JobDescription = c.PositionDescription }).ToList();
return View(jdata);
}
EDIT:
Thanks Reddy, your suggestions worked! My only remaining issue is that when I return to my Index view from the Authenticate action, I do not seem to have my "jdata". Is my Index action result not being rerun when I return the Index view via my Authenticate action? I am coming from a web forms background where, in an instance like this, the Load/Init events would automatically run when a form is loaded. Do I need to bind my "jdata" in the Authenticate action and include it in the viewmodel?
EDIT: Resolved. Changed my "return View" to a "return RedirectToAction" to resolve my final issue. Thanks everyone!
Answer For your after Edit:
All you want to pass to view is a int Id and your List<JobSummaryModel> jdata right?
So create a ViewModel JObSummaryModelHelper
Public class JObSummaryModelHelper
{
public int Id {get;set;}
public List<JobSummaryModel> jdata {get;set;}
}
Now in your controller
public ActionResult Index(int id = 0)
{
JObSummaryModelHelper jobDetails = new JObSummaryModelHelper();
jobDetails.Id = id;
ViewBag.IsResults = false;
if (id != 0)
{
ViewBag.IsResults = true;
}
jobDetails .jdata = db.Jobs.Where(c => c.ID == id).Select(c => new JobSummaryModel() { ID = c.ID, Name = c.Name, City = c.City, PostalCode = c.PostalCode, JobDescription = c.PositionDescription }).ToList();
return View(jobDetails );
}
Now make sure your view is set to expect this new viewmodel
#model JObSummaryModelHelper
carry on with your manipulation......
You are better off creating a ViewModel for this like so:
Create a View Model class i.e.
public class AuthViewModel
{
public int MyId { get; set; }
}
In your View put the following directive at the top:
#model AuthViewModel
In your initial [HttpGet] method return the view model:
[HttpGet]
public ActionResult Authenticate()
{
var model = new AuthViewModel { MyId = 123 };
return View("Index", model );
}
It's best to use Html helpers in your view, so you can change it to this:
#using(Html.BeginForm()
{
#Html.HiddenFor(m => m.MyId)
...
}
The above uses naming conventions to post back to the action that you are on.
Then return it to your view like this:
[HttpPost]
public ActionResult Authenticate(AuthViewModel model)
{
int myid = model.MyId;
return View("Index", model );
}
Then you can output using this razor syntax #Model.MyId
It's really worth doing some tutorials to learn the conventions, a small amount of time invested in this will save you a lot of time in the future.
Instead of
return View("Index", new { id = myid } );
could you do
return Index(myid);
I have an MVC application that when a link is clicked a page needs to be displayed based on the same values on another page. I can't figure out why what's getting passed is null instead of the string. My code is below.
Controller:
public string searchQ
{
get { return (string)Session["searchQ"]; }
set { Session["searchQ"] = value; }
}
public ActionResult Index()
{
Session["InitialLoad"] = "Yes";
return View();
}
[HttpPost]
public ActionResult Index(string heatSearch)
{
ViewBag.SearchKey = heatSearch;
searchQ = heatSearch;
return View();
}
public ActionResult Index_Perm()
{
ViewBag.SearchKey = searchQ;
return View();
}
public ActionResult PartialMainLim(string heatSearch)
{
HomeModel C = new HomeModel();
ChemViewModel D = new ChemViewModel();
D = C.QueryResults(heatSearch);
return PartialView(D);
}
public ActionResult PartialMain(string heatSearch)
{
HomeModel C = new HomeModel();
ChemViewModel D = new ChemViewModel();
D = C.QueryResults(heatSearch);
return PartialView(D);
}
The code in the index view looks like this (this one works):
#if (ViewBag.SearchKey != null)
{
<div>
#Html.Action("PartialMainLim", "Home", (string)ViewBag.SearchKey)
</div>
}
And in the index_perm view:
#if(ViewBag.SearchKey != null)
{
<div>
#Html.Action("PartialMain", "Home", (string)ViewBag.SearchKey)
</div>
}
When I check the value of SearchKey in both views it is correct. However for the method "PartialMain" null gets passed instead of the string, despite SearchKey being correct. This all works for the other view though. What am I doing wrong?
When passing values back to controller you have basically two options:
Make a form
Pass it as part of the url
Get methods only accept url attributes while Post methods are able to handle form content as well.
From what you are trying to do I'd say you could use something like:
#Html.Action("PartialMain", "Home", new {heatSearch = (string)ViewBag.SearchKey})
this should create url looking like /Home/PartialMain?heatSearch=[content of SearchKey]
EDIT:
That will only pass the value given it is present in the ViewBag. You are getting it from the Session which is imho a terrible idea in MVC (which should be session-less). Please consider if you really need it there. Usually there are other ways to implement this.
There is no HttpPost handler in the controller when you click the index_perm view.
I think that problem is your session that would be null. One of principles of framework ASP.NET MVC is stateless. Using session in ASP.NET MVC quite horrible.
At the moment, I think you can quickly fixed it by using TempData that default using Session under the hood. You could have a look an outdated article for further digging up ViewData vs TempData
It is said that the models should be fat and the Views should be thin.We put our business logic inside the Model (https://stackoverflow.com/questions/235233/asp-net-mvc-should-business-logic-exist-in-controllers).We normally write the LINQ inside the controller,but is it possible that we should write the query in models,If yes then how we will get the results in the View?
Second Question
public ActionResult Index()
{
using (NORTHWNDEntities c = new NORTHWNDEntities())
{
var x = c.Employees.Count();
ViewData["count"] = x;
return View(x);
}
}
When we do this are we passing the variable x to the View?
I tried to access the ViewData in View
<% ViewData["count"] %>
But it gives an error error ,Anyone who can help me with this
Thanks
If you are trying to display the value of ViewData["count"] in your view, you can use the following syntax:
<%= ViewData["count"] %>
Note the = in the opening tag. This is the equivalent of
<% Response.Write(ViewData["count"]) %>
there is better approach for doing this.and is very straight forward.create a Model that meets your needs and pass it to view.
public class MyModel
{
public int Count{get;set;}
}
and your controller can looks like
public ActionResult Index()
{
using (NORTHWNDEntities c = new NORTHWNDEntities())
{
var x = c.Employees.Count();
var model = new MyModel{Count = x};
return View(model);
}
}
and then create an strongly typed view
Razor Syntax :
#model MyModel
#Model.Count
ASPX syntax :
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.MyModel>" %>
<%= Model.Count %>
and the code can get even better if you do the following:
public class EmployeeService
{
public int GetEmployeeCount()
{
using (NORTHWNDEntities c = new NORTHWNDEntities())
{
var count = c.Employees.Count();
return count;
}
}
}
and the controller most change as well:
public ActionResult Index()
{
EmployeeService srvc = new EmployeeService();
var x = srvc.GetEmployeeCount();
var model = new MyModel{Count = x};
return View(model);
}
The query have to be into the Data Access Layer and the logic in MVC is into the Controller, not into the Model.
Here you can find an example of a layered architecture with MVC.
At the end you have always to use a Model into the View, don't pass data using the ViewData.