Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Single Page Applications with CoffeeScript

Single Page Applications with CoffeeScript

Frontend first. Hexagonal architecture. MVC.

Andrzej Krzywda

November 18, 2012
Tweet

More Decks by Andrzej Krzywda

Other Decks in Programming

Transcript

  1. • “I have to” • “unfortunately” • “I don’t know”

    • “I’d rather do Ruby|Python” • “someone has to do it” • “we have this JS dev in our team, I like him” • “Andrzej, which framework are you using?” What people say about JavaScript... Sunday, November 18, 12
  2. I have no idea what I’m doing me doing JS,

    10.10.2009, 4am Sunday, November 18, 12
  3. I have no idea what I’m doing A random Ruby

    programmer doing JS, 2007-2012 Sunday, November 18, 12
  4. I have no idea what I’m doing A random backend

    programmer doing JS, 2007-2012 Sunday, November 18, 12
  5. Data binding can be hard when you have data pushed

    in (web sockets) Sunday, November 18, 12
  6. 4 phases • phase 1: rejection • phase 2: shortcuts

    • phase 3: jquery explosion • phase 4: structuring Sunday, November 18, 12
  7. • Frontend • Initialization • Fetch data • Render html

    • UI cycle • business logic • Backend • Storage • Validations • API/JSON • serving assets • business logic Sunday, November 18, 12
  8. class CompleteTasksUseCase constructor: -> @todoTasks = [] completedTasks: => @todoTasks.filter

    (task) -> task.completed remainingTasks: => @todoTasks.filter (task) -> not task.completed setInitialTasks: (tasks) => @todoTasks = tasks addNewTask: (task) => @todoTasks.push(task) editTaskContent: (task) => updateTaskContent: (task, content) => task.content = content deleteTask: (task) => @todoTasks.remove(task) completeAllTasks: => @todoTasks.map((task) => @completeTask(task)) completeTask: (task) => task.complete() uncompleteTask: (task) => task.uncomplete() clearCompleted: => @completedTasks().each (task) => @deleteTask(task) class Task constructor: (@content, @completed=false) -> complete: => @completed = true uncomplete: => @completed = false Sunday, November 18, 12
  9. class Gui constructor: -> $("#new-todo").keypress((event) => @keyPressed(event)) $("#toggle-all").click( => @completeAllTasksClicked())

    @taskElements = [] findTaskElement: (task) => @taskElements.find((taskElement) -> taskElement.task == task) deleteTaskClicked: (task) => deleteTask: (task) => @findTaskElement(task).remove() updateTaskContent: (task, content) => element = @findTaskElement(task) element.removeClass("editing").find("input.edit").hide() element.find("label").html(content) completeTask: (task) => element = @findTaskElement(task) element.addClass("completed") element.find("input.toggle").attr("checked", "checked") Sunday, November 18, 12
  10. class Glue constructor: (@useCase, @gui)-> After(@useCase, 'addNewTask' , (task) =>

    @gui.addNewTask(task)) After(@useCase, 'deleteTask', @gui.deleteTask) After(@useCase, 'completeTask', @gui.completeTask) After(@useCase, 'uncompleteTask', @gui.uncompleteTask) After(@useCase, 'editTaskContent', @gui.editTaskContent) After(@gui, 'taskContentDoubleClicked', @useCase.editTaskContent) After(@useCase, 'updateTaskContent', @gui.updateTaskContent) Convention Sunday, November 18, 12
  11. typical adapters • GUI / Widgets • Canvas • ServerSide

    • LocalStorage • Sound • Facebook • Twitter • Pusher • WebSockets • Router (URL) Sunday, November 18, 12
  12. Callbacks • Fine for non-model code - ajax, gui •

    Callback hell in models Sunday, November 18, 12
  13. Events • Better than callbacks • good for gui •

    ugly in models Sunday, November 18, 12
  14. Method hooks • AOP-like • non-invasive • good for model-adapter

    communication • bad for model-model communication • application glue • clean model Sunday, November 18, 12
  15. class Glue constructor: (@useCase, @gui)-> After(@useCase, 'addNewTask' , (task) =>

    @gui.addNewTask(task)) After(@useCase, 'showAll' , => @gui.showTasks(@useCase.todoTasks)) After(@useCase, 'deleteTask', @gui.deleteTask) After(@useCase, 'completeTask', @gui.completeTask) After(@useCase, 'uncompleteTask', @gui.uncompleteTask) After(@useCase, 'editTaskContent', @gui.editTaskContent) After(@gui, 'taskContentDoubleClicked', @useCase.editTaskContent) After(@useCase, 'updateTaskContent', @gui.updateTaskContent) Convention Sunday, November 18, 12
  16. Use case View Glue Domain 1. deleteClicked 2. deleteTask 3.

    user.deleteTask 4. all fine (no exception) 5. task deleted 6. remove task DOM element 7. change task stats Storage 8. remove task from storage Sunday, November 18, 12
  17. Our process • Frontend first • InMemoryServerSide • Build all

    of the frontend • Build the backend at the end • postpone the decision • Realtime data pushed into the frontend Sunday, November 18, 12
  18. Our conclusion • It’s faster to create a SPA +

    backend • users love it • customers love it • developers love it • “it’s the best thing since Rails” • “I prefer to work on SPA than on the backend” Sunday, November 18, 12
  19. Summary • Frontend as a separate application • Hexagonal architecture

    • the Observer pattern • method hooks Sunday, November 18, 12
  20. Your turn • Look at todomvc.com • Look at github.com/gameboxed/todomvc

    • Create nice frontends! Sunday, November 18, 12