Last modified by David Avendasora on 2013/09/29 15:35

Show last authors
1 (% style="color: rgb(51,51,51);" %)Suppose you have a component that you would like to use in your ERModernLook application. You also want it to be available as its own Tab in the Navigation bar. There are several changes that you need to make. We will walk you through the process of integrating a standard HelloWorld.wo component into your application.
2
3 Starting with the following HelloWorld WOComponent:
4
5 {{code theme="Eclipse" language="xml" title="HelloWorld.wo"}}
6 <wo:WOForm>
7 What is your name? <wo:WOTextField value = "$username"/>
8 <wo:WOSubmitButton action="$fixUsername"/>
9 </wo:WOForm>
10 <wo:WOConditional condition = "$usernameExtended">
11 Hello <wo:WOString value = "$userNameExtended"/>
12 </wo:WOConditional>
13
14 {{/code}}
15
16 {{code language="java" title="HelloWorld.java" linenumbers="true"}}
17 import com.webobjects.appserver.WOContext;
18 import er.extensions.components.ERXComponent; 
19
20 public class HelloWorld extends ERXComponent {
21
22 private String username;
23 private String userNameExtended;
24
25 public HelloWorld(WOContext context) {
26 super(context);
27
28
29 public ERXComponent fixUserName() {
30 //create a new component, which will be sent to the user with it's name appended
31 HelloWorld aPage = pageWithName(HelloWorld.class);
32  
33 // we do not like WOBers without the name David
34 String extendedUserName = "";
35 if (username() != null) {
36 extendedUserName = "David-" + username() + " (There is no WOBber that doesn't have David in his firstname)";
37 } else {
38 extendedUserName = "Please fill in some name";
39 }
40
41 //set the User Name on the new page
42 aPage.setUserNameExtended(extendedUserName);
43 return aPage;
44 }
45
46 public String username() {
47 return username;
48 }
49 public void setUsername(String username) {
50 this.username = username;
51 }
52
53 public String userNameExtended() {
54 return userNameExtended;
55 }
56
57 public void setUserNameExtended(String userNameExtended) {
58 this.userNameExtended = userNameExtended;
59 }
60  }
61
62 {{/code}}
63
64 This component takes a username and then "corrects" it to be a more socially-acceptable and, who are we kidding, much cooler one.
65
66 First we will add it to the **NavigationMenu.plist** so that it will show up in the navigation bar as its own tab:
67
68 {{code title="NavigationMenu.plist entry"}}
69 (
70
71 name = Root; 
72 children = ("Home","HelloWorld");
73 },
74 {
75 name = "Home";
76 action = "session.navController.homeAction";
77 }
78
79 name = "HelloWorld"; 
80 action = "HelloWorld"; 
81 },
82 )
83 {{/code}}
84
85 (% style="color: rgb(51,51,51);" %)Notice that we added it as an additional element of the "children" array binding of the "Root" entry, and as its own entry.
86
87 (% style="color: rgb(51,51,51);" %)However, If you just do that clicking on the HelloWorld tab of your application will result in an error something like this:
88
89 |=(((
90 (% style="color: rgb(51,51,51);" %)**Error:**
91 )))|(((
92 {{{[&#x3c;er.extensions.appserver.navigation.ERXNavigationMenuItem name: er.extensions.appserver.navigation.ERXNavigationMenuItem subcomponents: null &#x3e; valueForKey()]: }}}
93
94 {{{lookup of unknown key: 'helloWorld'. The WOComponent er.extensions.appserver.navigation.ERXNavigationMenuItem does not have }}}
95
96 {{{an instance variable of the name HelloWorld or _HelloWorld, nor a method of the name HelloWorld, _HelloWorld, getHelloWorld, or _getHelloWorld}}}
97 )))
98
99 (% style="color: rgb(51,51,51);" %)The reason is that your navigation controller must have a method that tells it what to do when you click on the tab in the navigation bar. The "action" binding in the **NavigationMenu.plist** is the name of the method that it will try to execute.
100
101 {{code language="java" title="MainNavigationController#helloWorld() method referenced by the ~"action~" key in the plist"}}
102 public ERXComponent helloWorld() {
103 ERXComponent nextPage = (HelloWorld)  myApp().pageWithName(HelloWorld.class.getName(), session().context());
104 return nextPage;
105 }
106 {{/code}}
107
108 However, you are still going to get the following error unless you make sure that the action binding is actually correct.
109
110 |=(((
111 (% style="color: rgb(51,51,51);" %)Error:
112 )))|(((
113 {{{[&#x3c;er.extensions.appserver.navigation.ERXNavigationMenuItem name: er.extensions.appserver.navigation.ERXNavigationMenuItem subcomponents: null &#x3e; valueForKey()]: }}}
114
115 {{{lookup of unknown key: 'helloWorld'. The WOComponent er.extensions.appserver.navigation.ERXNavigationMenuItem does not have }}}
116
117 {{{an instance variable of the name HelloWorld or _HelloWorld, nor a method of the name HelloWorld, _HelloWorld, getHelloWorld, or _getHelloWorld}}}
118 )))
119
120 We need to tell it explicitly to call //session().navigationController().helloWorld()//
121
122 {{code}}
123 {
124 name = "HelloWorld";
125 action = "session.navController.helloWorld";
126 },
127
128 {{/code}}
129
130 (% style="color: rgb(51,51,51);" %)And indeed, we do get a response, but not what we expected:
131
132 [[image:attach:NoMenu.png]]
133 (% style="color: rgb(51,51,51);" %)Where is the header and the navigation bar? Those are provided by the **PageWrapper** component so we will need to add that to our component:
134
135 {{code language="xml" title="Add the PageWrapper component"}}
136 <wo:PageWrapper>
137 <wo:WOForm>
138 What is your name? <wo:WOTextField value = "$username"/>
139 <wo:WOSubmitButton action="$fixUsername"/>
140 </wo:WOForm>
141 <wo:WOConditional condition = "$usernameExtended">
142 Hello <wo:WOString value = "$userNameExtended"/>
143 </wo:WOConditional>
144 </wo:PageWrapper>
145
146 {{/code}}
147
148 This should create all the menu's etc. Unfortunately, running this will result in the error:
149
150 |=(((
151 Error:
152 )))|(((
153 {{{java.lang.NullPointerException at PageWrapper.bodyClass(PageWrapper.java:25) }}}
154 )))
155
156 The reason is that **PageWrapper** needs the component that it is wrapping to have a **D2WContext**. **ERXComponent** does not have a **D2WContext**. So we change the **HelloWorld** class to **ERD2WPage**:
157
158 {{code language="java" firstline="4" linenumbers="true"}}
159 public class HelloWorld extends ERD2WPage {
160
161 {{/code}}
162
163 (% style="color: rgb(51,51,51);" %)That, unfortunately results in this error:
164
165 |=(((
166 Error:
167 )))|(((
168 {{{java.lang.NullPointerException at er.directtoweb.pages.ERD2WPage.appendToResponse(ERD2WPage.java:773)}}}
169 )))
170
171 The reason is that a page should be constructed via **D2W.factory()** instead of **myApp().pageWithName(String, WOContext)**. Looking at the code, you will find that line 644 tries to get something out of d2wcontext, which only gets created via the D2WFactory:
172
173 {{code}}
174 String info = "(" + d2wContext().dynamicPage() + ")";
175
176 {{/code}}
177
178 (% style="color: rgb(51,51,51);" %)But the D2wFactory creates pages via rules, if you do not add a couple of rules you will get this error:
179
180 |=(((
181 (% style="color: rgb(51,51,51);" %)**Error:**
182 )))|(((
183 {{{java.lang.IllegalStateException: Couldn't find the dynamic page named HelloWorld in your DirectToWeb model.task and entity is null, it seems that one model, maybe ERDirectToWeb d2w.d2wmodel is not loaded!}}}
184 )))
185
186 (% style="color: rgb(51,51,51);" %)The reason is that every DirectToWebpage should have an "entity", and a "task". And you have to make sure that the templateNameFor[WO:task]Page for that task points to your component:
187
188 {{code language="text" title="user.d2wmodel additions" linenumbers="true"}}
189 pageConfiguration = 'HelloWorld' => entity = "Contacten" [WO:com.webobjects.directtoweb.EntityAssignment]
190 pageConfiguration = 'HelloWorld' => task = "inspect" [WO:com.webobjects.directtoweb.Assignment]
191 pageConfiguration = 'HelloWorld' => templateNameForInspectPage = "HelloWorld" [WO:com.webobjects.directtoweb.Assignment]
192 {{/code}}
193
194 (% style="color: rgb(51,51,51);" %)
195
196 [[image:attach:HelloWorldWrongTab.png]]
197 (% style="color: rgb(51,51,51);" %)We are nearly there. We just have to fix the navigation so the correct tab appears as selected, which we can do with a "navigationState" rule:
198
199 (% style="color: rgb(51,51,51);" %)
200
201 {{code language="text" title="~"navigationState~" D2W Rule"}}
202 pageConfiguration = 'HelloWorld' => navigationState = "HelloWorld" [WO:com.webobjects.directtoweb.Assignment]
203
204 {{/code}}
205
206 [[image:attach:HelloWorldRightTab.png]]
207
208 (% style="color: rgb(51,51,51);" %)And a submit will lead to:
209
210 [[image:attach:InternalError.png]]
211 (% style="color: rgb(51,51,51);" %)The reason is that all the responses in the component should also be created as a D2WPage. Our action fixUsername() created a WOComponent but not in the D2WFactory.
212
213 (% style="color: rgb(51,51,51);" %)We fix that by changing how the page is created:(%%) 
214
215 {{code language="java" title="Use the D2W#factory() to create the page" firstline="15"}}
216 HelloWorld aPage = (HelloWorld)  D2W.factory().pageForConfigurationNamed("HelloWorld", session());
217 {{/code}}
218
219 (% style="color: rgb(51,51,51);" %)And there we go:
220
221 [[image:attach:HelloWorldCorrect.png]]
222 (% style="color: rgb(51,51,51);" %)
223
224 == Finished Code: ==
225
226 {{code language="java" title="HelloWorld.java" linenumbers="true"}}
227 import com.webobjects.appserver.WOContext;
228 import com.webobjects.directtoweb.D2W;
229 import er.directtoweb.pages.ERD2WPage;
230
231 public class HelloWorld extends ERD2WPage {
232
233 private String username;
234 private String userNameExtended;
235
236 public HelloWorld(WOContext context) {
237          super(context);
238 }
239
240 public ERD2WPage fixUserName() {
241 //create a new component, which will be sent to the user with it's name appended
242     HelloWorld aPage = (HelloWorld)  D2W.factory().pageForConfigurationNamed("HelloWorld", session());
243 // we do not like WOBers without the name David
244     String extendedUserName = "";
245      if (username() != null) {
246 extendedUserName = "David-" + username() + "(There is no WOBber that doesn't have David in his firstname)";
247     } else {
248 extendedUserName = "Please fill in some name";
249 }
250 //set the User Name on the new page
251 aPage.setUserNameExtended(extendedUserName);
252 return aPage;
253 }
254
255 public String username() {
256 return username;
257 }
258
259 public void setUsername(String username) {
260 this.username = username;
261 }
262
263 public String userNameExtended() {
264 return userNameExtended;
265 }
266
267 public void setUserNameExtended(String userNameExtended) {
268 this.userNameExtended = userNameExtended; 
269 }
270 }
271 {{/code}}
272
273 {{code language="xml" title="HelloWorld.wo"}}
274 <wo:PageWrapper>
275 <wo:WOForm>
276 What is your name? <wo:WOTextField value = "$username"/>
277 <wo:WOSubmitButton action="$fixUsername"/>
278 </wo:WOForm>
279 <wo:WOConditional condition = "$usernameExtended">
280 Hello <wo:WOString value = "$userNameExtended"/>
281 </wo:WOConditional>
282 </wo:PageWrapper>
283 {{/code}}
284
285 {{code language="text" title="Additional user.d2wmodel entries"}}
286 pageConfiguration = 'HelloWorld' => entity = "Contacten" [WO:com.webobjects.directtoweb.EntityAssignment]
287 pageConfiguration = 'HelloWorld' => task = "inspect" [WO:com.webobjects.directtoweb.Assignment]
288 pageConfiguration = 'HelloWorld' => templateNameForInspectPage = "HelloWorld" [WO:com.webobjects.directtoweb.Assignment]
289 pageConfiguration = 'HelloWorld' => navigationState = "HelloWorld" [WO:com.webobjects.directtoweb.Assignment]
290 {{/code}}
291
292 {{code title="Additional entry in NavigationMenu.plist"}}
293 {
294 name = "HelloWorld";
295 action = "session.navController.helloWorld";
296 }
297
298 {{/code}}
299
300 {{code language="java" title="Additional method in MainNavigationController.java"}}
301 public WOComponent helloWorld() {
302 WOComponent nextPage = D2W.factory().pageForConfigurationNamed("HelloWorld", session());
303 return nextPage;
304 }
305 {{/code}}