Wiki source code of D2W Flow Control

Version 17.1 by Antoine Berry on 2012/09/20 11:13

Show last authors
1 == Related articles ==
2
3 [[What is Direct to Web?>>What is Direct to Web]]
4 [[The D2W Rule System]]
5
6 = D2W Flow Control =
7
8 by [[~~ramsey]]
9
10 Setting up a D2W app is almost too easy. You only need to build a data model and you get an instant application. You don't have to spend any time thinking about creating lots of page components, setting up proper input validation in your view, making sure everything is localizable, ... you get all that for free. A lot of really tedious work is simply done already. It is very liberating.
11
12 Once you are unshackled by D2W, you immediately want to modify it to fit your needs. To customize the structure of your app and the components used, you only need to learn your way around the rule system. With the rule system, you can switch out practically anything with a few rule changes. Understanding the rule system and the D2WContext leads you to creating custom components to handle tasks and object properties in new ways.
13
14 It is here where you must be very careful. Good D2W components are object neutral. They don't care what EO you stick in them, they just work. If you don't understand D2W flow control, you might end up coding a lot of specialized components to do the job. If you find you are writing a component for a specific entity or set of entities and it won't work with just any entities, it's possible you may be sticking control code, model code, or both in your view layer. WebObjects is designed to have a clean separation between Model, View, and Controller: MVC. If you do mix control code in your View or Model layer, you will only waste a lot of your own time by making your app more difficult to maintain.
15
16 == Learn to wield D2W. ==
17
18 If you are just starting out with D2W and you don't understand flow control, you may feel like you are stuck on rails. You go wherever the D2W train takes you. Understanding how flow control works in a D2W app is the key to taking the reigns and steering the application in any direction you would like to go. At the most basic level, D2W flow is very simple. D2W pages have a binding called nextPage and nextPageDelegate. These two simple bindings are the entry point to the controller code you use to direct the flow of a D2W app. What generally happens in well behaved D2W view components is that the nextPageDelegate is checked first. If the delegate exists, nextPage() is called on it. If the delegate does not exist, the component falls back to nextPage() on the D2WPage component. If you are not using Wonder, you can stop reading here. Sorry, that's all you get. ;-) The rest of this article will be discussing some of the custom flow control concepts found in Project Wonder's ERDirectToWeb framework.
19
20 ==== ERDActionButton subclasses ====
21
22 The first group of flow control components I'd like to mention are simply button components that perform specific tasks. Edit, delete, inspect, select, ... these tasks, common to all enterprise objects, can be accomplished with a number of components that subclass ERDActionButton. You can drop one into your page using using the rule system. They are typically used in conjunction with the actions RHS key, but can potentially be used in displayPropertyKeys repetitions as well. By default they are loaded with ERDDefaultActionAssignment.
23
24 === Control Delegates ===
25
26 ==== ERDQueryDataSourceDelegateInterface ====
27
28 ERDQueryDataSourceDelegateInterface isn't a next page delegate. What it allows you to do is manipulate the datasource generated by an ERD2WQueryPage. If you need to apply custom control logic to the results generated by your query pages, this is the place to do it. For example, if you wanted to replace the default AND qualifier used by the query page with an OR qualifier, you could do something like this:
29
30 ##100 : entity.name = 'MyEntity' => queryDataSourceDelegate = com.example.delegates.MyQueryDataSourceDelegate WO:ERDDelayedObjectCreationAssignment##
31
32 {{noformat}}
33
34 package com.example.delegates;
35
36 import com.webobjects.eoaccess.EODatabaseDataSource;
37 import com.webobjects.eocontrol.EODataSource;
38 import com.webobjects.eocontrol.EOFetchSpecification;
39 import com.webobjects.eocontrol.EOQualifier;
40 import com.webobjects.foundation.NSArray;
41
42 import er.directtoweb.delegates.ERDQueryDataSourceDelegateInterface;
43 import er.directtoweb.pages.ERD2WQueryPage;
44
45 public class MyQueryDataSourceDelegate implements ERDQueryDataSourceDelegateInterface {
46 public EODataSource queryDataSource(ERD2WQueryPage sender) {
47 EODataSource ds = sender.dataSource();
48 if (ds == null || !(ds instanceof EODatabaseDataSource)) {
49 ds = new EODatabaseDataSource(sender.session().defaultEditingContext(), sender.entity().name());
50 sender.setDataSource(ds);
51 }
52
53 EOFetchSpecification fs = ((EODatabaseDataSource) ds).fetchSpecification();
54 fs.setQualifier(qualifierFromSender(sender));
55 fs.setIsDeep(sender.isDeep());
56 fs.setUsesDistinct(sender.usesDistinct());
57 fs.setRefreshesRefetchedObjects(sender.refreshRefetchedObjects());
58
59 int limit = sender.fetchLimit();
60 if (limit != 0) {
61 fs.setFetchLimit(limit);
62 }
63 NSArray prefetchingRelationshipKeyPaths = sender.prefetchingRelationshipKeyPaths();
64 if (prefetchingRelationshipKeyPaths != null && prefetchingRelationshipKeyPaths.count() > 0) {
65 fs.setPrefetchingRelationshipKeyPaths(prefetchingRelationshipKeyPaths);
66 }
67 return ds;
68 }
69
70 private EOQualifier qualifierFromSender(ERD2WQueryPage sender) {
71 EOQualifier q = sender.qualifier();
72 // q = ... create your OR qualifier here
73 return q;
74 }
75 }
76
77 {{/noformat}}
78
79 === NextPageDelegate ===
80
81 ==== ERDPageNameDelegate ====
82
83 The ERDPageNameDelegate is an extremely simple next page delegate, so I'll mention it briefly. To use it, just hand it a page name in its constructor and it will return that component by invoking pageWithName on the sender WOComponent. That's it. Hand it to a D2WPage's setNextPageDelegate method and you're done.
84
85 {{noformat}}
86
87 D2WPage page = ...;
88 ERDPageNameDelegate delegate = new ERDPageNameDelegate("SomePageName");
89 page. setNextPageDelegate(delegate);
90
91 {{/noformat}}
92
93 ==== ERDBranchDelegate & ERDControllerButton ====
94
95 There are several other NextPageDelegates in Wonder, but I'm only going to mention one more: My personal favorite. ERDBranchDelegate is an extremely useful next page delegate. It is also a fairly well documented delegate... if you know where to look :-) It's located in the documentation for [[ERD2WPage's pageController() method>>http://wocommunity.org/documents/javadoc/wonder/latest/er/directtoweb/pages/ERD2WPage.html#pageController()]] (bit more here: [[Page Controller in ERD2W>>http://osdir.com/ml/web.webobjects.wonder-disc/2006-05/msg00041.html]]).
96
97 What this documentation is telling you is that in your 'WebSite' entity's list page, you will see an edit button and a cogwheel button in each row of your list table. The cogwheel button is the ERDControllerButton, a special subclass of ERDActionButton discussed earlier. It will produce a menu connected to your control methods. ERDBranchDelegate finds its control methods automatically via reflection. The control methods match a signature of
98
99 {{noformat}}
100
101 public WOComponent <method-name>(WOComponent sender) {
102 //control code goes here
103 }
104
105 {{/noformat}}
106
107 The menu items, by default, will be named "Copy Web Site" and "Delete Web Site" and they will execute the control code found in copyWebSite(WOComponent sender) and deleteWebSite(WOComponent sender), respectively.
108
109 A picture is worth a thousand words though... This is a screenshot of the ERDControllerButton in use.
110
111 [[image:ERDControllerButton.png]]
112
113 In this picture, the entity is named Child and I'm using an inspectAction instead of an editAction. Clearly, the css there needs a little work as well, but ... you get the idea.
114
115 If your application uses ERModernLook, the controllerButton is hidden in the style sheet. To display it you should add a stylesheet to your application using&nbsp;[[this method (point 4)>>documentation:ERModernLook]], in which you specify how you want to display your controller button. For exemple :&nbsp;
116
117 {{code language="css"}}
118
119 span.ERDControllerButton {
120     display: block;
121 }
122
123 {{/code}}
124
125 What this documentation **doesn't** tell you is that the ERDBranchDelegate is even more powerful than that. For one, you can easily customize/localize those menu item strings. For instance, your Spanish Localizable.strings file might contain
126
127 "Button.copyWebSite" = "Copie el Web site";
128 "Button.deleteWebSite" = "Web site de la cancelación";
129
130 Also, you can use Java annotations to define which control methods are available based on scope, task, and pageConfiguration.
131
132 {{noformat}}
133
134 @D2WDelegate (
135 scope = "object", availableTasks = "inspect list", availablePages = "ListWebSite InspectWebSite"
136 )
137 public WOComponent copyWebSite(WOComponent sender) {
138 setSender(sender);
139 WOComponent result = null;
140 return result;
141 }
142
143 {{/noformat}}
144
145 The values for scope can be either object or selection, availableTasks values are your d2wContext().task() keys, and availablePages values are equivalent to the page configuration names. What do these annotations mean? They define when the control method is available. For instance, in the above example, the "object" scope means the control method is only available at the object level. If you put it into a list page, then it will be available in the list repetition of objects, but not in a control button above or below the list. The other annotations further restrict the method to the specified tasks and page configurations.
146
147 Using this technique, you could conceivably define the branching control code for an entire application in a single file As [[~~anjo]] has pointed out on the list, using annotations means that you're compiling something that really belongs in the rule system. Of course, he's right... So how do you use the rule system to define your branch choices? That's easy enough. Just use a rule like
148
149 ##100 : (task = 'list' and pageConfiguration = 'ListWebSite') => branchChoices = ("copyWebSite","deleteWebSite") WO:Assignment##
150
151 And you will get the exact same effect, but you'll get the full power and flexibility of the rule system instead. ERDBranchDelegate is an extremely clever solution :-)
152
153 ==== ERDActionBar ====
154
155 Finally, I'd like to mention the ERDActionBar. Much like the ERDControllerButton, ERDActionBar also makes use of the ERDBranchDelegateInterface to provide branching control. However, with this component, instead of providing a single menu style selection, you get a repetition of buttons. Another big difference is that ERDActionBar looks for a branch delegate on the nextPageDelegate instead of looking at the pageController RHS key.
156
157 ##100 : (entity.name = 'WebSite' and task = 'list') => nextPageDelegate = your.app.ListWebSiteController WO:ERDDelayedObjectCreationAssignment##
158
159 Also worth mentioning, the ERDControllerButton uses the pageController from the innermost ERD2WPage with a pageController, whereas the ERDActionBar goes uses the outermost NextPageDelegate that is a branch delegate. Otherwise, the two components are pretty much the same. They fire methods and localize button names identically.
160
161 One tricky part to using an ERDActionBar is that the keys you use to insert one into a page are not very consistent. For instance, with the ERD2WListPageTemplate, it's embedded in the page. There's no way to turn it off besides having no branch delegate in your page's nextPageDelegate(). With ERDQueryPageTemplate, it's inserted with the 'actionBarComponentName' RHS key. With any of the ERNeutral components, you'll need to use 'belowDisplayPropertyKeys' or 'aboveDisplayPropertyKeys' in combination with a rule for an action propertyKey and a third rule for a component name for that action propertyKey. Similar key inconsistencies exists for the ERDControllerButton... So if you're having trouble figuring out how to insert one of these components, you might just want to dig around in the page components to look for the hooks.