# Frameworks Javascript - Tour d'horizon
## William Petit - S.C.O.P. Cadoles
---
## Les frameworks
## RiotJS
## ReactJS
## EmberJS
---
## RiotJS
### Présentation générale
### Concepts généraux
### Exercice
---
## Présentation générale (1)
- Publication: Septembre 2013
- Mainteneurs principaux: [@GianlucaGuarini](https://github.com/GianlucaGuarini) et [@tipiirai](https://github.com/tipiirai) (projet communautaire)
- License: MIT
---
## Présentation générale (2)
|Avantages|Inconvénients|
|:-|:-|
|Une librairie et pas un framework| Une librairie et pas un framework|
| Projet communautaire | Projet communautaire |
| Orientée "composant" | |
| API minimaliste |
| Prêt à l'emploi (pas de transpilage nécessaire en développement) |
| Routeur "frontend" disponible |
---
## Concepts généraux
### Composants ("tags")
### Observable
---
## Composants
### Structure et utilisation d'un composant
### Templating
### Styles de composant
### Évènements du DOM
### Cycle de vie du composant
---
## Structure d'un composant
```html
{ opts.foo }
```
---
## Utilisation du composant
### Compilation dans le navigateur
```html
```
---
## Templating
```html
{ foo }
{ item.text }
foo est égal à "bar" !
```
---
## Styles de composant
### Feuille de style
### Styles et classes dynamiques
---
## Feuille de style
```html
Foo
Bar
```
---
## Styles et classes dynamiques
```html
Foo
Bar
```
---
## Évènements du DOM
```html
```
---
## Cycle de vie du composant
```html
```
---
## Observable (1)
### Déclaration d'un "service"
```javascript
// services/auth.js
class MyAuthService {
constructor() {
riot.observable(this)
this.user = null;
}
login(user, password) {
var form = new FormData();
form.append('login', login);
form.append('password', password);
return fetch('/login', { method: 'POST', form: form })
.then(res => res.json())
.then(user => {
this.user = user;
this.trigger('loggedIn', user);
})
;
}
isLoggedIn() {
return this.user != null;
}
}
const myAuth = new MyAuthService();
```
---
## Observable (2)
### Utilisation du service
```html
```
---
## Exercice
Implémenter une application minimaliste de gestion de tâches avec Riot. Cette application doit comprendre les fonctionnalités suivantes:
- Ajouter une nouvelle tâche (champs texte) avec une priorité parmi les suivantes: "haute", "moyenne", "basse" et un statut: "en cours" ou "terminé"
- Marquer une tâche comme "terminée".
- Afficher la liste des tâches en attente, triées par priorité décroissante + tâches terminées en dernier.
- Les tâches doivent être persistées dans le "Local Storage" du navigateur.
---
## ReactJS
### Présentation générale
### Concepts généraux
### Prise en main
---
## Présentation générale (1)
- Publication: Mars 2013
- Mainteneur principal: Facebook
- License: MIT
---
## Présentation générale (2)
|Avantages|Inconvénients|
|:-|:-|
|Grande communauté, très active | Changement de license puis rétropédalage en 2017 |
| 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 |
| Projet "React Native" |
| API stable |
| Virtual DOM ([voir cet article](https://auth0.com/blog/face-off-virtual-dom-vs-incremental-dom-vs-glimmer/)) |
| Moteurs "lightweight" compatibles |
---
## Concepts généraux
### Composants
### L'architecture Flux
---
## Composants
### Structure d'un composant
### Props
### State
### Évènements du DOM
### Cycle de vie d'un composant
---
## Structure d'un composant
```jsx
// components/hello-world.js
import React from 'react'
class HelloWorld extends React.Component {
render() {
return (
Hello World
Welcome to React
)
}
}
export default HelloWorld
```
---
## Props
```jsx
// components/props-example.js
import React from 'react'
class PropsExample extends React.Component {
render() {
return (
{ this.props.text }
)
}
}
export default PropsExample
// app.js
import React from 'react'
import ReactDOM from 'react-dom'
import PropsExample from './components/props-example'
const mountpoint = document.getElementById('props-example');
ReactDOM.render(, mountpoint)
```
---
## State
```jsx
// components/clock.js
import React from 'react'
class Clock extends React.Component {
constructor(props) {
// On fait appel au constructeur de la classe
// parente
super(props)
// Initialisation du "state" du composant
this.state = {
time: new Date()
}
// On appelle la méthode tick() du composant
// toutes les secondes
setInterval(this.tick.bind(this), 1000);
}
// Méthode de rendu du composant
render() {
return (
Time: { this.state.time.toString() }
)
}
// La méthode tick() met à jour le state du composant avec
// la date courante
tick() {
this.setState({ time: new Date() });
}
}
export default Clock
```
---
### Évènements du DOM
```jsx
// 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
```
---
## Cycle de vie d'un composant
```
// components/clock-lifecycle.js
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
```
---
## L'architecture Flux
![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
Implémenter une application minimaliste de gestion de tâches avec React (sans l'architecture Flux). Cette application doit comprendre les fonctionnalités suivantes:
- Ajouter une nouvelle tâche (champs texte) avec une priorité parmi les suivantes: "haute", "moyenne", "basse" et un statut: "en cours" ou "terminé"
- Marquer une tâche comme "terminée".
- Afficher la liste des tâches en attente, triées par priorité décroissante + tâches terminées en dernier.
- Les tâches doivent être persistées dans le "Local Storage" du navigateur.
---
## EmberJS
### Présentation générale
### Concepts généraux
### Initialisation d'un projet
### Exercice
---
## Présentation générale (1)
- Publication: Décembre 2011
- Mainteneur principal: [Ember Core Team](https://emberjs.com/team/) (projet communautaire)
- License: MIT
---
## Présentation générale (2)
|Avantages|Inconvénients|
|:-|:-|
|"Clés en main"|"Opiniatre"|
|API stable|Structure MVC classique|
|Long-term support (1 an)|
|Documentation exhaustive|
|Ember Data|
|Ember Inspector|
|Ember CLI|
---
## Concepts généraux
### Ember CLI
### Routes
### Templates
### Modèles
### Contrôleurs
---
## Ember CLI
### Générer une nouvelle application
```shell
npm install ember-cli -g
ember new my-app
cd my-app
ember serve
```
---
## Routes (1)
### Générer une nouvelle route
```shell
ember generate route posts
```
---
## 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 }}
{{!-- Utilisation d'un "helper" --}}
{{helper params... }}
{{!-- Définition d'un "block" --}}
{{#block params... }}
...
{{/block}}
{{!-- Helpers imbriqués --}}
{{sum (multiply 2 4) 2}}
```
---
## 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 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"
}
});
```
---
## Exercice
Implémenter une application minimaliste de gestion de tâches avec EmberJS. Cette application doit comprendre les fonctionnalités suivantes:
- Ajouter une nouvelle tâche (champs texte) avec une priorité parmi les suivantes: "haute", "moyenne", "basse" et un statut: "en cours" ou "terminé"
- Marquer une tâche comme "terminée".
- Afficher la liste des tâches en attente, triées par priorité décroissante + tâches terminées en dernier.
- Les tâches doivent être persistées dans le "Local Storage" du navigateur.
---
# Licence
## CC BY-NC-SA 3.0 FR
[Creative Commons - Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 France](https://creativecommons.org/licenses/by-nc-sa/3.0/fr/)