diff --git a/developpement/intro_javascript/presentation/slides.md b/developpement/intro_javascript/presentation/slides.md
index 9b4ae99..08d053b 100644
--- a/developpement/intro_javascript/presentation/slides.md
+++ b/developpement/intro_javascript/presentation/slides.md
@@ -379,12 +379,12 @@ Implémenter une application minimaliste de gestion de tâches avec Riot. Cette
|Avantages|Inconvénients|
|:-|:-|
|Grande communauté, très active | Changement de license puis rétropédalage en 2017 |
-| Maintenu et utilisé par Facebook en interne | Nécessité de mettre en place un pipeline de transpilage |
+| Maintenu et utilisé par Facebook | Nécessité de mettre en place un pipeline de transpilage |
| Architecture Flux | Architecture Flux |
| Possibilité de faire des applications "isomorphiques" avec NodeJS |
-| "React Native" |
+| Projet "React Native" |
| API stable |
-| Virtual DOM |
+| Virtual DOM ([voir cet article](https://auth0.com/blog/face-off-virtual-dom-vs-incremental-dom-vs-glimmer/)) |
| Moteurs "lightweight" compatibles |
---
@@ -601,6 +601,8 @@ export default Clock
![center](./img/flux-simple-f8-diagram-with-client-action-1300w.png)
+[Voir une description des concepts](https://github.com/facebook/flux/tree/master/examples/flux-concepts)
+
---
## Exercice
@@ -641,6 +643,8 @@ Implémenter une application minimaliste de gestion de tâches avec React (sans
|Long-term support (1 an)|
|Documentation exhaustive|
|Ember Data|
+|Ember Inspector|
+|Ember CLI|
---
@@ -651,7 +655,6 @@ Implémenter une application minimaliste de gestion de tâches avec React (sans
### Templates
### Modèles
### Contrôleurs
-### Composants
---
@@ -668,18 +671,53 @@ ember serve
---
-## Routes
+## Routes (1)
### Générer une nouvelle route
```shell
-ember generate route foo
+ember generate route posts
```
---
-## Templates
+## Routes (2)
+### Attacher un/plusieurs modèles à une route
+
+```javascript
+// app/routes/posts.js
+import Route from '@ember/routing/route';
+
+export default Route.extend({
+ model() {
+ return this.get('store').findAll('post');
+ }
+});
+```
+
+---
+
+## Routes (3)
+
+### Effectuer une redirection
+
+```javascript
+// app/routes/index.js
+import Route from '@ember/routing/route';
+
+export default Route.extend({
+ beforeModel() {
+ this.transitionTo('posts');
+ }
+});
+```
+
+---
+
+## Templates (1)
+
+### Syntaxe générale
```handlebars
{{!-- Interpolation de variable --}}
{{ myVar }}
@@ -691,16 +729,183 @@ ember generate route foo
{{#block params... }}
...
{{/block}}
+
+{{!-- Helpers imbriqués --}}
+{{sum (multiply 2 4) 2}}
```
---
-## Modèles
+## Templates (2)
+
+### "Blocks" de base
+```handlebars
+{{!-- Condition --}}
+{{if myVar}}
+ Maybe...
+{{else}}
+ Or not.
+{{/if}}
+
+{{!-- Boucle --}}
+{{#each people as |person|}}
+
Hello, {{person.name}}!
+{{/each}}
+```
+
+---
+
+## Templates (3)
+
+### "Helpers" spécifiques
+```handlebars
+{{!-- Affiche myVar[key] (accès dynamique aux variables exposées dans le controleur) --}}
+{{get myVar "key"}}
+
+{{!-- Affiche le contenu de la (sous) route --}}
+{{outlet}}
+
+{{!-- Modèle exposé par la route --}}
+{{model}}
+
+{{!-- Affiche un lien vers la route donnée --}}
+{{link-to routeName}}
+
+{{!-- Attacher une action à un élément (onclick) --}}
+
+```
+---
+
+## Contrôleurs (1)
+
+### Générer un contrôleur
+
+```shell
+ember generate controller posts
+```
+---
+
+## Contrôleurs (2)
+
+### Exposer des variables pour le template
+
+```shell
+import Controller from '@ember/controller';
+
+export default Controller.extend({
+ foo: "bar" // {{ foo }} dans le template associé
+});
+```
+---
+
+## Contrôleurs (3)
+
+### Actions
+
+```shell
+import Controller from '@ember/controller';
+
+export default Controller.extend({
+
+ foo: "bar"
+
+ actions: {
+ myAction: function() {
+ // On modifie la valeur de "foo"
+ // Si "foo" est utilisé dans le template, il sera automatiquement mis à jour
+ this.set("foo", "hello world");
+ }
+ }
+});
+
+```
+---
+
+## Modèles (1)
### Générer un nouveau modèle
```shell
-ember generate model bar
+ember generate model post
+```
+---
+
+## Modèles (2)
+
+### Définition d'un modèle
+
+```javascript
+// app/models/comment.js
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+
+ // Simple attributs
+ text: DS.attr('string'),
+
+ // Relations
+ post: DS.belongsTo('post')
+
+});
+
+// app/models/post.js
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+
+ // Simples attributs
+ title: DS.attr('string'),
+ content: DS.attr('string'),
+
+ // Relations
+ comments: DS.hasMany('comment')
+
+});
+```
+---
+
+## Modèles (3)
+
+### Manipuler une instance de modèle
+
+```javascript
+// app/controllers/posts.js
+
+// Créer une nouvelle instance
+var post = store.createRecord('post', {
+ title: 'Mon article',
+ content: 'Lorem ipsum'
+});
+
+// Modifier l'instance
+post.set('title', 'Foo bar');
+
+// Sauvegarde de l'instance
+post.save().then(() => console.log("Post saved !"));
+
+// Supprimer l'instance
+post.destroyRecord().then(() => console.log("Post deleted !"));
+
+```
+---
+
+## Modèles (4)
+
+### Effectuer des requêtes sur le "store"
+
+```javascript
+// app/controllers/posts.js
+
+// Récupérer toutes les instances de "post"
+var posts = this.get('store').findAll('post');
+
+// Requête avec filtre
+var posts = this.get('store').query('post', {
+ filter: {
+ title: "Foo Bar"
+ }
+});
+
```
---
diff --git a/developpement/intro_javascript/ressources/ember-demo b/developpement/intro_javascript/ressources/ember-demo
new file mode 160000
index 0000000..51eafba
--- /dev/null
+++ b/developpement/intro_javascript/ressources/ember-demo
@@ -0,0 +1 @@
+Subproject commit 51eafba3eadbbce1833b76d11667461e14cb5197
diff --git a/developpement/intro_javascript/ressources/react-demo/.babelrc b/developpement/intro_javascript/ressources/react-demo/.babelrc
new file mode 100644
index 0000000..5d1b702
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/.babelrc
@@ -0,0 +1 @@
+{ "presets": ["react", "es2015"] }
diff --git a/developpement/intro_javascript/ressources/react-demo/.gitignore b/developpement/intro_javascript/ressources/react-demo/.gitignore
new file mode 100644
index 0000000..07e6e47
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/.gitignore
@@ -0,0 +1 @@
+/node_modules
diff --git a/developpement/intro_javascript/ressources/react-demo/app.js b/developpement/intro_javascript/ressources/react-demo/app.js
new file mode 100644
index 0000000..41f0278
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/app.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import HelloWorld from './components/hello-world'
+import PropsExample from './components/props-example'
+import Clock from './components/clock-lifecycle'
+import Counter from './components/counter'
+
+ReactDOM.render(, document.getElementById('app'))
+ReactDOM.render(, document.getElementById('clock'))
+ReactDOM.render(, document.getElementById('counter'))
diff --git a/developpement/intro_javascript/ressources/react-demo/components/clock-lifecycle.js b/developpement/intro_javascript/ressources/react-demo/components/clock-lifecycle.js
new file mode 100644
index 0000000..748a7ad
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/components/clock-lifecycle.js
@@ -0,0 +1,34 @@
+import React from 'react'
+
+class Clock extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ time: new Date()
+ }
+ }
+
+ componentDidMount() {
+ console.log('mount')
+ this.intervalId = setInterval(this.tick.bind(this), 1000);
+ }
+
+ componentWillUnmount() {
+ console.log('unmount')
+ clearInterval(this.intervalId);
+ }
+
+ render() {
+ return (
+ Time: { this.state.time.toString() }
+ )
+ }
+
+ tick() {
+ this.setState({ time: new Date() });
+ }
+
+}
+
+export default Clock
diff --git a/developpement/intro_javascript/ressources/react-demo/components/clock.js b/developpement/intro_javascript/ressources/react-demo/components/clock.js
new file mode 100644
index 0000000..6864344
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/components/clock.js
@@ -0,0 +1,26 @@
+import React from 'react'
+
+class Clock extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ time: new Date()
+ }
+ setInterval(this.tick.bind(this), 1000);
+ }
+
+ render() {
+ return (
+ Time: { this.state.time.toString() }
+ )
+ }
+
+ tick() {
+ this.setState({ time: new Date() });
+ }
+
+}
+
+
+export default Clock
diff --git a/developpement/intro_javascript/ressources/react-demo/components/counter.js b/developpement/intro_javascript/ressources/react-demo/components/counter.js
new file mode 100644
index 0000000..a6cf72d
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/components/counter.js
@@ -0,0 +1,41 @@
+// components/counter.js
+
+import React from 'react'
+
+class Counter extends React.Component {
+
+ constructor(props) {
+ // On fait appel au constructeur de la classe
+ // parente
+ super(props)
+
+ // Initialisation du "state" du composant
+ this.state = {
+ count: 0
+ }
+ }
+
+ // Méthode de rendu du composant
+ render() {
+ return (
+
+ Count: { this.state.count }
+
+
+
+ )
+ }
+
+ // La méthode increment() incrémente la valeur du compteur de 1
+ increment() {
+ this.setState(prevState => ({ count: prevState.count+1 }))
+ }
+
+ // La méthode decrement() incrémente la valeur du compteur de 1
+ decrement() {
+ this.setState(prevState => ({ count: prevState.count-1 }))
+ }
+
+}
+
+export default Counter
diff --git a/developpement/intro_javascript/ressources/react-demo/components/hello-world.js b/developpement/intro_javascript/ressources/react-demo/components/hello-world.js
new file mode 100644
index 0000000..450c630
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/components/hello-world.js
@@ -0,0 +1,17 @@
+import React from 'react'
+
+class HelloWorld extends React.Component {
+
+ render() {
+ return (
+
+
Hello World
+
Welcome to React
+
+ )
+ }
+
+}
+
+
+export default HelloWorld
diff --git a/developpement/intro_javascript/ressources/react-demo/components/props-example.js b/developpement/intro_javascript/ressources/react-demo/components/props-example.js
new file mode 100644
index 0000000..53aa2dc
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/components/props-example.js
@@ -0,0 +1,13 @@
+import React from 'react'
+
+class PropsExample extends React.Component {
+
+ render() {
+ return (
+ { this.props.text }
+ )
+ }
+
+}
+
+export default PropsExample
diff --git a/developpement/intro_javascript/ressources/react-demo/package.json b/developpement/intro_javascript/ressources/react-demo/package.json
new file mode 100644
index 0000000..eb8d0d6
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "react-demo",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "dependencies": {
+ "react": "^16.2.0",
+ "react-dom": "^16.2.0"
+ },
+ "devDependencies": {
+ "babel-cli": "^6.26.0",
+ "babel-loader": "^7.1.2",
+ "babel-preset-es2015": "^6.24.1",
+ "babel-preset-react": "^6.24.1",
+ "css-loader": "^0.28.8",
+ "path": "^0.12.7",
+ "style-loader": "^0.19.1",
+ "webpack": "^3.10.0",
+ "webpack-dev-server": "^2.10.0"
+ },
+ "scripts": {
+ "start": "webpack-dev-server",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/developpement/intro_javascript/ressources/react-demo/public/index.html b/developpement/intro_javascript/ressources/react-demo/public/index.html
new file mode 100644
index 0000000..11c1391
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/public/index.html
@@ -0,0 +1,16 @@
+
+
+
+ "Simple" React with Webpack
+
+
+
+ ---
+
+ ---
+
+ ---
+
+
+
+
diff --git a/developpement/intro_javascript/ressources/react-demo/webpack.config.js b/developpement/intro_javascript/ressources/react-demo/webpack.config.js
new file mode 100644
index 0000000..8f452d9
--- /dev/null
+++ b/developpement/intro_javascript/ressources/react-demo/webpack.config.js
@@ -0,0 +1,20 @@
+var webpack = require('webpack')
+var path = require('path')
+
+module.exports = {
+ entry: path.resolve(__dirname, 'app'),
+ output: {
+ path: __dirname + '/dist',
+ publicPath: '/',
+ filename: 'bundle.js'
+ },
+ devServer: {
+ contentBase: path.resolve(__dirname, 'public')
+ },
+ module: {
+ loaders: [
+ {test: /\.js$/, exclude: /node_modules/, loaders: ['babel-loader']},
+ {test: /(\.css)$/, loaders: ['style-loader', 'css-loader']}
+ ]
+ }
+}
diff --git a/developpement/symfony3/ressources/s3-micro/web/index.php b/developpement/symfony3/ressources/s3-micro/web/index.php
index 432531b..2e9e926 100644
--- a/developpement/symfony3/ressources/s3-micro/web/index.php
+++ b/developpement/symfony3/ressources/s3-micro/web/index.php
@@ -22,7 +22,6 @@ class AppKernel extends Kernel
{
return array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
- new Symfony\Bundle\TwigBundle\TwigBundle(),
);
}
@@ -32,15 +31,15 @@ class AppKernel extends Kernel
// PHP equivalent of config.yml
$c->loadFromExtension('framework', array(
'secret' => 'S0ME_SECRET',
- 'templating' => [
- 'engine' => [
- 'twig'
- ],
- ],
+ // 'templating' => [
+ // 'engine' => [
+ // 'twig'
+ // ],
+ // ],
));
- $c->loadFromExtension('twig', [
- 'default_path' => __DIR__.'/../templates',
- ]);
+ // $c->loadFromExtension('twig', [
+ // 'default_path' => __DIR__.'/../templates',
+ // ]);
}
// Configuration des routes