Symfony2 and AngularJS

32,173
-1

Published on

These are my slide from talk at PHPDay in Verona 20014.

Forget about classic website where UX is not so important. We are living in time where usability is one of the important thing if you are building some business client oriented web service. In locastic we are working on CRM that is based on Symfony2 as backend and AngularJS as frontend solution.

How to connect this two frameworks? What are best practices? What are disadvantageous? How to take best from both worlds? These are topics I will cover in my talk with real examples.

Published in: Software, Technology
2 Comments
73 Likes
Statistics
Notes
  • I'd love to see an example of using the form component with angular.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Could you make this demo-code available in a github repo? Sounds very interesting...
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
32,173
On Slideshare
0
From Embeds
0
Number of Embeds
16
Actions
Shares
0
Downloads
390
Comments
2
Likes
73
Embeds 0
No embeds

No notes for slide

Symfony2 and AngularJS

  1. 1. Symfony2 and AngularJS Antonio Perić-Mažar 16.05.2014, PHPday Verona https://joind.in/talk/view/11290
  2. 2. About me • Antonio Perić-Mažar, mag. ing. comp. • CEO @ locastic • Software developer, Symfony2 • Sylius Awesome Contributor :) • www.locastic.com • antonio@locastic.com • twitter: @antonioperic
  3. 3. Who we are? • locastic (www.locastic.com) • Web and mobile development • UI/UX design • Located in Split, Croatia
  4. 4. Our works?
  5. 5. Symfony2  PHP Framework, server side  MVC, HTTP  Toolbox, methodology  One of the most advanced PHP framework  Strong community  Easy to test it
  6. 6. Symfony2  Bundles  Components  Routing  Templating (Twig)  Forms  etc
  7. 7. AngularJS  Client-side JavaScript framework  Prescriptive, MVC (MVVM)  Makes creating UI easier thought data-binding  Good organizing and architecture  Learning curve, easy to start hard to master  $scope, modules, controllers, providers, services
  8. 8. AngularJS Core Two-way data binding JS: <span id=”someId”></span> document.getElementById('someId').text = 'locastic'; NG <span>{{someName}}<span> var someName = 'locastic';
  9. 9. AngularJS Core <html ng-app> <head> <script src=”angular.js”></script> <script src=”controller.js”></script> <head> <body> <div ng-controller=”HelloController”> <p>{{ greeting.text}}, World</p> </div> </body> </html> // controller.js function HelloController($scope) { $scope.gretting = {text: 'Hello'} } HTML template
  10. 10. AngularJS Core  Deep Linking  Dependency Injection  Directives <div class=”container”> <div class=”inner> <ul> <li>Item <div class=”subitem”>Item2</div> </li> </ul> </div> </div> <dropdown> <item>Item 1> <subitem >Item 2</subitem> </item> </dropdown>
  11. 11. + SPA (Singe Page App)
  12. 12. SPA  Aka SPI (Single Page interface)  desktop apps UX  HTML / JS / CSS / etc in single page load  fast  AJAX and XHR
  13. 13. SPA Arhitecture Backend (rest api) with Symfony2 Frontend with AngularJs Separation or combination?
  14. 14. UI == APP
  15. 15. Symfony2 AngularJS Usage, language Backend, PHP Frontend, Javascript Dependency Injection Yes Yes Templating Twig HTML Form component Yes Yes Routing component Yes Yes MVC Yes Yes Testable Yes Yes Services Yes Yes Events Yes Yes i18n Yes Yes Dependency management Yes Yes
  16. 16. RESTful ws Simpler than SOAP & WSDL Resource-oriented (URI) Principles: HTTP methods (idempotent & not) stateless directory structure-like URIs XML or JSON (or XHTML)
  17. 17. Building Rest Api with SF2 There is bundle for everything in Sf2. Right? So lets use some of them!
  18. 18. Building Rest Api with SF2 What we need? JMSSerializerBundle FOSRestBundle NelmioApiDocBundle
  19. 19. Building Rest API with SF2 JMSSerializerBundle (de)serialization via annotations / YAML / XML / PHP integration with the Doctrine ORM handling of other complex cases (e.g. circular references)
  20. 20. Building Rest Api with SF2 LocasticBundleTodoBundleEntityTodo: # exclusion_policy: ALL exclusion_policy: NONE properties: # description: # expose: true createdAt: # expose: true exclude: true deadline: type: DateTime<'d.m.Y. H:i:s'> # expose: true done: # expose: true serialized_name: status
  21. 21. Building Rest Api with SF2 fos_rest: disable_csrf_role: ROLE_API param_fetcher_listener: true view: view_response_listener: 'force' formats: xml: true json: true templating_formats: html: true format_listener: rules: - { path: ^/, priorities: [ html, json, xml ], fallback_format: ~, prefer_extension: true } exception: codes: 'SymfonyComponentRoutingExceptionResourceNotFoundException': 404 'DoctrineORMOptimisticLockException': HTTP_CONFLICT messages: 'SymfonyComponentRoutingExceptionResourceNotFoundException': true allowed_methods_listener: true access_denied_listener: json: true body_listener: true
  22. 22. Building Rest Api with SF2 /** * @ApiDoc( * resource = true, * description = "Get stories from users that you follow (newsfeed)", * section = "Feed", * output={ * "class" = "LocasticBundleFeedBundleEntityStory" * }, * statusCodes = { * 200 = "Returned when successful", * 400 = "Returned when bad parameters given" * } * ) * * @RestView( * serializerGroups = {"feed"} * ) */ public function getFeedAction() { $this->get('locastic_auth.auth.handler')->validateRequest($this->get('request')); return $this->getDoctrine()->getRepository('locastic.repository.story')->getStories($this->get('request')- >get('me')); }
  23. 23. Building Rest Api with SF2
  24. 24. Templating TWIG <3
  25. 25. Templating <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>{% block title %}Welcome!{% endblock %}</title> {% block stylesheets %} <!-- Place favicon.ico and apple-touch-icon.png in the root directory --> {#<link rel="stylesheet" href="{{ asset('css/normalize.css') }}">#} <link rel="stylesheet" href="{{ asset('css/main.css') }}"> <!-- load bootstrap and fontawesome via CDN --> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" /> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.0/css/font-awesome.css" /> <script src="{{ asset('js/vendor/modernizr-2.6.2.min.js') }}"></script> {% endblock %} <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" /> </head> <body> {% block body %}{% endblock %} {% block javascripts %} <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script> <script src="https://code.angularjs.org/1.2.16/angular-route.min.js"></script> <script src="{{ asset('js/main.js') }}"></script> {% endblock %} </body> </html>
  26. 26. Templating Problem: {{ interpolation tags }} - used both by twig and AngularJS
  27. 27. Templating {% verbatim %} {{ message }} {% endverbatim %}
  28. 28. Templating var phpDayDemoApp = angular.module('phpDayDemoApp', [], function($interpolateProvider) { $interpolateProvider.startSymbol('[['); $interpolateProvider.endSymbol(']]'); }); Now we can use {% block content %} [[ message ]] {# rendered by AngularJS #} {% end block %}
  29. 29. Templating Using assetic for minimize {% javascripts "js/angular-modules/mod1.js" "#s/angular-modules/mod2.js" "@AngBundle/Resources/public/js/controller/*.js" output="compiled/js/app.js" %} <script type="text/javascript" src="{{ asset_url }}"></script> {% endjavascripts %}
  30. 30. Templating Using assetic for minimize Since Angular infers the controller's dependencies from the names of arguments to the controller's constructor function, if you were to minify the JavaScript code for PhoneListCtrl controller, all of its function arguments would be minified as well, and the dependency injector would not be able to identify services correctly. Use an inline annotation where, instead of just providing the function, you provide an array. This array contains a list of the service names, followed by the function itself. function PhoneListCtrl($scope, $http) {...} phonecatApp.controller('phpDayCtrl', ['$scope', '$http', PhoneListCtrl]);
  31. 31. Managing routes Client side: ngRoute independent since Angular 1.1.6 hashbang #! & HTML5 mode <base href="/"> $locationProvider .html5Mode(true) .hashPrefix('!');
  32. 32. Managing routes http://localhost/todos http://localhost/#todos Resolving conflicts Fallback, managing 404
  33. 33. Managing routes Client side: // module configuration...$routeProvider.when('/todos/show/:id', { templateUrl : 'todo/show', controller : 'todoController' }) // receive paramssfugDemoApp.controller('todoController', function($scope, $http, $routeParams){ $scope.todo = {}; $http .get('/api/todo/show/' + $routeParams.id) .success(function(data){ $scope.todo = data['todo']; }); });
  34. 34. Managing routes Server side: locastic_rest_todo_getall: pattern: /api/get-all defaults: _controller: LocasticRestBundle:Todo:getAll locastic_rest_todo_create: pattern: /api/create defaults: _controller: LocasticRestBundle:Todo:create locastic_rest_todo_show: pattern: /api/show/{id} defaults: _controller: LocasticRestBundle:Todo:show
  35. 35. Translations AngularJS has its own translation system I18N/L10N . But it might be interesting to monitor and centralize translations from your backend Symfony.
  36. 36. Forms Symfony Forms <3 We don't want to throw them away Build custom directive
  37. 37. Forms sfugDemoApp.directive('ngToDoForm', function() { return { restrict: 'E', template: '<div class="todoForm">Form will be!</div>' } }); 'A' - <span ng-sparkline></span> 'E' - <ng-sparkline></ng-sparkline> 'C' - <span class="ng-sparkline"></span> 'M' - <!-- directive: ng-sparkline → Usage of directive in HTML: <ng-to-do-form></ng-to-do-form>
  38. 38. Forms sfugDemoApp.directive('ngToDoForm', function() { return { restrict: 'E', templateUrl: '/api/form/show.html' } });
  39. 39. Forms sfugDemoApp.directive('ngToDoForm', function() { return { restrict: 'E', templateUrl: '/api/form/show.html' } }); locastic_show_form: pattern: /form/show.html defaults: _controller: LocasticWebBundle:Default:renderForm public function renderFormAction() { $form = $this->createForm(new TodoType()); return $this->render('LocasticWebBundle::render_form.html.twig', array( 'form' => $form->createView() )); }
  40. 40. Forms Suprise!!!
  41. 41. Form Template behind directive <form class="form-inline" role="form" style="margin-bottom: 30px;"> Create new todo: <div class="form-group"> {{ form_label(form.description) }} {{ form_widget(form.description, {'attr': {'ng-model': 'newTodo.description', 'placeholder': 'description', 'class': 'form-control'}}) }} </div> <div class="form-group"> <label class="sr-only" for="deadline">Deadline</label> <input type="text" class="form-control" id="deadline" placeholder="deadline (angular-ui)" ng- model="newTodo.deadline"> </div> <input type="button" class="btn btn-default" ng-click="addNew()" value="add"/> </form>
  42. 42. Testing Symfony and AngularJS are designed to test. So write test Behat PHPUnit PHPSpec Jasmine … Or whatever you want just write tests
  43. 43. Summary The cleanest way is to separate backend and frontend. But there is some advantages to use both together. Twig + HTML works well. Assetic Bundle is very useful to minify bunches of Javascript files used by AngularJs Translation in the template. the data in the API payload does not need translation in most cases. Using Symfony I18N support for the template makes perfect sense. Loading of Option lists. Say you have a country list with 200+ options. You can build an API to populate a dynamic dropdown in angularjs, but as these
  44. 44. And remember Keep controllers small and stupid, master Dependency injection, delegate to services and events.
  45. 45. Thank you!
  46. 46. QA Please rate my talk https://joind.in/talk/view/11290 follow me on twitter: @antonioperic
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×