• Like

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

JavaScript Event-driven architecture

  • 3,330 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
3,330
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
68
Comments
0
Likes
6

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide













































































Transcript

  • 1. JavaScript event-driven architecture Радослав Станков OpenFest 2010
  • 2. Кой съм аз? @rstankov http://rstankov.com http://blog.rstankov.com http://github.com/rstankov
  • 3. Какво е Event-driven architecture?
  • 4. Демонстрация
  • 5. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>OpenFest Demo</title> <link href="/application.css" media="screen" rel="stylesheet" type="text/css" /> </head> <body> <div id="images_widget"> <form method="post" action="/images" enctype="multipart/form-data" id="new_image"> <input type="file" id="image_file" name="image[file]" /> <input type="submit" value="Добави снимка" /> </form> <ol> <li> <div style="background-image: url(/system/images/2/thumb.png);"></div> <span> <a href="/system/images/2/big.png" target="_blank" class="view">Преглед</a> <a href="/images/2" rel="nofollow" class="delete">Изтрий</a> </span> </li> <!-- more items --> </ol> </div> </body> </html>
  • 6. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>OpenFest Demo</title> <link href="/application.css" media="screen" rel="stylesheet" type="text/css" /> </head> <body> <div class="question"> <h1>Сигурни ли сте, че искате да изтриете тази снимка?</h1> <p> <img alt="Thumb" src="/system/images/1/thumb.png" /> </p> <form method="post" action="/images"> <input type="hidden" name="_method" value="delete" /> <input type="submit" value="Да" /> <a href="/images">Не</a> </form> </div> </body> </html>
  • 7. <form method="post" action="/images" enctype="multipart/form-data" id="new_image"> <input type="file" id="image_file" name="image[file]" /> <input type="submit" value="Добави снимка" /> </form>
  • 8. $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && alert(xhr.responseText); }, success: function (html){ $("#images_widget ol").prepend(html); }, }, files[0]); } this.reset(); return false; });
  • 9. $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && alert(xhr.responseText); }, success: function (html){ $("#images_widget ol").prepend(html); }, }, files[0]); } this.reset(); return false; });
  • 10. $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && alert(xhr.responseText); }, success: function (html){ $("#images_widget ol").prepend(html); }, }, files[0]); } this.reset(); return false; });
  • 11. $.fileUploadSupported && $('#new_image').submit(function(e){ var form = $(this), file = form.children('input[type="file"]'), files = file[0].files; if (files && files.length > 0){ $.uploadFile({ url: form.attr("action"), dataType: "html", name: file.attr("name"), beforeSend: function(){ /* disable form */ }, complete: function(){ /* enable form */ }, error: function(xhr){ xhr.status == 422 && form.trigger("action:error", xhr.responseText); }, success: function (html){ form.trigger("action:insert", html); }, }, files[0]); } this.reset(); return false; });
  • 12. $("#images_widget").each(function(){ var widget = $(this), images = element.children("ol"); element.bind("action:insert", function(e, content){ images.prepend(content); }); element.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); });
  • 13. <li> <div style="background-image: url(...);"></div> <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete">Изтрий</a> </span> </li>
  • 14. <li> <div style="background-image: url(...);"></div> <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span> </li>
  • 15. <li> <div style="background-image: url(...);"></div> <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span> </li>
  • 16. $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.parent("li").remove(); } }); };
  • 17. <span> <a href="..." class="view">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span>
  • 18. <span> <a href="..." class="view" data-action="preview">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span>
  • 19. <span> <a href="..." class="view" data-action="preview">Преглед</a> <a href="..." class="delete" data-method="delete" data-confirm="."> Изтрий </a> </span>
  • 20. $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); }; function preview(url){ // ... code ... }
  • 21. <ol> <li><!-- image --></li> <li><!-- image --></li> <li><!-- image --></li> </ol>
  • 22. <ol data-sortable-url="/images/reorder"> <li id="image_3"><!-- image --></li> <li id="image_2"><!-- image --></li> <li id="image_1"><!-- image --></li> </ol>
  • 23. <ol data-sortable-url="/images/reorder"> <li id="image_3"><!-- image --></li> <li id="image_2"><!-- image --></li> <li id="image_1"><!-- image --></li> </ol>
  • 24. <ol data-sortable-url="/images/reorder"> <li id="image_3"><!-- image --></li> <li id="image_2"><!-- image --></li> <li id="image_1"><!-- image --></li> </ol>
  • 25. $("#images_widget").each(function(){ // ... code ... images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 26. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 27. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 28. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 29. $("#images_widget").each(function(){ // ... code ... widget.bind("action:error", function(e, errorMessage){ alert(errorMessage); }); widget.delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); });
  • 30. $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); });
  • 31. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 32. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 33. $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); });
  • 34. $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 35. $("#images_widget").each(function(){ // ... code ... widget.delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.remove(); } }); });
  • 36. $("#images_widget").each(function(){ // ... code ... widget.delegate("li", "action:delete", function(){ $(this).remove(); }); });
  • 37. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 38. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 39. $("#images_widget").each(function(){ // ... code ... images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ $.ajax({ url: images.data("sortable-url"), type: "PUT", data: $(this).sortable("serialize") }); } }); });
  • 40. $("#images_widget").each(function(){ // ... code ... images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 41. $(document).delegate("[data-sortable-url]", "action:reorder", function(e, data){ $.ajax({ url: $(this).data("sortable-url"), type: "PUT", data: data }); });
  • 42. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 43. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 44. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 45. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 46. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 47. $("#images_widget").each(function(){ var widget = $(this), images = widget.children("ol"); widget.bind("action:insert", function(e, content){ images.prepend(content); }); widget.delegate("li", "action:delete", function(){ $(this).remove(); }); images.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ images.trigger("action:reorder", $(this).sortable("serialize")); } }); });
  • 48. var OpenFest = { createDynamicWidget: function(widget, opts){ widget = $(widget); opts = jQuery.extend({ list: "ol", item: "li" }, opts || {}); var list = widget.children(opts.list); widget.bind("action:insert", function(e, content){ list.prepend(content); }); widget.delegate(opts.item, "action:delete", function(){ $(this).remove(); }); list.sortable({ placeholder: "ui-state-highlight", update: function(e, ui){ list.trigger("action:reorder", $(this).sortable("serialize")); } }); } };
  • 49. OpenFest.createDynamicWidget("#images_widget");
  • 50. $("#images_widget").dynamicWidget();
  • 51. $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 52. $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 53. $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 54. $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 55. var OpenFest = { createDynamicWidget: function(){ /* code */ }, confirm: function(question){ return !question || confirm(question); }, errorMessage: function(errorMessage){ alert(errorMessage); }, preview: function(){ /* code */ } };
  • 56. $(document).bind("action:error", function(e, errorMessage){ alert(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 57. $(document).bind("action:error", function(e, errorMessage){ OpenFest.errorMessage(errorMessage); }); $(document).delegate('[data-action="preview"]', "click", function(e){ e.preventDefault(); OpenFest.preview($(this).attr("href")); }); $(document).delegate('[data-method="delete"]', "click", function(e){ e.preventDefault(); var element = $(this); if (OpenFest.confirm(element.data("confirm"))){ $.ajax({ url: element.attr("href"), type: "DELETE" }); element.trigger("action:delete"); } });
  • 58. //= require "jquery/core" //= require "jquery/ui" //= require "jquery/html5_upload" //= require "open_fest" //= require "behaviors/error" //= require "behaviors/delete" //= require "behaviors/preview" //= require "behaviors/reorder" OpenFest.createDynamicWidget("#images_widget"); $.fileUploadSupported && $('#new_image').submit(function(e){ // ... the ugly code ... });
  • 59. Кода стъпка по стъпка: https://github.com/RStankov/OpenFest-2010/
  • 60. Какво е Event-driven architecture? Основната идея, е да разделите приложeнието си на малки независими части, които не знаят еднa за другa и контактуват помежду си чрез предаване на събития.
  • 61. Няколко полезни съвета
  • 62. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития
  • 63. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си
  • 64. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е
  • 65. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития
  • 66. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития
  • 67. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя
  • 68. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя • внимавайте с често повтарящи се събития като mousemove, keyup
  • 69. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя • внимавайте с често повтарящи се събития като mousemove, keyup • следвайте добрите практики за писане на JavaScript
  • 70. Няколко полезни съвета • вашите модули да са изолирани и да контактуват само чрез събития • следвайте един модел на именуване на събитията и data-* атрибутите си • колкото по-общо е едно събитие толкова по-високо в йерархията трябва да е • използвайте често делегиране на събития • използвайте кратки и бързи selector-и при делегиране на събития • предефинирайте действията на потребителя • внимавайте с често повтарящи се събития като mousemove, keyup • следвайте добрите практики за писане на JavaScript • тествайте!
  • 71. Какво харесвам в този начин на работа?
  • 72. Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин
  • 73. Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост
  • 74. Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост • Лесно се сменят различните части от приложението
  • 75. Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост • Лесно се сменят различните части от приложението • Точно изразен начин, по който комуникират частите на приложението
  • 76. Какво харесвам в този начин на работа? • JavaScript е направен да работи по подобен начин • Пише се сравнително малко код с висока честота на пре-използваемост • Лесно се сменят различните части от приложението • Точно изразен начин, по който комуникират частите на приложението • Лесно тестване
  • 77. Благодаря за вниманието @rstankov