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