|
1 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|
2 <html lang="en" xml:lang="en"> |
|
3 <head> |
|
4 <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> |
|
5 <meta name="copyright" content="(C) Copyright 2005" /> |
|
6 <meta name="DC.rights.owner" content="(C) Copyright 2005" /> |
|
7 <meta content="concept" name="DC.Type" /> |
|
8 <meta name="DC.Title" content="Feed updates" /> |
|
9 <meta scheme="URI" name="DC.Relation" content="WRTKit_RSS_Reader_Tutorial-GUID-678d197f-c7b0-4e5e-85e2-f8549c75bbe8.html" /> |
|
10 <meta content="XHTML" name="DC.Format" /> |
|
11 <meta content="GUID-25CD0E54-0516-4469-965E-C5781CF44DC9" name="DC.Identifier" /> |
|
12 <meta content="en" name="DC.Language" /> |
|
13 <link href="commonltr.css" type="text/css" rel="stylesheet" /> |
|
14 <title> |
|
15 Feed updates</title> |
|
16 </head> |
|
17 <body id="GUID-25CD0E54-0516-4469-965E-C5781CF44DC9"><a name="GUID-25CD0E54-0516-4469-965E-C5781CF44DC9"><!-- --></a> |
|
18 |
|
19 |
|
20 |
|
21 <h1 class="topictitle1"> |
|
22 Feed updates</h1> |
|
23 |
|
24 <div> |
|
25 |
|
26 <div class="section"><h2 class="sectiontitle"> |
|
27 Fetching news items</h2> |
|
28 |
|
29 |
|
30 <p> |
|
31 |
|
32 Before we can show any news items we have to fetch them. So let's take |
|
33 a break in implementing the user interface and write the code that fetches |
|
34 the news items. The majority of that code is in the FeedUpdateBroker.js file |
|
35 in a JavaScript class called FeedUpdateBroker. We'll only be using one single |
|
36 method from that class: fetchFeed(). But how do we hook that up to our widget? |
|
37 </p> |
|
38 |
|
39 <p> |
|
40 |
|
41 The actual call to fetchFeed() will be done from the timer function updateFeedTimerFunc() |
|
42 that runs once a second. There we'll need an if-clause that checks if it's time |
|
43 to update the feeds, if another update is already going on, if we have a valid URL |
|
44 that we can fetch and if we're in the main view. If all of these tests tell us |
|
45 that it's time to update the feed, we'll check if the feed update was manually |
|
46 commanded or automatically triggered because it was time to update the feed in |
|
47 the background. If this is a manual update then we'll popup a progress notification |
|
48 dialog. Next we'll create a new instance of the FeedUpdateBroker class and call |
|
49 the fetchFeed() method to start the AJAX-based RSS feed update. We need to pass |
|
50 two arguments to this method: the feed URL that we have in the feedURL variable |
|
51 and a callback function that should be called when the update is completed. |
|
52 </p> |
|
53 |
|
54 <p> |
|
55 |
|
56 We don't have that function yet so that's the first thing we need to implement. |
|
57 We'll leave it empty for now but return to it in a bit: |
|
58 </p> |
|
59 |
|
60 <pre> |
|
61 |
|
62 // Callback function that gets called when a feed update has completed. |
|
63 function feedUpdateCompleted(event) { |
|
64 } |
|
65 </pre> |
|
66 |
|
67 <p> |
|
68 |
|
69 After we call the fetchFeed() function we'll schedule the next feed update. In |
|
70 theory we shouldn't do this until the feed update completes since there is a risk |
|
71 that we do two updates at the same time. But because we're adding a check to |
|
72 only do feed updates if another update isn't on-going this risk isn't actually |
|
73 real. Now that we have a plan for how to update the feed, let's write the code. |
|
74 First we need to declare a variable for the feed update broker instance that |
|
75 we'll create: |
|
76 </p> |
|
77 |
|
78 <pre> |
|
79 |
|
80 // Feed update broker. |
|
81 var feedUpdateBroker = null; |
|
82 </pre> |
|
83 |
|
84 <p> |
|
85 |
|
86 Then the actual implementation for the updateFeedTimerFunc() timer function: |
|
87 </p> |
|
88 |
|
89 <pre> |
|
90 |
|
91 // Timer function for feed updates - called once every second. |
|
92 function updateFeedTimerFunc() { |
|
93 var now = new Date().getTime(); |
|
94 |
|
95 // check if a feed update has been scheduled, if it's time to update now, |
|
96 // and if there's no update currently in progress and if we're in the main view |
|
97 if ((feedURL != null) && |
|
98 (feedUpdateTime != -1) && |
|
99 (now > feedUpdateTime) && |
|
100 (feedUpdateBroker == null) && |
|
101 (uiManager.getView() == mainView)) { |
|
102 // show progress dialog if this is a commanded feed update |
|
103 if (feedUpdateCommanded) { |
|
104 // no auto hiding, wait-type notification, unknown progress |
|
105 uiManager.showNotification(-1, "wait", "Loading feed...", -1); |
|
106 } |
|
107 |
|
108 // fetch the feed from the specified URL |
|
109 feedUpdateBroker = new FeedUpdateBroker(); |
|
110 feedUpdateBroker.fetchFeed(feedURL, feedUpdateCompleted); |
|
111 |
|
112 if (feedUpdateFrequency != -1) { |
|
113 // schedule next update |
|
114 feedUpdateTime = now + feedUpdateFrequency; |
|
115 } else { |
|
116 // feed update frequency is "never" |
|
117 feedUpdateTime = -1; |
|
118 } |
|
119 } |
|
120 } |
|
121 </pre> |
|
122 |
|
123 <p> |
|
124 |
|
125 The progress dialog is created so that it has a display time of -1 so that |
|
126 it doesn't automatically hide but rather has to be commanded to go away. The |
|
127 notification dialog type is "wait" and we'll give a progress of -1 since we |
|
128 don't know how far along in the feed updating process we are. A negative |
|
129 progress value like -1 means "unknown progress" and will result in an animated |
|
130 progress indicator that shows that something is going on but the exact time |
|
131 that it will take is unknown. |
|
132 </p> |
|
133 |
|
134 <p> |
|
135 |
|
136 Our feed updating should now be working and whenever a feed update is completed |
|
137 there should be a call to the empty feedUpdateCompleted() function. Let's |
|
138 continue the implementation there. |
|
139 </p> |
|
140 |
|
141 <p> |
|
142 |
|
143 When the feedUpdateCompleted() function is called it receives an event object |
|
144 from the FeedUpdateBroker. This object contains a status that is either "ok" |
|
145 or "error", a "lastModifiedTime" string that contains the time when the RSS |
|
146 feed was last modified to help us decide if there are any new news items to |
|
147 display, as well as an array of news item objects in a property called "items". |
|
148 If the status is "error" then we'll show an error notification dialog. Keep in |
|
149 mind that there might or might not be a progress dialog already showing at this |
|
150 time. Either way we can just call the showNotification() function in the user |
|
151 interface manager because if another dialog is already visible then it will simply |
|
152 replace it with the one that we asked it to show. If the status is "ok" then we'll |
|
153 call hideNotification() in the user interface manager. This will hide the progress |
|
154 dialog if it's showing and if the dialog wasn't showing then the call will just |
|
155 be ignored. |
|
156 </p> |
|
157 |
|
158 <p> |
|
159 |
|
160 We'll then need to compare the lastModifiedTime against the last news feed that |
|
161 we have. That means we'll need to track the lastModifiedTime of whatever news |
|
162 feed that we are showing, and that means we need a new global variable: |
|
163 </p> |
|
164 |
|
165 <pre> |
|
166 |
|
167 // Time when the feed was last modified. |
|
168 var feedLastModified = null; |
|
169 </pre> |
|
170 |
|
171 <p> |
|
172 |
|
173 If the lastModifiedTime is different from the one that we are storing in the |
|
174 feedLastModified variable then we know that there's new news items to show. |
|
175 If this is the case then we'll update the feedLastModified time and set the news |
|
176 items to the main view. We could do something fancy and only update the news items |
|
177 that have been modified but to keep the tutorial simple we'll just simply remove |
|
178 all the current items and replace them with the new items and then focus the first |
|
179 of the items. But before we move on to that we'll write as much as we can of the |
|
180 feedUpdateCompleted() function: |
|
181 </p> |
|
182 |
|
183 <pre> |
|
184 |
|
185 // Callback function that gets called when a feed update has completed. |
|
186 function feedUpdateCompleted(event) { |
|
187 if (event.status == "ok") { |
|
188 // if there aren't any feed items yet, we'll hide the progress dialog |
|
189 if (feedUpdateCommanded) { |
|
190 uiManager.hideNotification(); |
|
191 } |
|
192 |
|
193 // check if the feed has updated |
|
194 if (event.lastModified != feedLastModified) { |
|
195 // remember the last modified timestamp |
|
196 feedLastModified = event.lastModified; |
|
197 |
|
198 // update news item controls here |
|
199 } |
|
200 } else { |
|
201 // show error message |
|
202 uiManager.showNotification(3000, "warning", "Error while updating feed!<br/>(check network settings)"); |
|
203 } |
|
204 |
|
205 // null the broker reference to indicate that there's no current |
|
206 // update in progress |
|
207 feedUpdateBroker = null; |
|
208 |
|
209 // reset commanded feed update flag |
|
210 feedUpdateCommanded = false; |
|
211 } |
|
212 </pre> |
|
213 |
|
214 <p> |
|
215 |
|
216 We wrote a comment "update news item controls here" at the spot where we will |
|
217 actually create and add news feed items to the main view. We'll replace that in |
|
218 a bit with the actual code to do the job, but first we need to write some code |
|
219 that we'll need to do that. |
|
220 </p> |
|
221 |
|
222 </div> |
|
223 |
|
224 <div class="section"><h2 class="sectiontitle"> |
|
225 Showing news items</h2> |
|
226 |
|
227 |
|
228 <p> |
|
229 |
|
230 The news feed items will be shown using ContentPanel controls. We'll need a way |
|
231 to keep track of the ones we're showing so that we can easily remove them when |
|
232 there is new news items to show. Let's create an array to track them: |
|
233 </p> |
|
234 |
|
235 <pre> |
|
236 |
|
237 // Reference to current feed items controls. |
|
238 var feedItemControls = []; |
|
239 </pre> |
|
240 |
|
241 <p> |
|
242 |
|
243 Now we can implement a function that will remove all controls that are tracked |
|
244 by this array from the main view. We'll use this as the first step when we want |
|
245 to display news items. |
|
246 </p> |
|
247 |
|
248 <pre> |
|
249 |
|
250 // Removes feed items. |
|
251 function removeFeedItems() { |
|
252 // remove all current feed items from the main view |
|
253 for (var i = 0; i < feedItemControls.length; i++) { |
|
254 mainView.removeControl(feedItemControls[i]); |
|
255 } |
|
256 |
|
257 // reset feed item control array |
|
258 feedItemControls = []; |
|
259 } |
|
260 </pre> |
|
261 |
|
262 <p> |
|
263 |
|
264 The function simply loops through the array and calls the removeControl() method |
|
265 in the main view to remove each of the feed item controls from the view, one at a time. |
|
266 Finally we'll reset the feedItemControls array so that it reflects the fact that |
|
267 there are no more controls in the main view. |
|
268 </p> |
|
269 |
|
270 <p> |
|
271 |
|
272 What about adding news items? We'll do that in a function that we'll call from the |
|
273 feedUpdateCompleted() function. Let's call the function setFeedItems(). The function |
|
274 will start by calling the removeFeedItems() function we just created to empty the |
|
275 main view from news items before we start adding new ones to it. After this we'll |
|
276 loop through the news feed items that were handed to us from the FeedUpdateBroker. |
|
277 For each one we need a ContentPanel control. We'll be recycling the controls instead |
|
278 of constantly creating new ones over and over again as new news items come in. To |
|
279 this end we'll need a way to track ContentPanel controls that we have already |
|
280 created. We'll do this by creating an array called feedItemControlPool, which will |
|
281 be a global variable: |
|
282 </p> |
|
283 |
|
284 <pre> |
|
285 |
|
286 // Feed item control pool. |
|
287 var feedItemControlPool = []; |
|
288 </pre> |
|
289 |
|
290 <p> |
|
291 |
|
292 If we have enough controls in the pool we'll just take them from there. Otherwise |
|
293 we'll create a new ContentPanel and add it to the pool. Either way we'll end up with |
|
294 a ContentPanel control that is ready to be used. We'll need to reset its state since we |
|
295 recycled it, so we make sure it's collapsed rather than expanded, we'll set its caption |
|
296 to the title of the news item and we'll generate some HTML from the news item summary |
|
297 that we'll set as the content for the ContentPanel. And then finally we'll add the |
|
298 ContentPanel to the main view. |
|
299 </p> |
|
300 |
|
301 <p> |
|
302 |
|
303 Lets create the code but skip the HTML for the news item summary for now: |
|
304 </p> |
|
305 |
|
306 <pre> |
|
307 |
|
308 // Sets feed items. |
|
309 function setFeedItems(items) { |
|
310 // start by removing all current feed items |
|
311 removeFeedItems(); |
|
312 |
|
313 // create new feed items and add them to the main view |
|
314 // use feed item pool to recycle controls |
|
315 for (var i = 0; i < items.length; i++) { |
|
316 // get a feed item control from the pool or create one and |
|
317 // place it in the pool if there aren't enough feed item controls |
|
318 var feedItemControl; |
|
319 if (i == feedItemControlPool.length) { |
|
320 feedItemControl = new ContentPanel(null, null, null, true); |
|
321 feedItemControlPool.push(feedItemControl); |
|
322 } else { |
|
323 feedItemControl = feedItemControlPool[i]; |
|
324 } |
|
325 |
|
326 // initialize feed item control |
|
327 var item = items[i]; |
|
328 feedItemControl.setCaption(item.title); |
|
329 feedItemControl.setContent("placeholder"); |
|
330 feedItemControl.setExpanded(false); |
|
331 |
|
332 // add the feed item control to the main view |
|
333 feedItemControls.push(feedItemControl); |
|
334 mainView.addControl(feedItemControl); |
|
335 } |
|
336 } |
|
337 </pre> |
|
338 |
|
339 <p> |
|
340 |
|
341 The items argument contains the array of news items that we received in the event |
|
342 object argument to the feedUpdateCompleted() callback function. The items are in |
|
343 the "items" property of the event object. Note how the feedItemControl is either |
|
344 created or taken from the feedItemControlPool. If it's created it is given four |
|
345 arguments: a null unique identifier because we don't need one, a null caption and |
|
346 a null content because we will set both every time before we show it, and finally |
|
347 a value of true to the "foldable" argument in the ContentPanel constructor. This |
|
348 flag determines whether the ContentPanel can be folded (expanded and collapsed) or |
|
349 not. We want a foldable one so we pass true to this argument. |
|
350 </p> |
|
351 |
|
352 <p> |
|
353 |
|
354 Once we have the ContentPanel (either from the pool or newly created) we set its |
|
355 state: caption, content and expanded state. Note that for now we'll just set a |
|
356 placeholder string as the content. Also note that we set the expanded state to false |
|
357 to collapse the news items by default. Finally we add the content panels to the main |
|
358 view and to the feedItemControls array that tracks all the ContentPanels that we use |
|
359 for the news items. |
|
360 </p> |
|
361 |
|
362 <p> |
|
363 |
|
364 Now that we have written the code to create and add news items to the main view |
|
365 we have to hook that up to the feedUpdateCompleted() function. Remember we added |
|
366 a comment in the spot where we should return to do this? Let's replace that comment |
|
367 with the following: |
|
368 </p> |
|
369 |
|
370 <pre> |
|
371 |
|
372 // set feed items to the main view |
|
373 setFeedItems(event.items); |
|
374 |
|
375 // focus the first feed item control |
|
376 // (but only if we are in the main view) |
|
377 if (uiManager.getView() == mainView && feedItemControls.length > 0) { |
|
378 feedItemControls[0].setFocused(true); |
|
379 } |
|
380 </pre> |
|
381 |
|
382 <p> |
|
383 |
|
384 We just call the setFeedItems() function we just wrote, passing it the feed items |
|
385 that the FeedUpdateBroker fetched and parsed for us, and then if we're in the main |
|
386 view we'll focus the first of the news feed item controls. |
|
387 </p> |
|
388 |
|
389 <p> |
|
390 |
|
391 Now that we have all the code needed to fetch and update news feed items we'll go |
|
392 back and add some more functionality to a function we wrote a little bit earlier: |
|
393 the saveSettingsClicked() function. |
|
394 </p> |
|
395 |
|
396 <p> |
|
397 |
|
398 The functionality that we're adding will handle updates of the feed items in the |
|
399 main view after the user has modified the settings. We didn't write this earlier |
|
400 because we didn't have all the necessary support code in place but we can add it now. |
|
401 First we add some code to the end of the saveSettingsClicked() function to force an |
|
402 update of the news items after the users saves the settings: |
|
403 </p> |
|
404 |
|
405 <pre> |
|
406 |
|
407 // update the feed |
|
408 feedLastModified = null; |
|
409 updateFeed(); |
|
410 </pre> |
|
411 |
|
412 <p> |
|
413 |
|
414 We update the feed by setting the feedLastModified variable to null to force an update |
|
415 and then calling updateFeed(). This causes an immediate feed update and we also get the |
|
416 progress notification dialog, which is what we want because this was a manual |
|
417 update that was caused by the user clicking "save". |
|
418 </p> |
|
419 |
|
420 <p> |
|
421 |
|
422 Let's also add some code that checks if a new feed was selected and removes all the |
|
423 news items from the main view if the user selected a new feed. To do this we'll first |
|
424 copy the old feedURL to a variable that we'll call oldFeedURL at the very beginning |
|
425 of the function: |
|
426 </p> |
|
427 |
|
428 <pre> |
|
429 |
|
430 // remember the old feed URL |
|
431 var oldFeedURL = feedURL; |
|
432 </pre> |
|
433 |
|
434 <p> |
|
435 |
|
436 Then we'll add the following just before we show the main view: |
|
437 </p> |
|
438 |
|
439 <pre> |
|
440 |
|
441 // remove all feed items if the user selected a new feed |
|
442 if (feedURL != oldFeedURL) { |
|
443 removeFeedItems(); |
|
444 } |
|
445 </pre> |
|
446 |
|
447 <p> |
|
448 |
|
449 We can now test this in a PC browser, handset or emulator. Everything should work |
|
450 except that there's still nothing but a placeholder for the actual content in each |
|
451 ContentPanel. You can still expand and collapse the ContentPanels and the settings |
|
452 and automatic and manual updates are working. We're almost done but we still need |
|
453 to implement the code that will actually show news item summaries in the content |
|
454 panels. |
|
455 </p> |
|
456 |
|
457 <p> |
|
458 |
|
459 Remember we said that we would implement news items so that there's a link from |
|
460 each news item to the website where the full article is. When you open links |
|
461 to external websites in the S60 Web Runtime you should use the widget.openURL() |
|
462 function. But PC browsers don't have that function so we'll need to create a wrapper |
|
463 function that either calls widget.openURL() if we're in the Web Runtime or just |
|
464 opens a new window if we're in a PC browser. |
|
465 </p> |
|
466 |
|
467 <pre> |
|
468 |
|
469 // Opens a URL. |
|
470 function openURL(url) { |
|
471 if (window.widget) { |
|
472 // in WRT |
|
473 widget.openURL(url); |
|
474 } else { |
|
475 // outside WRT |
|
476 window.open(url, "NewWindow"); |
|
477 } |
|
478 } |
|
479 </pre> |
|
480 |
|
481 <p> |
|
482 |
|
483 Content in a ContentPanel control is a fragment of HTML. In other words it's a |
|
484 piece of HTML that will be inserted into the control using code. That means that |
|
485 in order to display the news item we will have to generate some HTML for it. |
|
486 We need a function that will take a news item object and return some HTML that |
|
487 we can give to thet setContent() method in the ContentPanel control. Let's write |
|
488 that function: |
|
489 </p> |
|
490 |
|
491 <pre> |
|
492 |
|
493 // Returns the content HTML for a feed item. |
|
494 function getContentHTMLForFeedItem(item) { |
|
495 var buf = ""; |
|
496 |
|
497 // item date |
|
498 if (item.date != null) { |
|
499 buf += "<div class=\"FeedItemDate\">" + item.date + "</div>"; |
|
500 } |
|
501 |
|
502 // item description |
|
503 if (item.description != null) { |
|
504 buf += "<div class=\"FeedItemDescription\">" + item.description + "</div>"; |
|
505 } |
|
506 |
|
507 // item URL |
|
508 if (item.url != null) { |
|
509 buf += "<div class=\"FeedItemLink\">"; |
|
510 buf += "<a href=\"JavaScript:openURL('" + item.url + "');\">"; |
|
511 buf += "Read more..."; |
|
512 buf += "</a>"; |
|
513 buf += "</div>"; |
|
514 } |
|
515 |
|
516 return buf; |
|
517 } |
|
518 </pre> |
|
519 |
|
520 <p> |
|
521 |
|
522 The function uses the properties in the news item object that the FeedUpdateBroker |
|
523 created for us. There's a date property that has the publish date of the news item. |
|
524 There's a description that contains the actual summary, and there's a URL that points |
|
525 to the website where the full article is. Note that we're using the openURL() wrapper |
|
526 function that we just wrote for the link to the full article. |
|
527 </p> |
|
528 |
|
529 <p> |
|
530 |
|
531 Our HTML is very simple: three div-tags that have the date, description and a link as |
|
532 their content. And ech of them has a CSS class so that we can match them with some |
|
533 style rules. Since we have three different pieces of data we need three CSS rules: |
|
534 FeedItemDate for the date, FeedItemDescription for the news item summary and finally |
|
535 FeedItemLink for the link to the website. Let's create these in the RSSReader.css |
|
536 stylesheet file: |
|
537 </p> |
|
538 |
|
539 <pre> |
|
540 |
|
541 |
|
542 /* Feed item date */ |
|
543 .FeedItemDate { |
|
544 font-style: italic; |
|
545 } |
|
546 |
|
547 /* Feed item text */ |
|
548 .FeedItemDescription { |
|
549 padding: 4px 0px; |
|
550 } |
|
551 |
|
552 /* Feed item links */ |
|
553 .FeedItemLink { |
|
554 |
|
555 } |
|
556 |
|
557 /* Anchor tags in the context of a feed item link */ |
|
558 .FeedItemLink a { |
|
559 text-decoration: underline; |
|
560 font-weight: bold; |
|
561 color: rgb(0,0,255); |
|
562 } |
|
563 |
|
564 /* Focused anchor tags */ |
|
565 .FeedItemLink a:focus { |
|
566 background: rgb(0,0,255); |
|
567 color: rgb(255,255,255); |
|
568 } |
|
569 </pre> |
|
570 |
|
571 <p> |
|
572 |
|
573 The rule for links can be left empty because we will just use default ContentPanel |
|
574 content styling. However we'll change the way the link looks in the context of that |
|
575 FeedItemLink div. We'll make links blue, bold and underlined in their normal state |
|
576 and inverse with a blue background and white text color when focused. |
|
577 </p> |
|
578 |
|
579 <p> |
|
580 |
|
581 Now that we have a function that generates HTML for the ContentPanel we can remove |
|
582 the placeholder content replace it with a call to our function: |
|
583 </p> |
|
584 |
|
585 <pre> |
|
586 |
|
587 feedItemControl.setContent(getContentHTMLForFeedItem(item)); |
|
588 </pre> |
|
589 |
|
590 <p> |
|
591 |
|
592 We're done! Now you can try the widget in a PC browser and then on the handset |
|
593 or emulator. |
|
594 </p> |
|
595 |
|
596 <div class="fignone" id="GUID-25CD0E54-0516-4469-965E-C5781CF44DC9__GUID-F7A5DBAA-7F9F-4C48-A25A-0DACE463CBF4"><a name="GUID-25CD0E54-0516-4469-965E-C5781CF44DC9__GUID-F7A5DBAA-7F9F-4C48-A25A-0DACE463CBF4"><!-- --></a><span class="figcap">Figure 1. |
|
597 RSS Reader main view</span> |
|
598 |
|
599 |
|
600 <br /><img src="RSS_Reader_Main_Screenshot_1.png" /><br /> |
|
601 </div> |
|
602 |
|
603 </div> |
|
604 |
|
605 <div class="section"><h2 class="sectiontitle"> |
|
606 What we have learned</h2> |
|
607 |
|
608 |
|
609 <p> |
|
610 |
|
611 The RSS Reader tutorial has taught us several things. We have learned to create a widget |
|
612 that has more than just one view. We have learned how to use several new WRTKit controls. |
|
613 We used the SelectionList, SelectionMenu and FormButton controls in our settings view, |
|
614 and in the main view we used the ContentPanel control that allowed us to add our own |
|
615 content as a seamless part of the rest of the user interface using HTML fragments that |
|
616 we styled with CSS rules. We have learned to modify a view while the widget runs by adding |
|
617 and removing controls. And we have learned how to separate our widget code so that the |
|
618 user interface code doesn't contain any logic code and so that the logic code doesn't |
|
619 contain any user interface code. |
|
620 </p> |
|
621 |
|
622 </div> |
|
623 |
|
624 </div> |
|
625 |
|
626 <div> |
|
627 <div class="familylinks"> |
|
628 <div class="parentlink"><strong>Parent topic:</strong> <a href="WRTKit_RSS_Reader_Tutorial-GUID-678d197f-c7b0-4e5e-85e2-f8549c75bbe8.html">RSS Reader</a></div> |
|
629 </div> |
|
630 </div> |
|
631 |
|
632 </body> |
|
633 </html> |