Attention, le contenu cet article est peut-être obsolète !
Amélioration du tutoriel backbone.js pour pouvoir communiquer avec le serveur de type REST et stocker ses donnéesPour faire suite au tutoriel pour pratiquer les bases de Backbone.js, voici un autre tutoriel pour améliorer notre application en la faisant communiquer avec le serveur.
Nous ne montrerons donc que le code modifié/ajouté.
Pour rappel notre application servait juste a saisir le nom d'une tâche et l'afficher dans une liste. Il y avait également un bouton supprimer pour la retirer. Cependant, aucune sauvegarde : au chargement de la page tout était à refaire.
Ici nous allons donc non seulement stocker notre information sur un serveur, mais également ajouter la possibilité de dire si cette tâche est terminée ou toujours en cours.
Nos tâches sont stockées dans une table nommée liste, contenu dans une base nommée taches. Chaque enregistrement comprend :
Le serveur est un script PHP simplifié pour comprendre les requêtes de type REST. Je passerai sur la méthode pour utiliser et vous connecter à la base...
Tout d'abord, déclarons une classe tache :
class tache {
private $id = 0;
private $titre = "";
private $statut = "En cours";
function getId() {
return $this->id;
}
function getTitre() {
return $this->titre;
}
function setId($id) {
$this->id = $id;
}
function setTitre($titre) {
$this->titre = $titre;
}
function getStatut() {
return $this->statut;
}
function setStatut($statut) {
$this->statut = $statut;
}
}
La première partie du script détecte le type de requête et lance la méthode appropriée :
//Infos de la requête
$method = $_SERVER['REQUEST_METHOD']; //Type de la requête
$demande = $_SERVER['REQUEST_URI']; //Url demandée
//Connexion BDD, je vous laisse déclarer et implémenter cette fonction comme bon vous semble
$conn = dbConnect();
//En fonction du type de la requête
switch ($method) {
case 'PUT':
rest_put();
break;
case 'POST':
rest_post();
break;
case 'GET':
rest_get();
break;
case 'DELETE':
rest_delete();
break;
default:
break;
}
La méthode DELETE
function rest_delete() {
global $conn; //Récuperer la connexion BDD
global $demande; //Récuperer les infos sur la demande
$arrayDemande = explode("/", $demande);
$detailDemande = $arrayDemande[count($arrayDemande)-1]; //On a à présent l'id de la tache à supprimer
$laTache = new tache();
$laTache->setId($detailDemande);
dbWrite($conn, "DELETE FROM liste WHERE id = :id", $laTache); //Méthode qui va s'occuper d'écrire dans la base, ici pour supprimer l'enregistrement
}
La méthode PUT
function rest_put() {
global $conn;
global $demande;
$arrayDemande = explode("/", $demande);
$detailDemande = $arrayDemande[count($arrayDemande)-1]; //id de la tache à supprimer
$detailDemande = @file_get_contents('php://input'); //Avec backone, l'objet jSon se trouve dans le body de la requête
$arrayDemande = json_decode($detailDemande); //On transforme notre objet jSon en objet PHP
$laTache = new tache();
$laTache->setTitre($arrayDemande->titre);
$laTache->setId($arrayDemande->id); //Facultatif
$laTache->setStatut("Terminé"); //Mise à jour du statut
$laRequeteSQL = "UPDATE liste SET statut=:statut WHERE id=:id"; //Requete de mise à jour
dbWrite($conn, $laRequeteSQL, $laTache); //Exécution de la requête
$var = '{"id" : "' . $laTache->getId() . '", "titre":"' . $arrayDemande->titre . '", "statut":"' . $laTache->getStatut() . '"}'; //On construit notre nouvel objet
echo $var; //Et on l'affiche pour backbone
}
La méthode POST
function rest_post() { //Création d'une tâche
global $conn;
$detailDemande = @file_get_contents('php://input'); //Contenu du body
$arrayDemande = json_decode($detailDemande);
$laTache = new tache();
$laTache->setTitre($arrayDemande->titre);
$laRequeteSQL = "INSERT INTO liste VALUES ('', :titre, :statut)"; //Requête d'insertion
$idNouvelleTache = dbWrite($conn, $laRequeteSQL, $laTache); //Récuperer l'id du nouvel enregistrement
$var = '{"id" : "' . $idNouvelleTache . '", "titre":"' . $arrayDemande->titre . '", "statut":"' . $laTache->getStatut() . '"}'; //Et on retourne l'objet en ajoutant l'id. //Cela permet à Backbone, donc côté client, de se mettre à jour.
echo $var;
}
Et enfin la méthode GET
function rest_get() { //Affichage de toutes les taches de la base
global $conn;
$laChainejSon = '[';
try {
$sql = "SELECT * from liste";
$req = $conn->query($sql);
while ($row = $req->fetch()) {
$laChainejSon = $laChainejSon . '{"titre":"' . $row['titre'] . '", "id":"' . $row['id'] . '", "statut" : "' . $row['statut'] .'"},';
}
$req->closeCursor();
} catch (Exception $e) {
echo "Probleme lecture des taches";
//echo $e;
exit;
}
$laChainejSon = substr($laChainejSon, 0, $laChainejSon - 1);
$laChainejSon = $laChainejSon . ']';
echo $laChainejSon; //On affiche un tableau d'objet jSon
}
Nous allons à présent pouvoir nous concenter sur ce qui nous intéresse ici : la partie client avec Backbone.js. Je vais donc présenter les modifications et ajouts de code par rapport à la version précédente de notre application.
Le model :
Les attributs id et statut font leur apparition :
var Tache = Backbone.Model.extend({
defaults: {
id: null,
titre: 'Une tâche vide',
statut: 'En cours'
}
});
Dans la collection, on ajoute l'URL du serveur REST :
var collectionDeTaches = Backbone.Collection.extend({
url: 'taches.php', //On spécifie l'URL de gestion de la collection
model: Tache
});
Dans la liste, on ajoute un identifiant (HTML), pour des raisons de commodité, mais surtout on lui demande d'afficher chaque élément de la collection :
var List = Backbone.View.extend({
tagName: 'ul', //Pour la création du conteneur de la liste : balise <ul>
id: 'maListeDeTaches', //Identifiant
// Constructeur
initialize: function () {
this.model.bind('add', this.ajouteObjet, this); //Ici , on réécrit la fonction ajout du modèle
//en la remplacant par celle déclarée plus bas dans la classe, puisque c'est ici que se verront
//les ajouts
},
render: function () {
this.model.each(this.addItem, this); //Rajout pour afficher chaque élément de la collection
return $(this.el);//Obligatoire sinon Vue non mise à jour!
},
//Méthode pour gérer l'ajout d'une nouvelle tâche
ajouteObjet: function (notreObjet) {
$(this.el).append(new Objet({
model: notreObjet
}).render());
}
});
On rajoute ici deux choses : un détail qui permet de vider la zone de saisie après validation, mais surtout la partie pour sauvegarder dans la collection et sur le serveur.
var Form = Backbone.View.extend({
tagName: 'form', //balise <form>
template: _.template('<input name="titre" type="text" placeholder="Saissez le titre de la tâche"></input>'),
initialize: function () {
},
render: function () {
$(this.el).html(this.template()); //Affiche le template de l'input
return $(this.el);//Obligatoire sinon vue non mise à jour!
},
events: {
'submit': 'envoi' //Evènement par défaut pour détecter la touche entrée, et la méthode à appeler le cas échéant
},
envoi: function (e) {
e.preventDefault(); //Méthode jQuery qui empêche l'appel direct de cette méthode (en fait l'action n'est pas exécutée)
//Pour chaque ajout, on envoie tout au model
var model = new Tache();
_.each($(e.target).serializeArray(), function (value) {
if (value.value !== '') {
model.set(value.name, value.value); //Création d'une nouvelle tâche
}
});
$(this.el).html(this.template()); //On réaffiche la vue pour vider le formulaire
this.model.add(model); //puis on appelle la méthode "add", spécifiée dans la liste (List) déclarée précédemment
model.save(null, {
success: function (model, response) {
console.log('success');
console.log(model);
console.log(response);
},
error: function (model, response) {
console.log('error');
},
wait: true
});
}
});
Afin de pouvoir travailler proprement, nous allons créer une nouvelle vue : refresh. Cela nous permettra de réactualiser la vue de la liste, via un bouton Refresh
var Refresh = Backbone.View.extend({
template: _.template('Refresh'),
initialize: function () {
},
render: function () {
$(this.el).html(
this.template());
return $(this.el);
},
events: {
'click': 'click'
},
click: function () {
this.model.fetch();
}
});
Maintenant, nous allons travailler sur des changements plus conséquents : l'Objet.
Nous ajoutons à son template une représentation de son champ statut, pour pouvoir cliquer dessus quand la tâche est terminée. Ensuite dans le constructeur, nous définissons la méthode destroy() qui sert à supprimer un objet. Nous rajoutons un évènement : le changement de statut. Enfin, il faut modifier la méthode de suppression qui remplace destroy() : laSuppression() et implémenter la nouvelle : changementStatut().
var Objet = Backbone.View.extend({
tagName: 'li',
template: _.template('<%= Tache.titre %> <span id="Statut">[<%= Tache.statut %>]</span><span id="Effacer">[Supprimer]</span>'),
initialize: function () {
this.model.bind('destroy', this.remove, this); //On modifie les méthodes de destruction et de changement
},
render: function () { //Méthode qui met en forme le message dans le template et le retourne pour affichage
$(this.el).html(this.template({
Tache: this.model.toJSON()
}));
return $(this.el);
},
events: {//Gestion des évènements particuliers
'click #Effacer': 'laSuppression',
'click #Statut': 'changementStatut'
},
laSuppression: function () { //Fonction pour gérer la suppression
var self = this;
this.model.destroy({//On appelle la méthode de destruction, avec, en cas de succès, mise à jour de l'affichage
success: function () {
self.remove();
}
});
},
changementStatut: function () { //Gestion du changement de statut
var self = this;
if (this.model.get('statut') === "En cours") {
this.model.set("statut", "terminée");
this.model.save(null, {
success: function () {
console.log('success');
self.$('#Statut').html("[Terminée]");
},
error: function (model, response) {
console.log('error');
},
wait: true
});
} else {
console.log("Tache déja finie");
}
}
});
Enfin, il faut modifier notre conteneur HTML : le tableau des tâches. Il doit instancier une vue Refresh, pour l'afficher un peu plus loin, mais également remplir la collection depuis le serveur.
var TableauDesTaches = Backbone.View.extend({
// Le constructeur (à la création de la vue) :
initialize: function () {
this.views = {};
//Cette vue comprend à présent les trois objets nécessaires : list, form et refresh
this.views.list = new List({model: this.model});
this.views.form = new Form({model: this.model});
this.views.refresh = new Refresh({model: this.model}); //Vue refresh
this.model.fetch(); //Remplissage de la collection
},
// Et la méthode render
render: function () {
$(this.el).append(this.views.refresh.render());
$(this.el).append(this.views.list.render()); //Demande à la liste de se générer pour affichage
$(this.el).append(this.views.form.render()); //Idem pour le formulaire et le refresh
return $(this.el); //Obligatoire sinon vue non mise à jour!
}
});
Voilà, à présent vous pouvez améliorer visuellement et techniquement votre todo-list maison!