Save 37% off PRO during our Black Friday Sale! »

Symfony UX Turbo

Symfony UX Turbo

3dd28ad260d202a12c2e93fc28fad5d7?s=128

Marco Petersen

May 25, 2021
Tweet

Transcript

  1. Symfony UX Turbo

  2. Tired Of Modern Web Development?

  3. You build your backend that exposes some kind of API.

  4. You build your backend that exposes some kind of API.

    You build a Single-Page Application that consumes your API.
  5. You build your backend that exposes some kind of API.

    You build a Single-Page Application that consumes your API. You make sure the backend doesn't send data the frontend doesn't expect (and vice versa).
  6. You build your backend that exposes some kind of API.

    You build a Single-Page Application that consumes your API. You make sure the backend doesn't send data the frontend doesn't expect (and vice versa). You coordinate releases to make sure neither side causes trouble in production.
  7. You build your backend that exposes some kind of API.

    You build a Single-Page Application that consumes your API. You make sure the backend doesn't send data the frontend doesn't expect (and vice versa). You coordinate releases to make sure neither side causes trouble in production. You fight with Webpack and Babel.
  8. You build your backend that exposes some kind of API.

    You build a Single-Page Application that consumes your API. You make sure the backend doesn't send data the frontend doesn't expect (and vice versa). You coordinate releases to make sure neither side causes trouble in production. You fight with Webpack and Babel. And all you wanted was to give your users that modern web experience !
  9. Ever Wish You Could Write a modern application Without Writing

    JavaScript?
  10. Is that even possible?

  11. Yes.

  12. Thanks for listening! $kernel->terminate($request, $response);

  13. Actually, there's a bit more to it...

  14. Olá! ! Marco Petersen " Software Developer @ QOSSMIC (fka

    SensioLabs Germany) # Berlin $ % @ocrampete16
  15. Today's Menu • Appetizer: The Back Story - What is

    Symfony UX Turbo? • Main Course: 3 Building Blocks - Drive, Frames & Streams • Dessert: Turbo In Action: Example Application
  16. Appetizer What is Symfony UX Turbo?

  17. None
  18. None
  19. What is Hotwire? • Turbo: modern applications without writing JavaScript

    • Stimulus: simple JS framework if you need JavaScript • Strada: web/native app bridge
  20. None
  21. Installation composer require symfony/ux-turbo symfony/ux-turbo-mercure yarn install --force yarn encore

    dev
  22. Main Course Drive, Frames & Streams

  23. Turbo Drive navigates to new pages without full page reloads.

  24. Turbo Drive 1. User clicks on a link or submits

    a form. 2. intervenes and stops the default action (visit new page). 3. fetches the page using fetch in the background. 4. Replaces <body> and merges <head>. 5. Updates browser history with history.pushState.
  25. Clicking Links <a href="{{ path('create_room') }}">Create Room</a> <a href="{{ path('create_room')

    }}" data-turbo-action="replace">Create Room</a>
  26. Handling Forms #[Route('/rooms/create', name: 'create_room')] public function create(Request $request, EntityManagerInterface

    $entityManager): Response { $room = new Room(); $form = $this->createForm(RoomType::class, $room); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $entityManager->persist($form->getData()); $entityManager->flush(); return $this->redirectToRoute('rooms', [], Response::HTTP_SEE_OTHER); } return $this->renderForm('room/create.html.twig', ['form' => $form]); }
  27. Learn more about Turbo Drive at https://turbo.hotwire.dev/ handbook/drive

  28. Turbo Frames are independent content blocks.

  29. Turbo Frames • quite similar to iframes • lets a

    part of the page have their own "context" • clicking links & submitting forms inside a frame only replaces the content inside that frame • pages can contain multiple frames
  30. Scoping Content and Actions <h1>Rooms</h1> <turbo-frame id="create-room"> <a href="{{ path('create_room')

    }}">Create Room</a> </turbo-frame> <ul id="room-list"> {% for room in rooms %} {{ include('fragments/_room_list_item.html.twig', {room: room}) }} {% endfor %} </ul>
  31. <!-- Current page --> <turbo-frame id="create-room"> <a href="{{ path('create_room') }}">Create

    Room</a> </turbo-frame> <!-- Response body --> <h1>Create New Room</h1> <turbo-frame id="create-room"> {{ form(form) }} </turbo-frame>
  32. <!-- Current page --> <turbo-frame id="create-room"> <a href="{{ path('create_room') }}">Create

    Room</a> </turbo-frame> <!-- Response body --> <h1>Create New Room</h1> <turbo-frame id="create-room"> {{ form(form) }} </turbo-frame>
  33. <!-- Current page --> <turbo-frame id="create-room"> {{ form(form) }} </turbo-frame>

    <!-- Response body --> <h1>Create New Room</h1> <turbo-frame id="create-room"> {{ form(form) }} </turbo-frame>
  34. Turbo Frames and ESI • Just like in Rails, Turbo

    Frames can also lazily load frames on the client-side. • The Symfony integration takes advantage of the already existing ESI features Symfony brings.
  35. Lazily Loading a Frame <turbo-frame id="latest-news" src="{{ fragment_uri(controller('App\\Controller\\NewsController::latest')) }}"> </turbo-frame>

    https://symfony.com/doc/current/http_cache/esi.html
  36. Turbo Streams stream server-side changes to your users' browsers.

  37. Turbo Streams • easily add real-time capabilities to your application

    • HTML fragments are streamed to the browser • server pushes changes to connected users using a real-time protocol (like Mercure or Websocket) • Symfony integration uses Mercure
  38. None
  39. Listening on Streams <ul id="room-list" {{ turbo_stream_listen('my_stream_key') }}> {% for

    room in rooms %} {{ include('fragments/_room_list_item.html.twig', {room: room}) }} {% endfor %} </ul>
  40. Publishing Changes #[Route('/rooms/create', name: 'create_room')] public function create( Request $request,

    EntityManagerInterface $entityManager, HubInterface $hub ): Response { // create form $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $entityManager->persist($form->getData()); $entityManager->flush(); $hub->publish( new Update( 'my_stream_key', $this->renderView('room/new.stream.html.twig', ['room' => $room]) ) ); return $this->redirectToRoute('view_room', ['id' => $room->getId()], Response::HTTP_SEE_OTHER); } return $this->renderForm('room/rename.html.twig', ['form' => $form]); }
  41. HTML Fragment <!-- room/new.stream.html.twig --> <turbo-stream action="append" target="room-list"> <template> <!--

    room list item content --> </template> </turbo-stream>
  42. Doctrine Integration • Symfony UX Turbo makes it easy to

    publish changes from Doctrine entities. • Maker bundle supports generating the necessary files (entity w/ attribute, fragment template etc.)
  43. Dessert Example Application

  44. Why I Like (Symfony UX) Turbo • Promising abstractions •

    React introduced components ➡ large client-side applications now maintainable • Drive, Frames & Streams are useful primitives that can be used together in various combinations • first-class integration with Symfony
  45. But It's Still Early Days • Hotwire has only been

    out for a few months. • Only large-scale user is Basecamp. • No established community best practices yet.
  46. Thanks for listening! (for real now) $kernel->terminate($request, $response);