I'm trying to understand controllers in the new router system. I understand they exist to decorate models and present non-permanent state to the view/template for rendering. And I understand the conventions the Ember router uses to instantiate and set up single copies of each controller from route names. But does that mean you should never have more than one copy of a controller?
Here's my use case: I have a set of nested lists of items with complex non-persistent per-item state (different levels of collapsed / visible nesting, different editing modes, etc). I don't think this belongs in the view, and it's too complicated (because of nesting) to keep in a singleton controller -- so I was planning to have one controller instance per one view instance (all the same controller and view class though). Is this sound?
Second, simpler question. How do I access the controllers that Ember router instantiates? Previously, you could do App.get('router.postController') but that no longer works.
First Question
Not all controllers are singletons. There are cases where Ember.js creates non-singleton controllers for you.
One case is when using itemController with {{each}} helper:
{{#each model itemController="post"}}
<!-- template here -->
{{/each}}
This will create a new instance of App.PostController for every post item in the loop.
One other case is when you use the {{render}} helper and pass a model to it:
{{render "post" firstPost}}
{{render "post" secondPost}}
This will create a separate App.PostController instance for every {{render}}.
Second Question
It depends where you want to access them from.
From the route:
this.controllerFor("post");
From another controller:
App.CommentController = Ember.ObjectController.extend({
needs: ['post'],
test: function() {
return this.get('controllers.post'); // this returns the post controller
}
});
More info on that here: http://emberjs.com/guides/controllers/dependencies-between-controllers/
From the view:
this.get('controller');
Related
I have a design question about MVC and controllers
I have this two routes
Route::get('/foo/{id}', FooController#show)
Route::get('/bar/{id}', BarController#show)
But know, I would like to add another route for the index like this
Route::get('/', ???)
In this route, I need some information about the Foo and Bar models.
The question is, should I create a new controller for that route? like MainController?
In general, controllers are meant to respond to requests associated with a specific resource (model). Thereby, given your concrete example, two distinct scenarios apply.
The Foo and Bar models are required on the landing page (/ route): In this case, a dedicated controller would be a perfectly fine thing to do. Also, the use of a view model would be beneficial.
No information about application-specific models is needed on the landing page: You can still use a dedicated controller class to return your view, while another possibility would be the use of a lambda function:
Route::get('/', function () {
return view('landing');
});
It has always worked best for me to have a controller per resource type.
You have the resource 'foo' with FooController, and 'bar' with BarController. So for the root context resource, you aught to have a root controller.
This helps with separation of concern and keeps your code modularized.
I've been using Ember for a while but still struggling sometimes to find out the best practices. So one of the Ember ways regarding controller and view is
an opinion of Ember's designers, that is enforced by the router is that for a given BaseName (e.g. "Application," "CustomerEntry," "My Items") there should be a BaseNameView and a BaseNameController. -- Ember guide
The problem is that what if I want multiple instances of the same view on a page. Since the controller is created during initiation of the application, they are singletons under the application namespace, which will not be able to hold two instances of the model data.
One solution I see is to create controllers(and model data) manually and pass them to views. But in this case, I'd like Ember not create controllers automatically for me. Put it another way, why would Ember creates controllers as singletons during application startup.
I think there are many use cases where a View type doesn't have a corresponding Controller type. Especially when the type of view is more like a UI widget than a full-fledged application feature. Many views can share the same controller. Take a look at this applicationView template:
<h1>Here are two files, compare them</h1>
{{view App.MyFileView contentBinding="leftFileContent"}}
{{view App.MyFileView contentBinding="rightFileContent"}}
This creates two instances of my view class and binds their content properties to two different properties on the applicationController. The controller property for both of those views is set to the singleton applicationController instance.
One possible reason why controllers are singletons could be that they're able to be addressed in the global namespace via something like App.router.myController.
I find myself needing to have a View expose its Model and Controller references. Is this the smell of bad design? Or is this considered "safe" practice?
For example: I have a list (composed of a ListView, ListController, and ListModel) and many list items (composed of a ItemView, ItemController, and ItemModel).
When I create the ItemModel, ItemView, and ItemController for each list item, I pass the ItemView instance off to the ListView. But, at some later point, my ListController needs a reference to the corresponding ItemController instance.
So, would it be more proper to pass both the ItemView and the ItemController in to ListView::addItem(), or just pass in ItemView and expose an instance method such as ItemView::getController()?
Or doesn't it matter? Is each approach equally viable? If followed to their logical conclusion, does either tactic result in an anti-pattern?
But, at some later point, my ListController needs a reference to the corresponding ItemController instance
Why? If you're decoupling your classes properly, you shouldn't need this.
Controllers almost always address a functional domain. An example of such a domain might be "Sales" or "Admin." In addition, MVC also supports the use of "Areas," which provides an additional hierarchical level of organization.
Adding references to controllers from other controllers is at cross-purposes with this organizational structure. If you need to combine functionality to make your code more DRY, ordinary refactoring will accomplish that. You can also inherit controllers from a base class containing common functionality.
In the mvc pattern the users request shall be routed to a controller, say invoicecontroller, that has actions.
Lets say the default action, Index, returns a list of invoices; the controller then creates a model with a list of invoice objects, instantiates the correct view and injects the model into the view.
Now it is the views turn to do its magic. It renders the best view it can with the data it has, which may include routes to one or more controllers.
In NO instance should the view (or model) do business logic themselves.
That said, I totally agree with Jakub. Hope that helps.
Considering you are not actually showing any code at all.
In my opinion, you should change your design. A controller is not supposed to communicate with another controller (directly), MVC dictates it: reference.
If you need to invoke a controller action from another controller, consider using delegates or composition. Instead of directly invoking the controller action.
I'm writing an application which uses ASP.NET MVC with JSON.NET as the server, sending JSON to the client which is read by Knockout and then displayed with data-bindings. This is all working wonderfully, except for one problem:
I have a class Customer which is used to generate a ReviewAuthorViewModel class - the latter is meant specifically for JSON serialization and removes unnecessary fields, circular references, etc. On the client, I want to render a link to the Customer's profile page, with the URL coming from a defined MVC route "user/{username}". Because I'm sending this via JSON, I don't have a .cshtml page in which I can call Url.Action.
The question is: For an arbitrary object, how do I most efficiently/elegantly get a URL for an action with some data, without a .cshtml view?
I'd prefer a solution that doesn't require extra code in each action, but that may be the only choice apart from recreating the routing tables client-side in JavaScript. Below are the things I've already tried, and what was unsatisfactory with each.
Solution Attempt 1
Call the UrlHelper.Action method in the ReviewAuthorViewModel class. However, the UrlHelper requires a request context. For the sake of separation of concerns, I don't want my view model to have a dependency on System.Web.Routing, nor do I want it to need a request context to function.
Solution Attempt 2
Create a class RouteInformation with members Controller, Action, and Data, and an interface IUrlViewModel with two properties, a get of type RouteInformation and a get/set string named Url. The view models needing links then implement this interface, and controllers look for view models of type IUrlViewModel and run UrlHelper.Action with the information from the view model's RouteInformation instance, storing the result in the Url property.
This one works but without reflection I don't know how to find view models implementing IUrlViewModel within other view models, and it feels very kludgy.
Solution Attempt 1 is the OK solution. In asp.net-mvc, view models are part of presentation layer, designed specifically for use with views. You should not worry about having view model depend on asp.net specific things. In fact, they should be coupled, as they should be designed to maximize simplicity of view generation and data exchange between web server and web client. And it's good solution to create separate view model and not use Customer class directly for clients. It wouldn't have been OK if Customer was dependent on Routing.
In fact, you could set that property in controller -
[HttpGet]
public ActionResult Get()
{
var viewModel = new ReviewAuthorViewModel();
viewModel.ProfilePageUrl = Url.Action("Index", "Profile");
// return viewModel;
}
I am developing an application that involves a type hierarchy and started by defining the models for each type via inheritance. When it comes to writing the corresponding controllers I am not sure how to approach the whole thing in a clean way. Should I write only one controller for the base type that is able to handle derived models or should there be one controller for each subtype? How should the view-controller bindings be set up to work with the different controllers?
You might want to check out SproutCore's new experimental polymorphism support: http://groups.google.com/group/sproutcore-dev/browse_thread/thread/b63483ab66333d15
Here's some information on defining sub-classes and overriding properties and methods:
http://wiki.sproutcore.com/w/page/12412971/Runtime-Objects.
From my (limited) use of Sproutcore, I've only been able to bind 1 view to 1 controller.
As such, if you are planning to use a single view (e.g. ListView) to display your data, then I think you will only be able to bind that view to 1 controller. This means the 1 base type that is able to handle derived models seems to be the way to go.
Typically you populate the content of ArrayController instances with the results of App.store.find calls. SC.Store#find can take an SC.Query instance, which typically looks like:
MyApp.myController.set('content') = MyApp.store.find(SC.Query.local(MyApp.MyModel));
This should return all instances of MyApp.MyModel, including any instances of MyApp.MyModel's subclasses.
The first argument to SC.Query.local can either be an SC.Record subclass or a string referring to the subclass. So if you've got some intermediary SC.Record subclasses, you might want to try using them there.
Controllers should just be proxies for objects, when dealing with single instances of your model. In other words, ObjectController can proxy anything. Here is what I mean in code:
You have two objects, Person and Student.
App.Person = SC.Object.extend({
// person stuff here
})
App.Student = App.Person.extend({
// student stuff here, you have have all Person things because you are extending person.
})
You then want to define controllers:
App.personController = SC.ObjectController.create({
contentBinding: 'App.path.to.person'
})
App.studentController = SC.ObjectController.create({
contentBinding: 'App.path.to.student'
})
note that you would only bind the controller's content to something if the person/student is a result of a selection, or some other flow where bindings fire. In other words, if you set the person manually (say from a statechart, as the result of an interaction), you would still define the controller but would do
App.personController.set('content', person);
You set up the controller differently depending on whether the Person is a 'top level' object in your app, or some intermediate object that gets selected. Also, you might only need one controller, you would only have a studentController and a personController if you were acting on a person and a student at the same time. Both are just ObjectControllers, and those can proxy anything.
Finally, in your view you would bind the relevant view element to the controller:
...
nameView: SC.LabelView.design({
layout: {/* props */},
valueBinding: SC.Binding.oneWay('App.personController.name')
})
...
note that the oneway binding is if the name is not going to be changed on the view, if the view can change the name, then just do a normal binding. Also note the path here. I am not binding to
'App.personController.content.name'
Since the personController proxies the object, you bind to the
'namespace.controller.property-on-object-controller-proxies'
If you are putting a lot of business logic in your controller, you are doing it wrong. Controllers should just be for proxying objects (at least ObjectControllers should be). Business logic should be on the models themselves, and decision making logic should be in statecharts.