I'm implementing MVC in C#, The controller has a property View to call its methods. My problem is that in the View when the user presses a button i want to notify the controller, in other words i need to access functions of the Controller class from my View. So i added a property Controller inside my View. Now this works, but it feels wrong and tight. What would be the way to go here ? Here's an example source code:
class Controller
{
public void DoSomething() { }
public void SetView (View v)
{
this.view = v;
}
private View view;
}
class View
{
public void SetController(Controller c)
{
this.controller = c;
}
public void OnButtonPress()
{
controller.DoSomething();
}
private Controller controller;
}
MVC is built the way it is so that the Controller, Model and View are separated and be easy to modify without one affecting the other
The controller should only have the actual logic while the View is only the presentation to the user
Try adding this to your View(html page):
#Html.ActionLink("linktext", "OnButtonPress", "ControllerName",new { #class = "yourButtonClass" })
Then add the method to your controller class with any logic you want:
class controller
{
public IActionResult OnButtonPress()
{
}
}
Related
I have two controllers:
AaController have a view named Index and a database context that we can call for dbContextAa
RrController also has its own database, which we can call for dbContextRr and an action method GetData ()
What I wonder is; how do I do if I want to take the data from the method GetData () and display it in the index view.
Previously, I tested with Temp Data and Sessions without any result. I had no value at all in the view from RrDatabase.
If I understand it right, I have to use Dependency Injections.
So here I have created dependency injection (not sure if it is correct)
AaController:
public class AaController : Controller
{
private RrController rc;
public AaController(RrController rr, AaContext aaContext)
{
rc = rr;
_aaContext = aaContext;
}
public IActionResult Index()
{
var dataFromRr= rs.GetData();
ViewBag.rrData = dataFromRr;
return View();
}
RrController:
public class RrController : Controller
{
private readonly RrContext _rrContext;
public RrController(RrContext rrContext)
{
_rrContext = rrContext;
}
public IActionResult GetData()
{
var data = _rrContext.RrData.Count(x => x.Pid == x.Pid);
return RedirectToAction("Index", "Aa", data );
}
Instead of the value of the view I get Microsoft.AspNetCore.Mvc.ViewResult.
How do I solve this?
Because GetData method returns an IActionResult type response(specifically MVC action result, RedirectResult).
Ideally what you should be doing is, extract the code which gives you the relevant data to a method in a separate class, which you can use in multiple places as needed (ControllerA and ControllerR).
Let's create an interface and a class to implement it.
public interface IRService
{
int GetRDataCount(int pId);
}
public class RService : IRService
{
private RrContext _rrContext;
public RService (RrContext rrContext)
{
this._rrContext=rrContext;
}
public int GetRDataCount(int pId)
{
return _rrContext.RrData.Count(x => x.Pid == pId);
}
}
You can inject IRService implementation to your controllers and call the GetRDataCount method to get the int value.
public class AaController : Controller
{
private IRService rService;
public AaController(IRService rService)
{
this.rService=rService;
}
public IActionResult Index()
{
int countVal = this.rService.GetRDataCount(234); // pass actual PId
ViewBag.rrData = countVal;
return View();
}
}
Make sure to add the IRservice to RService dependency mapping in your Startup class.
Now wherever you want to get the RData count for a specific pId, you can inject an IRservice implementation and use it (Do it in your RController as well)
There are a few different approaches depending on the structure of your projects and how (and if) they can communicate with one another.
What I wonder is; how do I do if I want to take the data from the
method GetData () and display it in the index view.
Your GetData() method is actually attempting to return a View on it's own, however it looks like what you want to do is actually just return the data (i.e. the count) and pass that to your other action:
public int GetData()
{
var data = _rrContext.RrData.Count(x => x.Pid == x.Pid);
// This will pass a parameter that contains your count
return RedirectToAction("Index", "Aa", new { count = data });
}
You need to actually pass your data to the View as a "model". This can easy be done by simply decorating your View with the type of data that you are going to be passing in:
public IActionResult Index(int count)
{
// The count parameter will have the current result of your previous call in it
return View(count);
}
Then you would just need to tell your actual View how you wanted to present the data (i.e. what type it was) via the #model directive at the top of the view:
#model int
There were #Model records found.
Consider A Service
A better implementation would be to consider using a service that decouples your logic from any one specific controller. This would allow you to simply instantiate the service and call it from wherever it was needed as Shyju's answer indicates.
I have a target to create a nested architecture of view/controller. For example:
we have few areas in webpage. All this areas managed by its own controller. In another words I need to create independend pages and then join them in one page.
Please see the image:
So is it possible and where I can read about it? Thanks.
Yes you can achieve this by simply calling
#Html.Action("ActionName", "ControllerName");
Example:
in your case you can do like this
Controller1
public ActionResult View1()
{
return View("View1");
}
Controller2
public ActionResult View2()
{
return View("View2");
}
Controller3
public ActionResult View3()
{
return View("View3");
}
Calling in main page:
#Html.Action("View1", "Controller1");
#Html.Action("View2", "Controller2");
#Html.Action("View2", "Controller2");
call these in different section of main page wherever you want.
You can use #Html.Action() to render child views from different controllers
public class FirstController : Controller
{
public ActionResult Index()
{
return View();
}
}
public class SecondController : Controller
{
[ChildActionOnly]
public ActionResult Method1()
{
return PartialView();
}
}
public class ThirdController : Controller
{
[ChildActionOnly]
public ActionResult Method2(int ID)
{
return PartialView();
}
}
Index View (FirstController)
....
#Html.Action("Method1", "Second")
#Html.Action("Method2", "Third", new { ID = someValue })
You can also use #{ Html.RenderAction(); which is more efficient is you are generating a lot of html.
I ve got 2 seperate controller sheets not just action methods..
MY First Controller:
namespace TestLokal.Controllers
{
public class BOUNCEController : Controller
{
BOUNCEDataDataContext db = new BOUNCEDataDataContext();
//
// GET: /BOUNCE/
[Authorize]
public ActionResult Index()
{
ViewData["Bouncers"] = new SelectList( db.Bouncers.Distinct(), "bouncer_id", "bouncer_name");
return View();
}
}
}
MY Second Controller:
namespace TestLokal.Controllers
{
public class DopplerController : Controller
{
//
// GET: /Doppler/
[Authorize]
public ActionResult Index()
{
elementmodel dop = new elementmodel();
ViewData["Dopplers"] = new SelectList( dop.BouncerList.Distinct(), "bouncer_id", "bouncer_name");
return View();
}
}
}
i wanna transfer data from first to second by using this model:
MY Model:
public class elementmodel
{
public IEnumerable<Bouncers> BouncerList { get; set; }
}
How can i achieve this?
You can use TempData for this.
See http://msdn.microsoft.com/en-us/library/dd394711(v=vs.90).aspx (passing data between action methods)
First of all ... are you sure you need two separate controller that has same data? In my humble opinion each controller should be responsible for handling his own group of models. If you need to have the same data in both of controllers maybe you should create some base class?
public class MyBaseController : Controller
{
//shared fields & methods for example datacontext
}
public class DopplerController : MyBaseController
{
}
public class BounceController : MyBaseController
{
}
but if I'm wrong and one of your controller just need results of work from another controller you can use Coockies, Session.
2 things come immediately to mind.
You can store the list of bouncers in the View as a JSON object
that gets POSTed off to the other controller.
You can use TempData to store it between requests. eg: http://msdn.microsoft.com/en-us/library/dd394711.aspx
I've created a tag cloud partial view for my site. The partialview is to be included on all pages on the site. The data comes from a database. Is there a way to run the code that lives in the controller globally, so I don't have to put it on every single action of every single controller?
I'd like to avoid putting this on every action:
public ActionResult Index()
{
ViewData["Tags"] = Tags.GetTags();
return View();
}
and
public ActionResult Index()
{
return View(Tags.GetTags());
}
It'd be a nightmare if I ever had to change that code. There has to be a better way of handling database bound content that is on every page of a site.
You could always use the ViewModel pattern and have a base ViewModel class for all your actions:
public abstract class ViewModelBase
{
public IEnumerable<Tag> Tags { get; private set; }
public ViewModelBase()
{
Tags = GetTagsFromDatabase();
}
}
Then just make every subsequent ViewModel inherit this base class.
public HomeViewModel : ViewModelBase
{
...
}
And then in your Controller action:
public ActionResult Index()
{
var viewModel = new HomeViewModel();
return View(viewModel);
}
Hope this helps.
You can put your global code either in your MasterPage, or you also make a custom control out of your code and reuse it in the pages that need that code.
So, basically what I'd like is to do something like:
#if(Notification!=null){
//perform javascript notification with #Notification.Text
}
And I'd like to be able to do this on any view, so I will always have the option of specifying a notification object in my controller action, that, if defined, can be handled in the view.
My dream scenario is to allow this simply by creating the Notification object somehow, and then just returning the view. Meaning, I wouldn't need to explicitly pass the Notification object to the model. Like so:
public ActionResult MyAction(){
Notification n = new Notification("Text for javascript");
return View();
}
I'm thinking, that there might be a way to do this with some ViewPage-inheritance? But I'm really unsure of how to go about this?
In an ideal world, I would also love to be able to "override" what to do. For example, if I in my 'top'-layout choose to perform a certain kind of jquery-notification if the notification object exists, but maybe in some other nested view would like to handle it differently, I'd like the option to override the top-layouts handling of the object.
I know this last thing might be a little utopian (I'm just starting out with MVC and Razor), but it would be cool :)
You could write a custom global action filter which will inject this information on all views. For example:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.Controller.ViewBag.Notification = new Notification("Text for javascript");
}
}
and then register this filter in the RegisterGlobalFilters method of your Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new MyActionFilterAttribute());
}
And then in your views:
#if(ViewBag.Notification != null)
{
//perform javascript notification with #ViewBag.Notification.Text
}
Use ViewBag for simple stuff like popup message.
public ActionResult Index()
{
ViewBag.PopupMessage = "Hello!";
return View();
}
and then in view (or layout page)
#if (ViewBag.PopupMessage != null)
{
<div class="popup">#ViewBag.PopupMessage</div>
}
For more complicated stuff you will need to either create static class and save/read from HttpContext.Current.Items or override Controller and WebViewPage and save/read from ViewBag/ViewData.
Update:
public abstract class BaseController : Controller
{
public const string NotificationKey = "_notification";
protected string Notification
{
get
{
return ViewData[NotificationKey] as string;
}
set
{
ViewData[NotificationKey] = value;
}
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
protected string Notification
{
get
{
return ViewData[BaseController.NotificationKey] as string;
}
}
}
Views/Web.config
<pages pageBaseType="OverrideTest.Framework.BaseViewPage">
Usage:
public class HomeController : BaseController
{
public ActionResult Index()
{
Notification = "Hello from index!";
return View();
}
}
<div>Notification: #(Notification ?? "(null)")</div>
Or get test project here
Update 2:
Check out this blog post for another way to do something similar.