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. How to connect Symfony2 as backend and AngularJS as frontend solution? 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.
10. SPA Arhitecture
Backend (rest api) with Symfony2
Frontend with AngularJs
Separation or combination?
11. SPA Arhitecture
Backend (rest api) with Symfony2
Frontend with AngularJs
Separation or combination?
12. RESTful ws
Simpler than SOAP & WSDL
Resource-oriented (URI)
Principles:
HTTP methods (idempotent & not)
stateless
directory structure-like URIs
XML or JSON (or XHTML)
GET (vs HEAD), POST, PUT (vs PATCH), DELETE, OPTIONS
13. Building Rest API with SF2
There is bundle for everything in Sf2. Right?
So lets use some of them!
14. Building Rest API with SF2
What we need?
JMSSerializerBundle
FOSRestBundle
NelmioApiDocBundle
15. 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)
16. 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
24. Templating
var phpDayDemoApp = angular.module('phpDayDemoApp', [],
function($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
Now we can use
{% block content %}
[[ message ]] {# rendered by AngularJS #}
{% end block %}
Tweak Twig lexer delimiters? Bad idea.
26. 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]);
33. Translations
AngularJS has its own translation system
I18N/L10N . But it might be interesting to monitor
and centralize translations from your backend
Symfony.
JMSTranslationBundle
34. Forms
Symfony Forms <3
We don't want to throw them away
Build custom directive
35. 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>
40. Submitting forms?
When the AngularJS $http service POSTs data the header application/x-www-form-urlencoded
is never set (unlike jQuery’s $.ajax()). Also, the $http data is not serialized
when sent. Both of these facts mean that the $_POST variable is never set properly by
php. Without the $_POST variable Symfony’s built in form handling cannot be used.
The fix is actually pretty simple:
angular needs to forced into setting a header
the data needs to be serialized
and the data needs to be normalized into a multidimensional array.
42. Same-origin policy
For security reasons, web browsers prevent JavaScript to Ajax requests (XMLHttpRequest)
to other areas ( Same-origin policy ).
An example of exception thrown by the browser:
XMLHttpRequest cannot load http://api.mondomaine.com/v1/maressource.json.
Invalid HTTP status code 405
Using JSOP (Json with Padding) – easy with FOSRestApi
Configure server (simple and stupid)
<VirtualHost *:80> ServerName mon-appli-angular.com DocumentRoot
/var/www/some-ng-app/ Alias /api /var/www/some-ng-app/ <Directory xxxx>
</Directory> </VirtualHost> </VirtualHost>
Use Cors
CORS (Cross-origin resource sharing) is an elegant and standardized response to allow
Cross-domain requests.
Be careful though, the CORS mechanism is not supported by all browsers (guess
which ) …
43. Testing
Symfony and AngularJS are designed to test. So write test
Behat
PHPUnit
PHPSpec
Jasmine
…
Or whatever you want just write tests
44. 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 options are static, you can simply build that list in twig. Forms in
45. And remember
Keep controllers small and stupid, master Dependency
injection, delegate to services and events.