Skip to content
Newer
Older
100644 850 lines (747 sloc) 31.8 KB
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
1 ==============
2 Binding Events
3 ==============
4
5 **Time Estimate:** 20 minutes
6
7 **Difficulty Level:** Advanced
8
9 Summary
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
10 =======
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
11
12 This example shows how to bind events to a mojit, configure code to run on the client, and make AJAX calls to the YQL Web service. The application listens for events and then makes AJAX calls to
13 YQL to get Flickr photo information.
14
15 The following topics will be covered:
16
17 - configuring the application to run on the client
18 - getting Flickr data from the model with YQL
19 - binding events through the ``mojitProxy`` object
20 - making AJAX calls to YQL from the binder
21
22 Implementation Notes
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
23 ====================
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
24
25 Configuring the Application to Run on the Client
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
26 ------------------------------------------------
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
27
28 Mojito lets you configure applications to run on either the server or client side. This example uses binders that are deployed to the client, so we need to configure Mojito to deploy
29 the application to the client, where it will be executed by the browser.
30
31 To configure Mojito to run on the client, you simply set the ``"deploy"`` property to ``true`` in ``application.json`` as seen below.
32
33 .. code-block:: javascript
34
35 [
36 {
37 "settings": [ "master" ],
38 "specs": {
39 "frame": {
40 "type": "HTMLFrameMojit",
41 "config": {
42 "deploy": true,
43 "child": {
44 "type": "PagerMojit"
45 }
46 }
47 }
48 }
49 }
50 ]
51
52 Getting Data with YQL in the Model
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
53 ----------------------------------
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
54
55 In the mojit model, the `YUI YQL Query Utility <http://developer.yahoo.com/yui/3/yql/>`_ is used to get Flickr photo information. To access the utility in your model, specify ``'yql'``
56 in the ``requires`` array as seen in the code snippet below:
57
58 .. code-block:: javascript
59
60 YUI.add('PagerMojitModel', function(Y,NAME) {
61 ...
62 /* Code for PagerMojitModel */
63 ...
64 }, '0.0.1', {requires: ['yql']});
65
66 This code example uses the ``flickr.photos.search`` table to get information for photos that have a title, description, or tags containing a string.
67 For example, the YQL statement below returns Flickr photo information for those photos that have a title, description, or tags containing the string "Manhattan".
68 Open the query in the YQL Console and click **TEST** to see the returned XML response.
69
70 `select * from flickr.photos.search where text="Manhattan" <http://developer.yahoo.com/yql/console/#h=select%20*%20from%20flickr.photos.search%20where%20text%3D%22Manhattan%22>`_
71
72 The returned response contains photo information in the ``photo`` element. You extract the ``farm``, ``server``, ``id``, and ``secret`` attributes from each photo element to create the photo
73 URI as seen here:
74
75 ``http://farm + {farm} + static.flickr.com/ + {server} + / + {id} + _ + {secret} + .jpg``
76
77 In the ``model.js`` of ``PagerMojit`` shown below, the ``YQL`` function uses the YQL statement above to get photo data, then parses the returned response to create the photo URIs. The model then
78 wraps the photo information in an object and stores those objects in the ``images`` array that is sent to the controller through the ``callback`` function.
79
80 .. code-block:: javascript
81
82 YUI.add('PagerMojitModel', function(Y,NAME) {
83 /**
84 * The PagerMojitModel module.
85 * @module PagerMojitModel
86 */
87 /**
88 * Constructor for the Model class.
89 * @class Model
90 * @constructor
91 */
92 Y.mojito.models.simple = {
93 init: function(config) {
94 this.config = config;
95 },
96 getData: function(query, start, count, callback) {
97 var q = null;
98 // Get Flickr API key: http://www.flickr.com/services/api/keys/apply/
99 var API_KEY = "{your_api_key}";
100 start = parseInt(start) || 0;
101 count = parseInt(count) || 10;
102 q = 'select * from flickr.photos.search(' + start + ',' + count + ') where text="%' + query + '%" and api_key="' + API_KEY+'"';
103 Y.YQL(q, function(rawData) {
104 if (!rawData.query.results) {
105 callback([]);
106 return;
107 }
108 var rawImages = rawData.query.results.photo, rawImage = null,images = [], image = null, i = 0;
109 for (; i<rawImages.length; i++) {
110 rawImage = rawImages[i];
111 image = {
112 title: rawImage.title,
113 location: 'http://farm' + rawImage.farm + '.static.flickr.com/' + rawImage.server + '/' + rawImage.id + '_' + rawImage.secret + '.jpg',
114 farm: rawImage.farm,
115 server: rawImage.server,
116 image_id: rawImage.id,
117 secret: rawImage.secret
118 };
119 if (!image.title) {
120 image.title = "Generic Title: " + query;
121 }
122 images.push(image);
123 }
124 callback(images);
125 });
126 }
127 };
128 }, '0.0.1', {requires: [ 'yql']});
129
130 For a more detailed explanation about how to use YQL in your Mojito application, see `Calling YQL from a Mojit <calling_yql.html>`_. For more information about YQL, see the `YQL Guide <http://developer.yahoo.com/yql/guide>`_.
131
132 Binding Events
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
133 --------------
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
134
135 This section will discuss the basics of binding events in Mojito and then look at the binder used in this code example.
136
137 Binder Basics
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
138 #############
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
139
140 A mojit may have zero, one, or many binders within the ``binders`` directory. Each binder will be deployed to the browser along with the
141 rest of the mojit code, where the client-side Mojito runtime will call it appropriately. On the client,
142 the binder has a proxy object (``mojitProxy``) for interacting with the mojit it represents as well as with other mojits on the page.
143 Methods can be called from the ``mojitProxy`` object that allow binders to listen for and fire events.
144
145 The binder consists of a constructor, an initializer, and a bind function. The following describes each component and indicates when the ``mojitProxy`` object can be used.
146
147 - **constructor** - creates the namespace for your binder that wraps the initialization code and binder.
148 - **initializer** - is passed the ``mojitProxy`` where it can be stored and used to listen and fire events with other binders. The ``mojitProxy`` is the only gateway back into the Mojito framework for your binder.
149 - **bind** - is a function that is passed a ``Y.Node`` instance that wraps the DOM node representing this mojit instance. The DOM event handlers for capturing user interactions should be attached in this function.
150
151 The skeleton of the ``binders/index.js`` file below illustrates the basic structure of the binder. For more information, see `Mojito Binders <../intro/mojito_binders.html>`_.
152
153 .. code-block:: javascript
154
155 YUI.add('AwesomeMojitBinder', function(Y, NAME) {
156 // Binder constructor
157 Y.namespace('mojito.binders')[NAME] = {
158 init: function(mojitProxy) {
159 this.mojitProxy = mojitProxy;
160 },
161 // The bind function
162 bind: function(node) {
163 var thatNode = node;
164 }
165 };
166 Y.mojito.registerEventBinder('AwesomeMojit', Binder);
167 }, '0.0.1', {requires: ['mojito']});
168
169 Examining the PageMojitBinder
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
170 #############################
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
171
172 This code example uses the binder ``PageMojitBinder`` to perform the following:
173
174 - attach ``onClick`` handlers to ``prev`` and ``next`` links
175 - invoke the ``index`` method of the controller through the ``mojitProxy`` object
176 - create an overlay with Flickr photo information received from YQL
177
178 The ``binders/index.js`` for this code example is long and fairly involved, so we will dissect and analyze the code. Let's begin by looking at the ``bind`` function of ``index.js``,
179 which allows mojits to attach DOM event handlers.
180
181 In this code snippet of ``binders/index.js``, the ``bind`` function contains the nested ``updateDOM`` function that updates node content and attaches event handlers. Using the ``mojitProxy`` object,
182 the nested ``flipper`` function calls the ``index`` function of the controller. The callback ``updateDOM`` is passed to ``index`` to update the content.
183
184 .. code-block:: javascript
185
186 ...
187 bind: function(node) {
188 var thatNode = node;
189 // Define the action when user click on prev/next.
190 var flipper = function(event) {
191 var target = event.target;
192 // Get the link to the page.
193 var page = parsePage(target.get('href'));
194 var updateDOM = function(markup) {
195 thatNode.set('innerHTML', markup);
196 thatNode.all('#nav a').on('click', flipper, this);
197 thatNode.all('#master ul li a').on('mouseover', showOverlay, this);
198 thatNode.all('#master ul li a').on('mouseout', showOverlay, this);
199 };
200 this.mojitProxy.invoke('index',
201 {
202 params: {page: page},
203 }, updateDOM
204 );
205 };
206 ...
207
208
209 The event handler for mouseovers and mouseouts are handled by the ``showOverlay`` function, which creates the overlay containing photo information.
210 In the code snippet below, ``showOverlay`` makes an AJAX call to YQL to get photo data that is placed in an unordered list for the overlay.
211
212 .. code-block:: javascript
213
214 ...
215 bind: function(node) {
216 ...
217 var showOverlay = function(event) {
218 var target = event.target;
219 var href = target.get('href');
220 var imageId = parseImageId(href);
221 if (target.hasClass('overlayed')) {
222 target.removeClass('overlayed');
223 thatNode.one('#display').setContent('');
224 } else {
225 Y.log('HREF: ' + href);
226 Y.log('IMAGE ID: ' + imageId);
227 target.addClass('overlayed');
228 // Query for the image metadata
229 var query = 'select * from flickr.photos.info where photo_id="' + imageId + '"';
230 thatNode.one('#display').setContent('Loading ...');
231 Y.YQL(query, function(raw) {
232 if (!raw.query.results.photo) {
233 Y.log('No results found for photoId: ' + imageId);
234 return;
235 }
236 var props = raw.query.results.photo;
237 var snippet = '<ul style="list-style-type: square;">';
238 for (var key in props) {
239 if (typeof(props[key]) == 'object') {
240 continue;
241 }
242 snippet += '<li>' + key + ': ' + props[key] + '</li>';
243 }
244 snippet += '</ul>';
245 thatNode.one('#display').setContent(snippet);
246 });
247 }
248 };
249 ...
250 }
251 ...
252
253 Thus far, we've looked at the event handlers, but not the actual binding of the handlers to nodes. At the end of the ``bind`` function, you'll see three important lines (shown below) that
254 bind the ``flipper`` and ``showOutlay`` functions to handle click and mouseover events.
255
256 .. code-block:: javascript
257
258 ...
259 bind: function(node) {
260 ...
261 // Bind all the image links to showOverlay
262 thatNode.all('#master ul li a').on('mouseover', showOverlay, this);
263 thatNode.all('#master ul li a').on('mouseout', showOverlay, this);
264 // Bind the prev + next links to flipper
265 thatNode.all('#nav a').on('click', flipper, this);
266 }
267 ...
268
269 After a little analysis, the full ``binders/index.js`` below should be easier to understand. The binder attaches event handlers to nodes, invokes a function in the controller, and updates the content in
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
270 the template. The binder also has a couple of helper functions for parsing and requires the IO and YQL modules, which are specified in the ``requires`` array.
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
271
272 .. code-block:: javascript
273
274 YUI.add('PagerMojitBinder', function(Y, NAME) {
275 Y.namespace('mojito.binders')[NAME] = {
276 init: function(mojitProxy) {
277 this.mojitProxy = mojitProxy;
278 },
279 /**
280 * The binder method, invoked to allow the mojit
281 * to attach DOM event handlers.
282 * @param node {Node} The DOM node to which this
283 * mojit is attached.
284 */
285 bind: function(node) {
286 var thatNode = node;
287 Y.log('NODE: ' + Y.dump(this.node));
288 // define the action when user click on prev/next
289 var flipper = function(event) {
290 var target = event.target;
291 // get the link to the page
292 var page = parsePage(target.get('href'));
293 Y.log('PAGE: ' + page);
294 var updateDOM = function(markup) {
295 thatNode.set('innerHTML', markup);
296 thatNode.all('#nav a').on('click', flipper, this);
297 thatNode.all('#master ul li a').on('mouseover', showOverlay, this);
298 thatNode.all('#master ul li a').on('mouseout', showOverlay, this);
299 };
300 this.mojitProxy.invoke('index',
301 {
302 params: {page: page}
303 }, updateDOM
304 );
305 };
306 var showOverlay = function(event) {
307 var target = event.target;
308 var href = target.get('href');
309 var imageId = parseImageId(href);
310 if (target.hasClass('overlayed')) {
311 target.removeClass('overlayed');
312 thatNode.one('#display').setContent('');
313 } else {
314 Y.log('HREF: ' + href);
315 Y.log('IMAGE ID: ' + imageId);
316 target.addClass('overlayed');
317 // Query for the image metadata
318 var query = 'select * from flickr.photos.info where photo_id="' + imageId + '"';
319 thatNode.one('#display').setContent('Loading ...');
320 Y.YQL(query, function(raw) {
321 if (!raw.query.results.photo) {
322 Y.log('No results found for photoId: ' + imageId);
323 return;
324 }
325 var props = raw.query.results.photo;
326 var snippet = '<ul style="list-style-type: square;">';
327 for (var key in props) {
328 if (typeof(props[key]) == 'object') {
329 continue;
330 }
331 snippet += '<li>' + key + ': ' + props[key] + '</li>';
332 }
333 snippet += '</ul>';
334 thatNode.one('#display').setContent(snippet);
335 });
336 }
337 };
338 // Bind all the image links to showOverlay
339 thatNode.all('#master ul li a').on('mouseover', showOverlay, this);
340 thatNode.all('#master ul li a').on('mouseout', showOverlay, this);
341 // Bind the prev + next links to flipper
342 thatNode.all('#nav a').on('click', flipper, this);
343 }
344 };
345 function parseImageId(link) {
346 var matches = link.match(/com\/(\d+)\/(\d+)_([0-9a-z]+)\.jpg$/);
347 return matches[2];
348 }
349 function parsePage(link) {
350 var matches = link.match(/page=(\d+)/);
351 return matches[1];
352 }
353 }, '0.0.1', {requires: ['mojito', 'yql', 'io', 'dump', 'mojito-client']});
354
355 Using Paging
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
356 ------------
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
357
358 The paging for this code example relies on the application configuration to set route paths and the controller to create links to access previous and next pages.
359
360 The ``routes.json`` file below configures two route paths for HTTP GET calls made on the root path. The ``perpage`` configuration, however, requires a query string with the ``page`` parameter,
361 which is used for paging. The ``page`` parameter has the value ``:page``, which is a variable that is assigned a value by the controller that we're going to look shortly.
362
363 .. code-block:: javascript
364
365 [
366 {
367 "settings": ["master"],
368 "root": {
369 "verbs": ["get"],
370 "path": "/",
371 "call": "frame.index"
372 },
373 "perpage": {
374 "verbs": ["get"],
375 "path": "/?page=:page",
376 "call": "frame.index"
377 }
378 }
379 ]
380
381 The controller for ``PagerMojit`` performs several functions:
382
383 - uses the ``Params`` addon to get the ``page`` parameter from the query string
384 - calculates the index of the first photo on the page
385 - calls the ``getData`` function in the model to get photo data
386 - creates URLs for the **next** and **prev** links
387
4af19b6 @zhouyaoji Fixed links.
zhouyaoji authored
388 The `Params addon <../../api/classes/Params.common.html>`_ allows you to access variables from the query string parameters, the POST request bodies, or the routing systems URLs.
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
389 In this code example, you use the ``getFromMerged`` method, which merges the parameters from the query string, POST request body, and the routing system URLs to give you access to
390 all of the parameters. In the code snippet taken from ``controller.server.js`` below, the ``getFromMerged`` method is used to get the value for the ``page`` parameter and then calculate
391 the index of the first photo to display:
392
393 .. code-block:: javascript
394
395 ...
396 index: function(actionContext) {
397 var page = actionContext.params.getFromMerged('page');
398 var start;
399 page = parseInt(page) || 1;
400 if ((!page) || (page<1)) {
401 page = 1;
402 }
403 // Page param is 1 based, but the model is 0 based
404 start = (page - 1) * PAGE_SIZE;
405 ...
406 }
407 ...
408
409 To get the photo data, the controller depends on the model to call YQL to query the Flickr API. Using ``actionContext.models.{model_name}`` lets you get a reference to the model.
410 In this example controller, the model of the ``PagerMojit`` is accessed through ``actionContext.models.PageMojit``, allowing you to call ``getData`` and get the returned data from YQL
411 in the callback function.
412
413 .. code-block:: javascript
414
415 ...
416 index: function(actionContext) {
417 ...
418 var model = actionContext.models.PagerMojit;
419 // Data is an array of images
420 model.getData('mojito', start, PAGE_SIZE, function(data) {
421 Y.log('DATA: ' + Y.dump(data));
422 var theData = {
423 data: data, // images
424 hasLink: false,
425 prev: {
426 title: "prev" // opportunity to localize
427 },
428 next: {
429 link: createLink(actionContext, {page: page+1}),
430 title: "next"
431 },
432 query: 'mojito'
433 };
434 if (page > 1) {
435 theData.prev.link = createLink(actionContext, {page: page-1});
436 theData.hasLink = true;
437 }
438 actionContext.done(theData);
439 });
440 }
441 ...
442 };
443 ...
444
445 The URLs for the **prev** and **next** links are created by passing the mojit instance, the method, and the query string parameters to the ``make`` method from the ``Url`` addon.
446 The code snippet below creates the query string parameters with the `YUI QueryString module <http://yuilibrary.com/yui/docs/api/modules/querystring.html>`_.
447 If the query string created by ``Y.QueryString.stringify`` is "page=2" , ``actionContext.url.make`` would return the URL ``{domain_name}:8666/?page=2``.
448
449 .. code-block:: javascript
450
451 ...
452 function createLink(actionContext, params) {
453 var mergedParams = Y.mojito.util.copy(actionContext.params.getFromMerged());
454 for (var k in params) {
455 mergedParams[k] = params[k];
456 }
457 return actionContext.url.make('frame', 'index', Y.QueryString.stringify(mergedParams));
458 }
459 ...
460
461 Stitching the above code snippets together, we have the ``controller.server.js`` below. The ``index`` function relies on the model for data and
462 the ``createLink`` function to create URLs for the **next** and **prev** links.
463
464 .. code-block:: javascript
465
466 YUI.add('PagerMojit', function(Y,NAME) {
467 /**
468 * The PagerMojit module.
469 * @module PagerMojit */
470 var PAGE_SIZE = 10;
471 /**
472 * Constructor for the Controller class.
473 * @class Controller
474 * @constructor
475 */
476 Y.mojito.controllers[NAME] = {
477 init: function(config) {
478 this.config = config;
479 },
480 index: function(actionContext) {
481 var page = actionContext.params.getFromMerged('page');
482 var start;
483 page = parseInt(page) || 1;
484 if ((!page) || (page<1)) {
485 page = 1;
486 }
487 // Page param is 1 based, but the model is 0 based
488 start = (page - 1) * PAGE_SIZE;
489 var model = actionContext.models.PagerMojit;
490 // Data is an array of images
491 model.getData('mojito', start, PAGE_SIZE, function(data) {
492 Y.log('DATA: ' + Y.dump(data));
493 var theData = {
494 data: data, // images
495 hasLink: false,
496 prev: {
497 title: "prev" // opportunity to localize
498 },
499 next: {
500 link: createLink(actionContext, {page: page+1}),
501 title: "next"
502 },
503 query: 'mojito'
504 };
505 if (page > 1) {
506 theData.prev.link = createLink(actionContext, {page: page-1});
507 theData.hasLink = true;
508 }
509 actionContext.done(theData);
510 });
511 }
512 };
513 // generate the link to the next page based on:
514 // - mojit id
515 // - action
516 // - params
517 function createLink(actionContext, params) {
518 var mergedParams = Y.mojito.util.copy(actionContext.params.getFromMerged());
519 for (var k in params) {
520 mergedParams[k] = params[k];
521 }
522 return actionContext.url.make('frame', 'index', Y.QueryString.stringify(mergedParams));
523 }
524 }, '0.0.1', {requires: ['dump']});
525
526 Setting Up this Example
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
527 =======================
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
528
529 To set up and run ``binding_events``:
530
531 #. Create your application.
532
533 ``$ mojito create app binding_events``
534
535 #. Change to the application directory.
536
537 #. Create your mojit.
538
539 ``$ mojito create mojit PagerMojit``
540
541 #. To configure you application to run on the client and use ``HTMLFrameMojit``, replace the code in ``application.json`` with the following:
542
543 .. code-block:: javascript
544
545 [
546 {
547 "settings": [ "master" ],
548 "specs": {
549 "frame": {
550 "type": "HTMLFrameMojit",
551 "config": {
552 "deploy": true,
553 "child": {
554 "type": "PagerMojit"
555 }
556 }
557 }
558 }
559 }
560 ]
561
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
562 #. To configure routing to call the ``index`` action from the instance of the ``HTMLFrameMojit``, replace the code in ``routes.json`` with the following:
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
563
564 .. code-block:: javascript
565
566 [
567 {
568 "settings": ["master"],
569 "root": {
570 "verbs": ["get"],
571 "path": "/",
572 "call": "frame.index"
573 },
574 "perpage": {
575 "verbs": ["get"],
576 "path": "/?page=:page",
577 "call": "frame.index"
578 }
579 }
580 ]
581
582 #. Change to ``mojits/PageMojit``.
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
583 #. To have the controller get data from the model and create links for paging, replace the code in ``controller.server.js``
584 with the following:
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
585
586 .. code-block:: javascript
587
588 YUI.add('PagerMojit', function(Y,NAME) {
589 var PAGE_SIZE = 10;
590 /**
591 * Constructor for the Controller class.
592 * @class Controller
593 * @constructor
594 */
595 Y.mojito.controllers[NAME] = {
596 init: function(config) {
597 this.config = config;
598 },
599 index: function(actionContext) {
600 var page = actionContext.params.getFromMerged('page');
601 var start;
602 page = parseInt(page) || 1;
603 if ((!page) || (page<1)) {
604 page = 1;
605 }
606 // Page param is 1 based, but the model is 0 based
607 start = (page - 1) * PAGE_SIZE;
608 var model = actionContext.models.PagerMojit;
609 // Data is an array of images
610 model.getData('mojito', start, PAGE_SIZE, function(data) {
611 Y.log('DATA: ' + Y.dump(data));
612 var theData = {
613 data: data, // images
614 hasLink: false,
615 prev: {
616 title: "prev" // opportunity to localize
617 },
618 next: {
619 link: createLink(actionContext, {page: page+1}),
620 title: "next"
621 },
622 query: 'mojito'
623 };
624 if (page > 1) {
625 theData.prev.link = createLink(actionContext, {page: page-1});
626 theData.hasLink = true;
627 }
628 actionContext.done(theData);
629 });
630 }
631 };
632 // Generate the link to the next page based on:
633 // - mojit id
634 // - action
635 // - params
636 function createLink(actionContext, params) {
637 var mergedParams = Y.mojito.util.copy(actionContext.params.getFromMerged());
638 for (var k in params) {
639 mergedParams[k] = params[k];
640 }
641 return actionContext.url.make('frame', 'index', Y.QueryString.stringify(mergedParams));
642 }
643 }, '0.0.1', {requires: ['dump']});
644
645 #. To get Flickr photo information using YQL, replace the code in ``model.server.js`` with the following:
646
647 .. code-block:: javascript
648
649 YUI.add('PagerMojitModel', function(Y,NAME) {
650 /**
651 * The PagerMojitModel module.
652 * @module PagerMojitModel
653 */
654 /**
655 * Constructor for the Model class.
656 * @class Model
657 * @constructor
658 */
659 Y.mojito.models.PagerMojit = {
660 init: function(config) {
661 this.config = config;
662 },
663 getData: function(query, start, count, callback) {
664 var q = null;
665 // Get Flickr API key: http://www.flickr.com/services/api/keys/apply/
666 var API_KEY = "{your_api_key}";
667 start = parseInt(start) || 0;
668 count = parseInt(count) || 10;
669 q = 'select * from flickr.photos.search(' + start + ',' + count + ') where text="%' + query + '%" and api_key="' + API_KEY+'"';
670 Y.YQL(q, function(rawData) {
671 if (!rawData.query.results) {
672 callback([]);
673 return;
674 }
675 var rawImages = rawData.query.results.photo, rawImage = null,images = [], image = null, i = 0;
676 for (; i<rawImages.length; i++) {
677 rawImage = rawImages[i];
678 image = {
679 title: rawImage.title,
680 location: 'http://farm' + rawImage.farm + '.static.flickr.com/' + rawImage.server + '/' + rawImage.id + '_' + rawImage.secret + '.jpg',
681 farm: rawImage.farm,
682 server: rawImage.server,
683 image_id: rawImage.id,
684 secret: rawImage.secret
685 };
686 if (!image.title) {
687 image.title = "Generic Title: " + query;
688 }
689 images.push(image);
690 }
691 callback(images);
692 });
693 }
694 };
695 }, '0.0.1', {requires: ['yql']});
696
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
697 #. To create the binder for click events and invoke the ``index`` function of the controller, replace the code in ``binders/index.js``
698 with the following:
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
699
700 .. code-block:: javascript
701
702 YUI.add('PagerMojitBinder', function(Y, NAME) {
703 /**
704 * The PagerMojitBinder module.
705 * @module PagerMojitBinder
706 */
707 /**
708 * Constructor for the Binder class.
709 *
710 * @param mojitProxy {Object} The proxy to allow
711 * the binder to interact with its owning mojit.
712 * @class Binder
713 * @constructor
714 */
715 Y.namespace('mojito.binders')[NAME] = {
716 /**
717 * Binder initialization method, invoked
718 * after all binders on the page have
719 * been constructed.
720 */
721 init: function(mojitProxy) {
722 this.mojitProxy = mojitProxy;
723 },
724 /**
725 * The binder method, invoked to allow the mojit
726 * to attach DOM event handlers.
727 * @param node {Node} The DOM node to which this
728 * mojit is attached.
729 */
730 bind: function(node) {
731 var thatNode = node;
732 Y.log('NODE: ' + Y.dump(this.node));
733 // define the action when user click on prev/next
734 var flipper = function(event) {
735 var target = event.target;
736 // get the link to the page
737 var page = parsePage(target.get('href'));
738 Y.log('PAGE: ' + page);
739 var updateDOM = function(markup) {
740 thatNode.set('innerHTML', markup);
741 thatNode.all('#nav a').on('click', flipper, this);
742 thatNode.all('#master ul li a').on('mouseover', showOverlay, this);
743 thatNode.all('#master ul li a').on('mouseout', showOverlay, this);
744 };
745 this.mojitProxy.invoke('index',
746 {
747 params: {page: page}
748 }, updateDOM
749 );
750 };
751 var showOverlay = function(event) {
752 var target = event.target;
753 var href = target.get('href');
754 var imageId = parseImageId(href);
755 if (target.hasClass('overlayed')) {
756 target.removeClass('overlayed');
757 thatNode.one('#display').setContent('');
758 } else {
759 Y.log('HREF: ' + href);
760 Y.log('IMAGE ID: ' + imageId);
761 target.addClass('overlayed');
762 // Query for the image metadata
763 var query = 'select * from flickr.photos.info where photo_id="' + imageId + '"';
764 thatNode.one('#display').setContent('Loading ...');
765 Y.YQL(query, function(raw) {
766 if (!raw.query.results.photo) {
767 Y.log('No results found for photoId: ' + imageId);
768 return;
769 }
770 var props = raw.query.results.photo;
771 var snippet = '<ul style="list-style-type: square;">';
772 for (var key in props) {
773 if (typeof(props[key]) == 'object') {
774 continue;
775 }
776 snippet += '<li>' + key + ': ' + props[key] + '</li>';
777 }
778 snippet += '</ul>';
779 thatNode.one('#display').setContent(snippet);
780 });
781 }
782 };
783 // Bind all the image links to showOverlay
784 thatNode.all('#master ul li a').on('mouseover', showOverlay, this);
785 thatNode.all('#master ul li a').on('mouseout', showOverlay, this);
786 // Bind the prev + next links to flipper
787 thatNode.all('#nav a').on('click', flipper, this);
788 }
789 };
790 function parseImageId(link) {
791 var matches = link.match(/com\/(\d+)\/(\d+)_([0-9a-z]+)\.jpg$/);
792 return matches[2];
793 }
794 function parsePage(link) {
795 var matches = link.match(/page=(\d+)/);
796 return matches[1];
797 }
798 }, '0.0.1', {requires: ['yql', 'io', 'dump']});
799
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
800 #. To display links to photos and associated photo data in the rendered template, replace the code in ``views/index.hb.html`` with the following:
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
801
802 .. code-block:: html
803
804 <div id="{{mojit_view_id}}" class="mojit" style="position: relative; width: 960px">
805 <h3>Query Term: {{query}}</h3>
806 <div id="nav" style="clear: both;">
807 {{#hasLink}}
808 {{#prev}}
809 <a href="{{{link}}}">{{title}}</a>
810 {{/prev}}
811 {{/hasLink}}
812 {{^hasLink}}
813 {{#prev}}{{title}}{{/prev}}
814 {{/hasLink}}
815 {{#next}}
816 <a href="{{{link}}}">{{title}}</a>
817 {{/next}}
818 </div>
819 <div id="master" style="width: 30%; float: left;">
820 <ul>
821 {{#data}}
822 <li><a href="{{location}}" data-id="{{image_id}}">{{title}}</a></li>
823 {{/data}}
824 </ul>
825 </div>
826 <div style="width: 50%; float: right">
827 <!-- load image here dynamically -->
828 <div id="display" style="margin: 0 auto;">
829 &nbsp;
830 </div>
831 </div>
832 </div>
833
834 #. From the application directory, run the server.
835
836 ``$ mojito start``
837
838 #. To view your application, go to the URL:
839
840 http://localhost:8666
841
842 Source Code
b5ece1a @zhouyaoji Fixed steps, replaced 'view template' with 'template', fixed syntax f…
zhouyaoji authored
843 ===========
4b30716 @zhouyaoji Adding the reST documentation to the Mojito project.
zhouyaoji authored
844
845 - `Application Configuration <http://github.com/yahoo/mojito/tree/master/examples/developer-guide/binding_events/application.json>`_
846 - `Mojit Binder <http://github.com/yahoo/mojito/tree/master/examples/developer-guide/binding_events/mojits/PagerMojit/binders/index.js>`_
847 - `Binding Events Application <http://github.com/yahoo/mojito/tree/master/examples/developer-guide/binding_events/>`_
848
849
Something went wrong with that request. Please try again.