I'm still learning GWT, yet already have to face some kind of challenge for a work I have to do. Can't show any specific code so I'll try to explain it well.
Here's the situation: A certain class "Navigator" creates and save the Presenter instances of my architecture to allow reusing them. There is a method show() inside that same class that actually displays the view related but that system only works full screen by calling RootPanel.get().
What i'd like to do is showing that presenter instance's view inside of a flex panel element declared in a class myView (related to a class myPresenter) that basically uses Flex Panel to structure it's content.
To make it maybe more clear:
class myView{
...
flexPanel.setWidget(firstWIdget)
flexPanel.setWidget(secondWidget) //secondWidget to be replaced by a "thirdWidget"
...
}
I'd like the secondWidget to be replaced by another one, let's call it thirdWidget, that consists of a specific presenter instance's view.
To resume, I'd like my presenter instance's view to not go full screen but only occupy a certain area of the screen.
The displaying is managed almost entirely programmatically, means very limited use of css files and no use at all of xml ui files.
How can I manage this ?
Thanks
Use a SimplePanel as a container for your views returned by your Navigation class instead of adding them directly to root panel, and use that instance of SimplePanel where ever you want.
I work in a team of 25 developers. We use ExtJS MVC pattern of Sencha. But we believe that their definition of MVC is misleading. Maybe we might call their MVC an anti-pattern too.
AMAIK, in MVC controller only knows the name or the path of the view, and has no knowledge on the view's internal structure. For example, it's not the responsibility of the controller, whether view renders the list of customers a simple drop down, or an auto-complete.
However, in Ext JS's MVC, controller should know the rendering of view's elements, because controller hooks into those elements, and listens to their events. This means that if an element of the view change (for example a button become a link), then the relevant selector in the controller should change too. In other words, controller is tightly-coupled to the internal structure of the view.
Is this reason acceptable to denounce Ext JS's MVC as anti-pattern? Are we right that controllers are coupled to views?
UPDATE (March 2015): Ext 5.0 introduced ViewControllers that should address most of the concerns discussed in this thread. Advantages:
Better/enforced scope around component references inside the ViewController
Easier to encapsulate view-specific logic separately from application flow-control logic
ViewController lifecycle managed by the framework along with the view it's associated with
Ext 5 still offers the existing Ext.app.Controller class, to keep things backwards-compatible, and to give more flexibility for how to structure your application.
Original answer:
in Ext JS's MVC, controller should know the rendering of view's elements, because controller hooks into those elements, and listens to their events. This means that if an element of the view change (for example a button become a link), then the relevant selector in the controller should change too. In other words, controller is tightly-coupled to the internal structure of the view.
I actually agree that in most cases this is not the best choice for the exact reasons you cite, and it's unfortunate that most of the examples that ship with Ext and Touch demonstrate refs and control functions that are often defined using selectors that violate view encapsulation. However, this is not a requirement of MVC -- it's just how the examples have been implemented, and it's easy to avoid.
BTW, I think it definitely can make sense to differentiate controller styles between true application controllers (control app flow and shared business logic, should be totally uncoupled from views -- these are what you're referring to), and view controllers (control/event logic specific to a view, tightly-coupled by design). Example of the latter would be logic to coordinate between widgets within a view, totally internally to that view. This is a common use case, and coupling a view-controller to its view is not an issue -- it's simply a code management strategy to keep the view class as dumb as possible.
The problem is that in most documented examples, every controller simply references whatever it wants to, and that's not a great pattern. However, this is NOT a requirement of Ext's MVC implementation -- it is simply a (lazy?) convention used in their examples. It's quite simple (and I would argue advisable) to instead have your view classes define their own custom getters and events for anything that should be exposed to application controllers. The refs config is just a shorthand -- you can always call something like myView.getSomeReference() yourself, and allow the view to dictate what gets returned. Instead of this.control('some > view > widget') just define a custom event on the view and do this.control('myevent') when that widget does something the controller needs to know about. Easy as that.
The drawback is that this approach requires a little more code, and for simple cases (like examples) it can be overkill. But I agree that for real applications, or any shared development, it's a much better approach.
So yes, binding app-level controllers to internal view controls is, in itself, an anti-pattern. But Ext's MVC does not require it, and it's very simple to avoid doing it yourself.
I use ExtJS 4's MVC everyday. Rather than spaghetti code, I have an elegant MVC app that has tightly defined separation of concens and is ridiculously simple to maintain and extend. Maybe your implementation needs to be tweaked a bit to take full advantage of what the MVC approach offers.
Of course the controllers are bound to the views in some way. You need to target exactly which elements in your views you want to listen to.
eg: listen to that button clicks or to that form element change or to that custom component/event.
The goal of MVC is components decoupling and reusability and the Sencha MVC is awesome for that. As #bmoeskau says, you have to be careful in separation of the view controllers (builtin for the view/widgets itself) and the application controllers (top level views manipulations) to take full advantage of the MVC pattern. And this is something not obvious when your read http://docs.sencha.com/ext-js/4-1/#!/guide/application_architecture. Refactor your MVC approach, create different controllers, create custom component, and embrace the full ExtJS MVC architecture to take advantage of it.
There's still a slight problem in Sencha approach IMHO, the MVC refs system doesnt really work when you have multiple instances of the same views in an Application. eg: if you have a TabPanel with multiple instances of the same Component, the refs system is broken as it will always target the first element found in the DOM... There are workarounds and a project trying to fix that but i hope this will be adressed soon.
I'm currently undergoing the Fast Track to ExtJS 4 from Sencha Training. I have a strong background in ExtJS (since ExtJS 2.0) and was very curious to see how the MVC was implemented in ExtJS 4.
Now, previously, the way I would simulate kind of a Controller, would be to delegate that responsibility to the Main Container. Imagine the following example in ExtJS 3:
Ext.ns('Test');
Test.MainPanel = Ext.extend(Ext.Container, {
initComponent : function() {
this.panel1 = new Test.Panel1({
listeners: {
firstButtonPressed: function(){
this.panel2.addSomething();
},
scope: this
}
});
this.panel2 = new Test.Panel2();
this.items = [this.panel1,this.panel2];
Test.MainPanel.superclass.initComponent.call(this);
}
});
Test.Panel1 = Ext.extend(Ext.Panel, {
initComponent : function() {
this.addEvents('firstButtonPressed');
this.tbar = new Ext.Toolbar({
items: [{
text: 'First Button',
handler: function(){
this.fireEvent('firstButtonPressed');
}
}]
});
Text.Panel1.superclass.initComponent.call(this);
}
});
Test.Panel2 = Ext.extend(Ext.Panel, {
initComponent : function() {
this.items = [new Ext.form.Label('test Label')]
Test.Panel2.superclass.initComponent.call(this);
},
addSomething: function(){
alert('add something reached')
}
});
As you can see, my MainPanel is (besides the fact that is holding both panels) also delegating events and thus creating a communication between the two components, so simulating sort of Controller.
In ExtJS 4 there is MVC directly implemented in it. What really striked me was that the way the Controller actually fetches the components is through QuerySelector which in my opinion is very prone to error. Let's see:
Ext.define('MyApp.controller.Earmarks', {
extend:'Ext.app.Controller',
views:['earmark.Chart'],
init:function () {
this.control({
'earmarkchart > toolbar > button':{
click:this.onChartSelect
},
'earmarkchart tool[type=gear]':{
click:this.selectChart
}
});
}
});
So as we can see here, the way the Controller is aware of the earmarkchart button and tool is through selectors. Let's imagine now that I am changing the layout in my earmarkchart and I actually move the button outside of the toolbar. All of a sudden my application is broken, because I always need to be aware that changing the layout might have impact on the Controller associated with it.
One might say that I can then use itemId instead, but again I need to be aware if I delete a component I will need to scatter to find if there is any hidden reference in my Controllers for that itemId, and also the fact that I cannot have the same itemId per parent Component, so if I have an itemId called 'testId' in a Panel1 and the same in a Grid1 then I would still need to select if I want the itemId from Panel1 or from the Grid1.
I understand that the Query is very powerful because it gives you a lot of flexibility, but that flexibility comes at a very high price in my opinion, and if I have a team of 5 people developing User Interfaces and I need to explain this concepts I will put my hands on the fire that they will make tons of mistakes because of the points I referenced before.
What's your overall opinion on this? Would it be easier to just somehow communicate with events? Meaning if my Controller is actually aware of what views he's expecting events, then one could just fire an event dosomethingController and the associated Controller would get it, instead of all this Query problem.
I think if you use the Sencha Architect to produce the Views then Inherit from that View to create Your own View.
Now this View Can be responsible to hook up to any events and raise meaningful events.
This is just a thought...
//Designer Generated
Ext.define('MyApp.view.MainView', {
extend: 'Ext.grid.GridPanel',
alias: 'widget.mainview',
initComponent: function() {
}
});
//Your View Decorator
Ext.define('MyApp.view.MainView', {
extend: 'MyApp.view.MainViewEx',
alias: 'widget.mainviewex',
initComponent: function() {
this.mon(this, 'rowselect', function(){
this.fireEvent('userselected', arguments);
}, this);
}
});
I think there is a pretty bad problem here - its very difficult to shard isolated units within a page.
The approach I'm experimenting with (which makes it somewhat easier to write tests aswell) is to have a vanilla js context object for each page which contains the logic (and has the benefit of being very easy to break up and delegate to different objects). The controllers then basically call methods on the context objects when they receive events and have methods on them for retrieving bits of the view or making changes to the view.
I'm from a wpf background and like to think of the controllers as code-behind files. The dialog between presenter/context and view is a lot chattier than wpf (since you dont have binding + data templating) but its not too bad.
Theres also a further problem I haven't had to solve yet - the use of singletons for controllers causes problems for reuse of UI elements on the same page. That seems like a serious design flaw. The common solution I've seen is (yet again) to pile everything into one file and in this case to ditch the controller altogether. Clearly that's not a good approach as soon as things start to get complicated.
It seems like getting all the state out of the controller should help though and you'd then have a second level of context objects - the top level one would basically just assign a unique id to each view and have a map of context=>view and provide dispatch to the individual context methods - it'd basically be a facade. Then the state for each view would be dealt with in the objects dispatched to. A good example of why statics are evil!
I have a viewmodel tied to a view used in a region. I'm trying to find a way that when that view is navigated to from a particular view (say view A), it does some work internally, like initializing some lists, setting some stuff, whatever. But if it has been navigated to from view B, it needs to NOT reinitialize everything, and just display the data it already has.
I could pass a parameter I suppose, saying whether this is a new operation or if we are going back to work on the old one, but I thought it would be nicer to be able to state that if we came from this view, we do one thing, and if we came from that one we do another.
If that makes sense :)
You can implement the INavigationAware interface which contains 3 methods. One of these methods is the OnNavigatedTo method. There you can access the journal and check the current entry. From there you should be able to determine if it came from View A or View B.
public void OnNavigatedTo(NavigationContext navigationContext)
{
var journal = navigationContext.NavigationService.Journal;
//use journal.CurrentEntry
}
A WP 7.1 project starts with a Page that host a Panorama control. At some point user click on a ListBox, and this navigates the application to a details page.
In case the debugger is attached, everything stays on the screen as it should. But If I test an application either in emulator, or on the phone without a debugger, approximately in 5-10 seconds after the details page navigation, an application gets deactivated.
No unhanded exception, not closing, but deactivated even is raised. I have placed a message boxes in each of "exit handlers" to know exactly what happens and found out that it is deactivation.
No user input takes place after navigation and before the deactivation.
What may be the reason for such "no interaction" deactivation?
I don't call no "deactivate" requests from code.
Additional info:
Details page is bound to a sample view model that is obtained via MVVM Light ViewModel locator. View model locator gets it from ninject kernel that is a static public property of an App object(Yes, I have made IOC container publicly available via App property. I know it probably is a horrible practice, but I doubt the problem is linked to that). The page initializes just fine and displays all the data from a sample view model class. It almost seems like an app is deactivated due to inactivity, but there is no such thing in WP7 as far as I know.
UPDATE
A deactivation takes place exactly 10 seconds afer I call this line:
((PhoneApplicationFrame)(Application.Current.RootVisual)).Navigate(new Uri("/Views/BookDetailsView.xaml", UriKind.Relative));
from a view model of a main application view. The problem view is a details view, not the main one.
The constructor for BookDetailsView is empty (default):
public partial class BookDetailsView : UserControl
{
public BookDetailsView()
{
InitializeComponent();
}
}
The XAML for the view binds it's datacontext to a property of a mvvm light view model locator:
DataContext="{Binding Source={StaticResource Locator}, Path=BookDetails}"
The Locator resource is decleared in App.xaml and points to ViewModelLocator.cs.
The property that provides datacontext for a problem view is:
public static IBookDetailsViewModel BookDetailsStatic
{
get;
set;
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public IBookDetailsViewModel BookDetails
{
get
{
return BookDetailsStatic;
}
}
The BookDetailsStatic is initialized from the IoC container call in the ViewModelLocator ctor:
BookDetailsStatic = App.Kernel.Get<IBookDetailsViewModel>();
In a any scenario the call to get an IBookDetailsViewModel returns an instance of a
public class SampleBookDetailsViewModel: IBookDetailsViewModel
which has an empty constructor and a bunch of properties.
SOLVED
My view, that I have been navigating to, was declared as a UserControl, and should have been as PhoneApplicationPage.
Hard to say without knowing what's on the page, but you could be hitting the memory limit.
In general, you can consider the memory limit to be 90mb, but you're better off checking DeviceStatus.ApplicationMemoryUsageLimit and DeviceStatus.ApplicationCurrentMemoryUsage and possibly displaying it on screen every half second or so to debug.
You can also try the profiler, assuming it doesn't affect the repro.
BookDetailsView was decleared as a UserControl.
Navigating to a UserControl deactivates an application in 10 seconds.
Changing the type of a view to PhoneApplicationPage solves the problem.
I have a region named "ActiveModule" and want to re-use it with different views, for example you press the search button and I show the search view in there, etc. etc.
The only way I can ATM do that is to deactivate all the active views in that region and then activate the view I like, this is a bit dirty, is there a "viewManager" or something similar I can use?
If your region is a ContentControl or derives from ContentControl, then there can only be one active view at a time, and you only need to activate the search view on the region.
Did you consider to use a type of contentControl that is able to show multiple views?
For example you can use a TabControl like this:
<TabControl Name="MainRegion" Regions:RegionManager.RegionName="MainRegion"/>
You can now add more than one view to the region. Use INavigationAware and IActiveAware interfaces from Prism to be able to do navigation on the views (activate them, find the correct view etc.).
If you are using a IRegionManager, you can remove all of the views whose types you recognize and then add your own.
foreach (var view in _regionsManager.Regions["MyRegion"].Views.ToArray())
{
if (view is MyType ||
view is MyOtherType)
_regionsManager.Regions["MyRegion"].Remove(view);
}
_regionsManager.AddToRegion("MyRegion", typeof(MyView));
Its by no means ideal, but it works. :)
To my knowledge what you are doing is the only way, theoretically in SCSF the top most view was activated by the framework. You could create ur own ViewManager or a ShowViewService equivalent to get this done. MAtter of fact, thats what i have done!
Not sure how you laid out your framework but if you are using navigation related framework you can simply call
regionManager.RequestNavigate(RegionNames.MainContentRegion, new Uri("your target view" + parameters, UriKind.Relative));
the above line will take care of deactivating other views in the region.
Otherwise if you do view discovery or view injection you can use the approach here
region.Activate(view);