Skip to content

Commit

Permalink
Comments and README file w/ apiService refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
thierry2015 committed Jan 21, 2018
1 parent 94d4c62 commit 6da9665
Show file tree
Hide file tree
Showing 12 changed files with 785 additions and 87 deletions.
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# AngularJS Boilerplate

Ce boilerplate est fait pour bien comprendre les différents concepts
de AngularJS et quelques-unes de ces meilleures pratiques. Toute la
documentation se retrouve directement dans le code JavaScript et HTML.

## Installation
Pour installer et partir l'application, voici les étapes à suivre:

#### installation des dépendances
Pour installer les dépendances NPM:

`npm install`
ou
`npm i`

## Serveurs

#### Démarrage du serveur PHP
Le serveur fonctionne en PHP (une version >=5.4 devrait normalement bien
fonctionner mais a été testé avec 7.1.9). Par soucis de simplicité, les
deux commandes de démarrages du serveur vont être via le serveur interne
de PHP.

Il est important de noter que le serveur occupera le processus du terminal
en entier. Il faudra ouvrir deux instances du terminal pour faire fonctionner
les deux serveurs.

Pour démarrer le serveur PHP, voici les instructions:

`cd /path/to/project`

`cd server/public`

`php -S 0.0.0.0:8002`

Le serveur sera hébergé via http://localhost:8002. Le nom du post est
important puisqu'il est ainsi configuré au niveau de AngularJS.

#### Démarrage du serveur AngularJS

Pour garder le démarrage du serveur simple, la même ligne de commande
sera utilisée: le serveur sera donc PHP plutôt que NodeJS

`cd /path/to/project`

`cd boilerplate`

`php -S 0.0.0.0:8000`

Le port a très peu d'importance, tant qu'il ne s'agit pas du port 8002
(déjà utilisé par le serveur) et qu'il ne rentre pas en conflit avec
d'autres systèmes (WAMP/MAMP/XAMPP, YouTrack, etc.)

On peut maintenant accéder au projet boilerplate via http://localhost:8000
Pour bien tester le serveur, on peut maintenant y accéder via http://localhost:8002/post.php
Une réponse JSON devrait alors apparaître, comportant 3 posts.

#### Interruption d'un serveur

Pour arrêter le serveur, utiliser `ctrl+C` sur Windows ou `cmd+C` sur Mac au niveau du terminal.
65 changes: 64 additions & 1 deletion boilerplate/index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
<!doctype html>
<!DOCTYPE html>
<!--
Pour une application AngularJS qui couvre autant le contenu que le méta-contenu,
le ngApp devrait se retrouver sur le <html> plutôt que sur <body>. Ça permettra
de modifier le contenu de <title>, par exemple. Le ngApp doit être placé sur le
parent (root, d'où le $rootScope) du document virtuel d'AngularJS.
-->
<html lang="en">
<head>
<!-- Basic head tags -->
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>AngularJS boilerplate</title>

<!-- Style dependencies -->
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">

<!-- Custom style -->
<link rel="stylesheet" href="css/style.css">

<!-- Angular library -->
Expand All @@ -16,15 +27,67 @@
<script src="node_modules/@uirouter/angularjs/release/angular-ui-router.min.js"></script>

<!-- Application scripts -->

<!-- L'application est chargée dès le début pour initialiser le module principal. -->
<script src="js/app.js"></script>
<!--
Les scripts devraient ensuite être chargés selon leurs interdépendances.
Cependant, AngularJS gère lui-même l'exécution des différentes composantes dans l'ordre approprié.
Il s'agit donc seulement d'une bonne pratique, de suivre l'ordre d'initialisation du moteur AngularJS,
mais ça permet également d'éviter les mauvaises surprises.
-->

<!-- Les constantes ne dépendent normalement de rien, donc ils sont chargées en premier. -->
<script src="js/constants.js"></script>

<!--
On crée ensuite les service providers (comme $stateProvider), qui sont créés avant la phase de configuration.
Un provider est une sorte de créateur. Il prend les commandes (config) qui permettrons de créer sur mesure un service.
Un provider, pour fournir un service, doit posséder une méthode $get, qui est une méthode avec injection qui retourne un service.
Le service est donc créé dans un contexte donné (le provider) plutôt que sans contexte (via factory())
-->
<!-- <script src="js/providers.js"></script>
<!--
On charge ensuite les scripts de configuration. Les routes font partie des configurations.
À ce stade-ci, aucun controller, service, filter, directive ou autre n'a été créé, à l'exception des providers.
-->
<script src="js/routes.js"></script>

<!--
À partir de maintenant, AngularJS a booté le module. Le reste des éléments (controllers, services, etc.)
vont être instanciés selon le besoin. Si un service FooService existe dans le module mais ne sert jamais
(du moins durant le runtime), il ne sera jamais instancié pour sauver de la mémoire et accélérer le cycle
$digest. Donc à partir d'ici, l'injection de dépendance ne prend plus des providers, comme en configuration,
mais des services, des values, des constants et des provided services (provenant de $get d'un provider)
-->
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<script src="js/directives.js"></script>

<!--
Rendu ici, on a chargé tout ce qu'il fallait pour que l'application puisse rouler correctement.
On peut cependant rajouter une étape supplémentaire, semblable à $(document).ready(function(){});
Cette étape permet d'exécuter un script une seule fois lors du démarrage de AngularJS. la méthode
angular.module('foo').run() permet d'ajouter un écouteur au ready de AngularJS, qui utilise aussi
des dépendances.
-->
<!-- <script src="js/run.js"></script> -->
</head>
<!--
À partir d'ici, le module est bootstrappé au <body>. Il est possible de bootstrsappé un module à une
partie du document (sans jamais avoir une application dans une application) manuellement, par JS.
Voici un exemple de ngApp manuel avec le module "foo" sur le body:
angular.element(function() {
angular.bootstrap(document.body, ["foo"]);
});
Il est important de noter que lorsqu'AngularJS bootstrap un module, on ne peut plus le modifier.
Donc, aucun ajout de controller, de services, etc., n'est possible après le bootstrap.
Autre élément important, ngApp ne peut se retrouver sur une directive qui sera remplacé (donc {replace: true}).
-->
<body ng-app="app">
<!-- Ici, j'appelle une directive custom, qui met en place le layout (voir js/directives.js). -->
<layout></layout>
</body>
</html>
38 changes: 37 additions & 1 deletion boilerplate/js/app.js
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
angular.module('app', ['ui.router']);
/*
Je crée mon module principal, "app". Le deuxième paramètre est un array de modules sur lesquels repose mon module.
Une bonne façon de diviser une grande application ou une librairie AngularJS moindrement complexe est en la
subdivisant en différents sous-modules.
Voici un exemple concret d'architecture en plusieurs modules :
// Mon module d'API. Il ne dépend d'aucun module, sinon d'AngularJS (qui est automatiquement injecté).
angular.module('app.api', [])
.factory('apiService', [...]);
// Mon module qui gère les posts. Il utilise l'API.
angular.module('app.content.post', ['app.api'])
.controller('app.post.indexCtrl', [...]);
// Mon module qui gère les pages. Il utilise l'API.
angular.module('app.content.page', ['app.api'])
.controller('app.page.indexCtrl', [...]);)
// Mon module gère le contenu. Il utilise les posts et les pages.
angular.module('app.content', [
'app.content.post',
'app.content.page'
]);
// Mon module global. Il utilise du contenu.
angular.module('app', ['app.content']);
Lorsqu'on fait une architecture en module, il est souvent intéressant de faire un module qui inclut
l'ensemble des sous-dépendances (comme "app.content", qui inclut l'API, les posts et les pages).
Il devient facile d'en faire la maintenance et de comprendre facilement les dépendances de l'application.
Dans le cas de ce module, il utilise la librairie "ui.router" (@uirouter/angularjs pour npm)
*/
angular.module('app', ['ui.router']);
8 changes: 8 additions & 0 deletions boilerplate/js/constants.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
/*
Ici, on crée des constantes qui peuvent servir dans différents modules custom. Les constantes devraient
en principe servir uniquement au module conserné, pas à ses dépendances. S'il advient que le module dépendant
soit utiliser dans un autre contexte, la constante ne serait plus injectable puisqu'elle ne sera jamais définie.
Dans le cas où j'avais un sous-module "app.api" (comme dans l'exemple de app.js), la constante "API_ROOT"
aurait dû être attaché à "app.api", pas à "app".
*/
angular.module('app')
.constant('API_ROOT', 'http://localhost:8002');
45 changes: 43 additions & 2 deletions boilerplate/js/controllers.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,65 @@
/*
Ici, je déclare mes controllers reliés à mes routes. Il y a toujours deux choix lors de l'assignation
d'un controller à une composante, comme une route ou une directive. Soit on crée un controller assigné
au module via controller() en indiquant son nom sur la propriété controller de la composante (comme
c'est fait actuellement), soit on déclare le controller directement au niveau de la composante.
L'avantage de déclarer le controller à part est d'une part le suivi du pattern MVC, où les composantes
sont séparées en tout temps et doivent être autonomes, sans considérer le contexte. Également, la
déclaration à part de la composante agit exactement comme la déclaration d'une variable globale plutôt
que l'utilisation directe d'une valeur: il y a possibilité de réutilisation par d'autres composantes.
Un exemple de réutilisation est lorsqu'un controller sert à une route (comme app.post.create) pour
l'affichage d'une portion de page qui affiche un formulaire pour créer un post, mais que le même
formulaire (ou presque) et le même behaviour doit être réutiliser dans un popup "shortcut" via un
bouton flottant dans la page de création d'un post.
*/
angular.module('app')
.controller('appCtrl', [
'$rootScope',
function ($rootScope) {
'$scope',
function ($scope) {
/*
Ce controller sert à châpeauter le reste des routes du namespace "app".
On va gérer le model "currentUser", "notifications" et tous les autres
models globaux. Il est important de souligner qu'à ce stade, la route
est déjà "resolved", donc l'ensemble des inconnus comme le currentUser,
les notifications de départ et les autres (qui proviennent souvent d'un
API ou d'un repository comme le localStorage) sont maintenant connus.
Ils devraient normalement avoir été resloved depuis la déclaration de
"resolve" de la route et ensuite utilisés dans le $scope du controller.
Comme ce parent est le "root" du namespace des autres routes (puisqu'ils
sont nommés avec la notation en point "app.foo.bar"), les $scope enfants
des autres routes vont hérités de ce $scope.
*/
}
])
.controller('indexCtrl', [
'$scope',
function ($scope) {
/*
Ici, il ne s'agit que du controller de la page d'accueil.
La page des posts n'héritera pas de ce $scope puisqu'ils
sont frères et non parents.
*/

}
])
.controller('post.indexCtrl', [
'$scope', 'posts',
function ($scope, posts) {
/*
Ici, les posts ont été resolved. Un resolve peut être injecté
comme n'importe quel composante injectable.
*/
$scope.posts = posts;
}
])
.controller('post.showCtrl', [
'$scope', 'post',
function ($scope, post) {
/*
Le post a aussi été resoled depuis la route.
*/
$scope.post = post;
}
]);
Loading

0 comments on commit 6da9665

Please sign in to comment.