Site RESTful avec Slim3 et ng-admin

SLIM 3 est un microframework php qui permet de créer des API. Associé à un module AngularJS nommé ng-admin[0], il permet de construire un tableau de bord d’administration d’une base de données dans une application de type SPA (Single Page Application). Nous verrons ici comment mettre en œuvre l’ensemble.

Les vidéos

  1. Création du projet dans NetBeans, installation du micro Framework Slim 3 avec composer
  2. Création de l’API. mise en place du routage, interrogation de ma base mySQL
  3. Installation du moteur de vue ng-admin et affichage d’une liste de données.
  4. Ajout, modification et suppression de données en faisant interagir ng-admin et l’API Slim 3

Prérequis

L’ordinateur hôte de la solution doit avoir :

Tous ces logiciels sont multiplateformes et fonctionnent quel que soit l’OS.

Création de la solution (Netbeans)

Dans Netbeans (exemple pour créer un projet portant le nom : « monProjet »)

Fichier->Nouveau Projet cela lance un assistant en 5 points

  1. Sélectionner un projet : Choisir Catégories -> PHP et Projets -> PHP Application, cliquer sur Suivant
  2. Name and Location : Project Name : monProjet, Sources Folder : pointer sur le dossier root du serveur et un sous dossier portant le nom du projet : par exemple c:\laragon\www\monProjet, PHP version (celle qui est installée sur l’ordinateur), Default Encoding : UTF-8, cliquer sur Suivant.
  3. Run Configuration : Run As : Local Web Site, project URL : le chemin du virtual host créé sur le serveur (si ce n’est fait automatiquement, il faut le faire) : http://monProjet.dev:8080
  4. PHP Frameworks : laisser vide, cliquer sur Suivant
  5. Composer : (installation des bibliothèques php nécessaires au projet, ici nous allons ajouter le paquet Slim 3 et le paquet Slim PDO).
    Dans token -> slim puis cliquer sur Search, sélectionner slim/slim, dans la liste déroulante Version choisir 3.x-dev , puis cliquer sur le bouton afin de faire apparaitre slim/slim (3.x-dev) dans la fenêtre de droite.
    refaire la même chose avec le paquet slim/PDO version dev-master

Cliquer sur Terminer, les différents dossiers du projet vont être créés, et composer va télécharger et installer les bibliothèques

A l’issue de l’opération, le dossier du projet ressemble à ceci :

Le dossier nbprojet contient les fichiers de paramétrage du projet necessaires à NetBeans (on n’y touche jamais)

Le dossier vendor, qui contient les bibliothèques téléchargées et un fichier autoload.php, qui comme son nom l’indique, permet de charger automatiquement les classes et leur espace de nom afin de les rendre disponibles facilement.

Composer.json est le fichier qui contient les références des packages installés,

{
    "name": "vendor/mon-projet",
    "description": "Description of project monProjet.",
    "authors": [
        {
            "name": "francois",
            "email": "your@email.here"
        }
    ],
    "require": {
        "slim/slim": "3.x-dev",
        "slim/pdo": "dev-master"
    }
}

Si on ajoute ou supprime une dépendance dans la partie « require » du fichier, puis qu’on effectue une commande composer update, le projet se met à jour automatiquement et chargeant ou déchargeant les bibliothèques ajoutées ou retirées. Un autoload.php est alors recréer. Composer.lock contient les informations de l’autoload actuel. (on ne touche jamais ce fichier).

Enfin un fichier index.php et un squelette de page web

L’API

Dans notre application, tous les accès au modèle de données se font au travers de requêtes web vers une API en respectant le format REST[1].

Le format REST considère l’url, non plus comme l’appel d’une page web mais comme l’appel à une fonction, les arguments étant passés soit dans l’url (pour les requêtes de type GET, DELETE) dans le corps (pour les requêtes POST) ou bien les deux (requêtes PUT).

Dans le format REST, les verbes des requêtes permettent de définir le type d’action que le serveur devra effectuer.

Exemple :

Verbe

url

Action

GET

/products   

Récupère un jeu de
données contenant tous les éléments « products »

GET

/products/:id

Récupère un jeu de
données contenant 1 élément « products » ayant l’identifiant « :id »

POST

/products   

Créer un « product »
dans la table « products » , les paramètres passent dans le corps
de la requête

PUT

/products/:id

Mise à jour du product
 d’identifiant « :id », les
paramètres passent dans le corps de la requête

DELETE

/products/:id

Destruction du product
d’identifiant « :id »

Grâce à ces 4 verbes, on peut créer des services web qu’on nomme CRUD (Create Read Modify Delete).

Le serveur renvoie au client un jeu de données soit au format XML soit au format JSON.

Exemple :

GET /products

Renvoie un tableau JSON de type :

[
    {
      "id": 1,
      "name": "LG P880 4X HD",
      "description": "My first awesome phone!",
      "price": null,
      "category_id": 5,
      "created": "2014-06-01 01:12:26",
      "modified": "2014-05-31 17:12:26"
    },
    {
      "id": 2,
      "name": "Google Nexus 4",
      "description": "The most awesome phone of 2013!",
      "price": "56",
      "category_id": 2,
      "created": "2014-06-01 01:12:26",
      "modified": "2014-05-31 17:12:26"
    },
…
]

GET /products/2

Renvoie un objet JSON ressemblant à ceci :

{
      "id": 2,
      "name": "Google Nexus 4",
      "description": "The most awesome phone of 2013!",
      "price": "56",
      "category_id": 2,
      "created": "2014-06-01 01:12:26",
      "modified": "2014-05-31 17:12:26"
}

 

L’API sera construite à l’aide du microframework Slim Framework 3[2]. Ce dernier a été créé pour développer simplement des API, il possède un routeur pour gérer les diverses requêtes et un injecteur de dépendance qui permet d’utiliser les bibliothèques tierces.

Créer un dossier « api » sur la racine du projet. Ajouter un fichier index.php et un fichier .htaccess

Comme indiqué sur la page https://www.slimframework.com/docs/start/web-servers.html , compléter la fichier .htaccess de la manière suivante :

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

Vérifier aussi que dans la configuration de Apache l’option AllowOverride est sur All.

Sur un raspberry il s’agit du fichier /etc/apache2/apache2.conf, section :

<Directory /var/www/>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

Comme indiqué page https://www.slimframework.com/docs/tutorial/first-app.html , le squelette de la page index est le suivant :

<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response) {
    $name = $request->getAttribute('name');
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();

Nous allons nous inspirer de ce squelette afin que l’api puisse répondre  des données statiques dans un premier temps, puis des données issues de la BDD

exemple avec des données issues de la base de données

On suppose que la base de données est créée et qu’elle contient une table « ouvrants » (champs id et nom) avec quelques valeurs.

Dans Slim l’accès à la base de donnée est simplifié grâce à la bibliothèque slim/PDO . D’après la documentation[3]

L’usage typique se fait de la manière suivante :

require_once 'vendor/autoload.php';

$dsn = 'mysql:host=your_db_host;dbname=your_db_name;charset=utf8';
$usr = 'your_db_username';
$pwd = 'your_db_password';

$pdo = new \Slim\PDO\Database($dsn, $usr, $pwd);

// SELECT * FROM users WHERE id = ?
$selectStatement = $pdo->select()
                       ->from('users')
                       ->where('id', '=', 1234);

$stmt = $selectStatement->execute();
$data = $stmt->fetch();

// INSERT INTO users ( id , usr , pwd ) VALUES ( ? , ? , ? )
$insertStatement = $pdo->insert(array('id', 'usr', 'pwd'))
                       ->into('users')
                       ->values(array(1234, 'your_username', 'your_password'));

$insertId = $insertStatement->execute(false);

// UPDATE users SET pwd = ? WHERE id = ?
$updateStatement = $pdo->update(array('pwd' => 'your_new_password'))
                       ->table('users')
                       ->where('id', '=', 1234);

$affectedRows = $updateStatement->execute();

// DELETE FROM users WHERE id = ?
$deleteStatement = $pdo->delete()
                       ->from('users')
                       ->where('id', '=', 1234);

$affectedRows = $deleteStatement->execute();

 

On a ici les prototypes de l’instanciation d’un objet $pdo et des diverses requêtes select, delete, insert et update. L’intégration dans Slim se fait via un « container » permettant d’instancier un objet de la bibliothèque slim/PDO qui se situe dans un autre espace de nom que l’objet $app de Slim…

Le container va permettre de préparer, gérer et injecter des dépendances à l’application. L’avantage est que l’objet lié au container est créé à la demande et non de manière permanente. D’autre part les objets de l’application pourront accéder à des ressources dans d’autres espaces de nom.

Pour créer un container « db », insérer le code suivant :

<?php

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$config = [
    'settings' => [
        'displayErrorDetails' => true,

       
    ],
];
$app = new \Slim\App($config);
$container = $app->getContainer();
$container['db'] = function () {
    $dsn = 'mysql:host=localhost;dbname=apidb;charset=utf8';
    $usr = 'root';
    $pwd = '';
    $pdo = new \Slim\PDO\Database($dsn, $usr, $pwd);
    return $pdo;
};

Cela permet de créer un objet « db » qui sera instancié à la demande.

création d’une requête SELECT

Remplacer le code de la fonction $app->get(…) par celui-ci

/*********************************************************
 *    gestion des requêtes GET
 ******************************************************** */
$app->get('/{table}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    // SELECT * FROM {table}
    $selectStatement = $this->db->select()
            ->from($table);
    $stmt = $selectStatement->execute();
    $data = $stmt->fetchAll();
    return $response->withJson($data);
});

$this->db fait référence au container  « db » créé précédemment.

Sur le même principe si on veut que l’api renvoie juste un produit dont on spécifie l’id, le code sera le suivant :

$app->get('/{table}/{id}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    $id = $request->getAttribute('id');
    // SELECT * FROM {table} WHERE id = {id}
    $selectStatement = $this->db->select()->from($table)->where("id","=",$id);
    $stmt = $selectStatement->execute();
    $data = $stmt->fetch();
    return $response->withJson($data);
});

Code complet de la page /api/index.php

<?php

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$config = [
    'settings' => [
        'displayErrorDetails' => true,
       
    ],
];
$app = new \Slim\App($config);
$container = $app->getContainer();
$container['db'] = function () {
    $dsn = 'mysql:host=localhost;dbname=apidb;charset=utf8';
    $usr = 'root';
    $pwd = '';
    $pdo = new \Slim\PDO\Database($dsn, $usr, $pwd);
    return $pdo;
};

$app->get('/{table}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    // SELECT * FROM users WHERE id = ?
    $selectStatement = $this->db->select()
            ->from($table);
    $stmt = $selectStatement->execute();
    $data = $stmt->fetchAll();
    return $response->withJson($data);
});

$app->get('/{table}/{id}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    $id = $request->getAttribute('id');
    // SELECT * FROM {table} WHERE id = {id}
    $selectStatement = $this->db->select()->from($table)->where("id","=",$id);
    $stmt = $selectStatement->execute();
    $data = $stmt->fetch();
    return $response->withJson($data);
});

$app->run();

Installation de NG-ADMIN

ouvrir un terminal dans le dossier qui contient la solution puis effectuer la commande

npm install -g npm@2.x (sous linux faire précéder de sudo)
npm install ng-admin –-save

Cela installera le dossier node_modules et le sous dossier ng-admin qui contient tout le matériel pour le front-end.

Le dossier du projet va ressembler à ceci :

Modifier la page web index.php à la racine du site. Le code de la page est le suivant :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Controle d'accès</title>
       <link rel="stylesheet" href="node_modules/ng-admin/build/ng-admin.min.css">
    </head>
    <body ng-app="myApp">
        <div ui-view="ng-admin"></div>
        <script src="node_modules/ng-admin/build/ng-admin.min.js" type="text/javascript"></script>
        <script src="js/app.js"></script>
    </body>
</html>

Le corps de la page est constitué d’une simple balise <div ui-view=”ng-admin”></div> et le <body> fait référence à l’application angularJS sous-jacente grâce à l’attribut ng-app=”myApp”[4]

Rajouter un dossier « js » et un fichier nommé app.js (dont il est fait référence dans la page index.php). Le script va créer un module angularJS nommé « myApp » et qui intégrera la bibliothèque [‘ng-admin’], la suite permet de configurer l’application. La documentation détaillée au format pdf de ng-admin est accessible ici : https://www.gitbook.com/book/marmelab/ng-admin/details

Code de /js/app.js

// declare a new module called 'myApp', and make it require the `ng-admin` module as a dependency
var myApp = angular.module('myApp', ['ng-admin']);
// declare a function to run when the module bootstraps (during the 'config' phase)
myApp.config(['NgAdminConfigurationProvider', function (nga) {
        // create an admin application
        var admin = nga.application('Mes produits')
                .baseApiUrl('/api/'); // main API endpoint;

        /************************************
         *  entité produts
         ************************************/
        var produits = nga.entity('products');
        var categories= nga.entity('categories');
        categories.listView();
        // tableau = listview
        produits.listView()
                .title('les produits')
                .fields([
                    nga.field('id'),
                    nga.field('name').label('nom'),
                    nga.field('description'),
                    nga.field('price').label('prix')
                  ]) ;
        admin.addEntity(produits);
        admin.addEntity(categories);
        nga.configure(admin);
    }]);

L’application fonctionne,

Reste à intégrer dasn l’API les fonctions d’ajout, de mise à jour et d’effacement des produits.

code complet

/api /index.php

<?php

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$config = [
    'settings' => [
        'displayErrorDetails' => true,

       
    ],
];
$app = new \Slim\App($config);
$container = $app->getContainer();
$container['db'] = function () {
    $dsn = 'mysql:host=localhost;dbname=apidb;charset=utf8';
    $usr = 'root';
    $pwd = '';
    $pdo = new \Slim\PDO\Database($dsn, $usr, $pwd);
    return $pdo;
};

$app->get('/{table}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    // SELECT * FROM users WHERE id = ?
    $selectStatement = $this->db->select()
            ->from($table);
    $stmt = $selectStatement->execute();
    $data = $stmt->fetchAll();
    return $response->withJson($data);
});

$app->get('/{table}/{id}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    $id = $request->getAttribute('id');
    // SELECT * FROM users WHERE id = ?
    $selectStatement = $this->db->select()->from($table)->where("id","=",$id);
    $stmt = $selectStatement->execute();
    $data = $stmt->fetch();
    return $response->withJson($data);
});

$app->post('/{table}', function (Request $request, Response $response) {
    $data=$request->getParsedBody();
    $table = $request->getAttribute('table');
    $keys= array_keys($data);
    $values=array_values($data);
    $insertStatement= $this->db->insert($keys)
            ->into($table)
            ->values($values);
    $stmt = $insertStatement->execute();
    $response->withJson($stmt);  
    
});

$app->put('/{table}/{id}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    $id = $request->getAttribute('id');
    $data=$request->getParsedBody();
    $updateStatement = $this->db->update($data)
            ->table($table)
            ->where('id',"=",$id);
        $stmt = $updateStatement->execute();
    $response->withJson($stmt);

});
$app->delete('/{table}/{id}', function (Request $request, Response $response) {
    $table = $request->getAttribute('table');
    $id = $request->getAttribute('id');
    $deleteStatement = $this->db->delete()->from($table)->where('id', '=', $id);
    $stmt = $deleteStatement->execute();
    $response->withJson($stmt);
});

$app->run();

 

/js/app.js

// declare a new module called 'myApp', and make it require the `ng-admin` module as a dependency
var myApp = angular.module('myApp', ['ng-admin']);
// declare a function to run when the module bootstraps (during the 'config' phase)
myApp.config(['NgAdminConfigurationProvider', function (nga) {
        // create an admin application
        var admin = nga.application('Mes produits')
                .baseApiUrl('/api/'); // main API endpoint;

        /************************************
         *  entité produts
         ************************************/
        var produits = nga.entity('products');
        var categories= nga.entity('categories');
        categories.listView();
        // tableau = listview
        produits.listView()
                .title('les produits')
                .fields([
                    nga.field('id'),
                    nga.field('name').label('nom'),
                    nga.field('description'),
                    nga.field('price').label('prix'),
                    nga.field('category_id','reference')
                            .targetEntity(categories)
                            .targetField(nga.field('name'))
                            .label('catégorie')
                ])
                .listActions(['edit']);
        produits.creationView()
                .title('Ajout d\'un produit')
                .fields(produits.listView().fields());
        
        produits.editionView()
                .fields(produits.creationView().fields());
        admin.addEntity(produits);
        admin.addEntity(categories);
        nga.configure(admin);
    }]);

 

  1. https://marmelab.com/fr/blog
  2. https://openclassrooms.com/courses/utilisez-des-api-rest-dans-vos-projets-web/pourquoi-rest
  3. https://www.grafikart.fr/tutoriels/php/slim-framework-831
  4. https://github.com/FaaPz/Slim-PDO
  5. Voir formation angularJS : https://www.grafikart.fr/formations/angularjs
    ou le livre « AngularJS » editions ENI ISBN 978-2-7460-9334-8

PHP mySQL

L’objectif est ici d’afficher dans une page HTML des données issues d’une base de données mySQL. La page web va emettre des requêtes AJAX vers une page PHP. Cette dernière se chargera d’interroger la base et de fournir en réponse un fichier JSON que la page HTML pourra traiter et afficher les résultats

La première vidéo présente l’installation de la base de donnée  (elle est téléchargeable ici ). Ensuite on voit comment utiliser phpmyadmin afin de faire une requête avec des tables liées. Enfin, elle aborde la construction du script PHP, permettant d’interroger la base et de fournir un fichier JSON en réponse à une requête de type GET.

La seconde vidéo présente comment utiliser jQuery pour émettre une requête AJAX afin de récupérer un jeu de données. Puis comment insérer ces données dans un tableau.

La troisième vidéo montre comment peupler une liste déroulante à partir de la base de données, puis d’afficher les détails  de l’élément sélectionné. (Master/Detail)

phpMyAdmin

PhpMyAdmin est un outils graphique via des page web permettant de manipuler un serveur de base de données mySQL. Il est intégré aux packages genre Wamp, Lamp, Laragon.
nous verrons ici comment débuter avec cet outils.

La première vidéo (Part 01) explique comment :

  • Créer une nouvelle base de donnée.
  • Créer une table et ses colonnes
  • Effectuer les 4 opérations de base d’un CRUD (Create, Read, Update, Delete) c’est à dire créer des valeurs, les lire, les mettre à jour ou les effacer.

la seconde vidéo (Part 02) explique comment :

  • Créer une seconde table et créer une liaison entre les 2 tables
  • Utiliser l’utilitaire de requête pour extraire une jeu d’informations  prélevé dans les 2 tables.

Attention pour que les liaisons fonctionnent avec mySQL il faut bien s’assurer lors de la création de la table que le moteur stockage est de type InnoDB

jQuery et AJAX

Gérer une liste à l’aide de jQuery

La page que nous allons utiliser ressemble à celle-ci. Il s’agit d’une liste de véhicules ayant participé au challenge EducEco en 2012. Chaque véhicule est accompagné de sa vignette. La page est construite avec le framework Bootstrap. La barre de menu supérieure permet changer la couleur de fond une ligne sur deux, de mettre le nom de la voiture en vert. Le premier lien quant à lui permet de restituer l’état initial.

Dans ce document la liste est statique, c’est à dire qu’elle est inscrite “en dur” dans le code html, ce qui n’est pas très pratique si on désire faire une mise à jour (par exemple rajouter ou enlever un véhicule de la liste). La page statique est test-liste-educEco (pour la télécharger faire un clic droit et enregistrer la cible du lien sous…)

AJAX[1]

L’idée va donc être de séparer les données de la page html. La page html assure la mise en forme, les données, elles seront contenues dans un fichier séparé au format json (fichier de données javascript). C’est un script jQuery qui va formuler une requête asynchrone (AJAX) afin de récupérer le jeu de données et intégrer ces dernières dans la page html.

Utilisation de JQUERY pour manipuler des jeux de données

Jquery peut effecteur des requêtes de type AJAX afin de récupérer des jeux de données puis les injecter dans la page web. Le diagramme de séquence est le suivant :

Le client initialise la requête web, le code HTML est alors renvoyé. Le code HTML contient des liens vers les feuilles de styles CSS et les scripts JS, ce qui provoque d’autres requêtes. Une fois le(s) script(s) rapatrié(s), il(s) s’exécute(nt). Un script peut effectuer des requêtes asynchrones auprès du serveur (c’est-à-dire en tâche de fond). Cela permet de mettre à jour la page sans la recharger complètement ce qui apporte un confort visuel pour l’utilisateur et une moindre occupation de la bande passante

Format d’une requête AJAX dans JQUERY :


$(document).ready(function () {
$.ajax({
type: "GET", // type de requête : GET ou POST
url: "http://.../data.json", // l'URL pour accéder aux données
dataType: "json", // le format des données JSON ou XML
success: parseData, // fonction appelée en cas de succès les données seront passée en argument de cette fonction
error: function (jqXHR, textStatus, errorThrown) { // fonction traitée en cas d'erreur
alert(textStatus);
alert(errorThrown);
}
});

function parseData(data) {…} // l’argument data correspond au jeu de données en retour de la requête AJAX
});

La fonction parseData récupère en argument les données au format JSON issu de la requête asynchrone. Et peut donc modifier le document HTML à l’aide de ce jeu de données.

Imaginons que le jeu de données JSON soit de la forme :

[
{
"Numero": "4",
"Nomvehicule": "MIRELEC09",
"Ville": "Mirepoix",
"Groupe": "EcoCitadin",
"Motorisation": "Electrique"
},
{
"Numero": "5",
"Nomvehicule": "Vincicar",
"Ville": "Blois",
"Groupe": "EcoCitadin",
"Motorisation": "Electrique"
},…
]

On voit qu’il s’agit d’un tableau (balises « [] ») contenant des objets JSON (balises « {} »). Chaque objet possède les propriétés “Numero”, “Nomvehicule”, “Ville”, “Groupe” et “Motorisation”. Si on désire afficher tous les éléments du tableau, il faut prendre les objets un par un et afficher chacune des propriétés. Pour cela, on peut s’aider de la boucle « for/in » propre au langage JS : Cette boucle peut itérer tous les index d’un tableau ou d’un objet.

function parseData(data) {
for (var i in data) {
$("#content").append(
"<li class='col-md-6' data-filter=" + data[i].Groupe + ">"
+ "<div class='vignette'>"
+ "<a href='#'>"
+ "<img width='150' height='80' alt='Photo vehicule' src='http://www.rgot.org/wp-content/uploads/img/" + data[i].Numero + ".jpg'>"
+ "</a>"
+ "<h4>"
+ "<a href='#'>" + data[i].Nomvehicule + "</a>"
+ "</h4>"
+ "<h4>" + data[i].Motorisation + "</h4>"
+ "<p>" + data[i].Ville + "</p>"
+ "</div>"
+ "</li>"
);
};
};

Dans le code précédent « data[i] » représente l’objet de position « i » dans le tableau.
La page web et le fichier json sont téléchargeables ici :
jquery manipulation de données

  1. Acronyme d’Asynchronous JAvascript and Xml, AJAX  permet en autreMise à jour d’une page web sans avoir à la recharger.Demander (requête) des données au serveur, après chargement de la page.Recevoir des données du serveur, après chargement de la page.Envoyer des données au serveur en tâche de fond.

jQuery

Scripts coté client : JS / JQUERY

JavaScript est un langage de scripts qui peuvent s’exécuter depuis le navigateur. Les scripts permettent d’agrémenter l’expérience de l’utilisateur (effet visuels, vérification d’une saisie…) , ils peuvent interagir avec le DOM afin de modifier la mise en page (ajout/masquage de textes, modification dynamique de la mise en page, interaction avvec les feuilles CSS…). Les scripts peuvent aussi effectuer des requêtes asynchrones (c’est-à-dire des requêtes qui n’ont pas lieux en même temps que le chargement de la page) afin de compléter un contenu de page ( effet de lazy loading (chargement différé) pour les images…).

Javascript a un formaliste proche du langage C/C++. Mais c’est un langage faiblement typé (pas besoin de déclaré le type d’une variable lors de sa création) ce qui semble alléchant à priori, mais se révèle très lourd à l’usage. Par exemple maVariable=0000 : le moteur sera incapable de savoir si 0 est un nombre ou une chaine de caractères…

Depuis quelques années on voit apparaitre des frameworks (structure ou environnement logiciel) JavaScript. Ces frameworks regroupent des bibliothèques JavaScript créées pour faciliter l’écriture de scripts côté client dans le code HTML des pages web. Une des plus connue est jQuery.

La bibliothèque jQuery contient notamment les fonctionnalités suivantes :

  • Parcours et modification du DOM (y compris le support des sélecteurs CSS 1 à 3 et un support basique de XPath) ;
  • Événements ;
  • Effets visuels et animations ;
  • Manipulations des feuilles de style en cascade (ajout/suppression des classes, d’attributs…) ;
  • Ajax (requêtes asynchrones formulées par le navigateur pour, par exemple, récupérer un jeu de données ;
  • Plugins ;
  • Utilitaires (version du navigateur web…).

1. bases de jquery

2. Notes de cours

Fondamentalement le Framework jQuery est un script JavaScript. Pour l’utiliser il suffit juste d’intégrer ce script dans la page html. Pour cela il faut télécharger jQuery (https://jquery.com/download/ ), placer le script dans le dossier du site (l’idéal est de créer un sous dossier nommé JS et qui contiendra tous les fichiers de script).

Dans la page html, il suffit alors de charger ce script et, pour pouvoir utiliser jQuery, on rajoute le lien vers le fichier qui contiendra les scripts actifs sur la page (demo.js dans l’exemple ci-dessous).

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <!-- déclaration du moteur jQuery. Dans cet exemple il se nomme jquery.js et est localisé dans le dossier JS-->
    <script src="Scripts/jquery-1.9.1.min.js"></script>
    <!-- script de la page-->
    <script>
        $(function () {
            //ici on place les fonctions qui permettent de personnaliser la page
            console.log("ready!"); // indication dans la console que jQuery est correctement initialisé
            $("#cachemoi").hide(3000); // au chargement de la page la division d'id "cahemoi" disparait en 3000ms
            //…
        });
    </script>
</head>
<body>
    <div id="conteneur">
        <h1>Le titre de la page</h1>
        <p>bonjour, comment ça va ? Je suis sûr que si tu <span>cliques ici</span> ça fait quelque chose</p>
        <p>2ème paragraphe</p>
        <div id="makemegreen">Fait-moi devenir tout vert</div>
        <div id="cachemoi">Cache moi !</div>
    </div>
</body>
</html> 

 

 

Pour initialiser les scripts de la page le contenu du fichier de script de la page (demo.js) doit avoir la forme suivante :

$(function () {
  //ici on place les fonctions qui permettent de personnaliser la page
  console.log("ready!"); // indication dans la console que jQuery est correctement initialisé
  $("#cachemoi").hide(3000); // au chargement de la page la division d'id "cahemoi" disparait en 3000ms
//…
});

 

Pour plus d’information : http://learn.jquery.com/

    1. Sélection des éléments du DOM

Voir : http://learn.jquery.com/using-jquery-core/selecting-elements/

Les balises html contiennent souvent des attributs. Par exemple un lien hypertexte contient des attributs tels que « href » (lien hypertexte), « title » (info bulle)… jQuery permet d’interroger ces attributs pour lire leur valeur ou pour la changer

Exemple avec la balise img (image)

<img id="greatphoto" src="brush-seller.jpg" alt="brush seller">

L’attribut « alt » (comme alternative) est le texte qui apparait à la place de l’image si cette dernière, pour une quelconque raison, ne peut être affichée.

Avec le jQuery suivant :

$("#greatphoto").attr("alt", "Brush Manquant");

L’image d’id « greatphoto », verra son attribut alt remplacé par « Brush Manquant »

Avec jQuery on peut modifier le contenu de la page

Autre exemple pour remplacer du code html

$("#conteneur").html("<p>Texte de remplacement</p>");

Dans cet exemple le contenu de la div d’id « conteneur » sera remplacé par un paragraphe de texte « Texte de remplacement ».

Si on désire rajouter du texte à la fin d’un paragraphe :

$("#conteneur p:first").append(" texte rajouté à la fin");

jQuery cherche le premier paragraphe contenu dans la div « conteneur » et rajoute à la fin le texte «  texte rajouté à la fin »

De la même manière,

$("#conteneur p:first").prepend("Texte rajouté au début<br/>");

Rajoute du texte au début du même paragraphe

Les évènements permettent de capturer les interactions entre l’utilisateur et le navigateur (déplacement ou clic de souris, saisie clavier…) et d’exploiter ces informations pour modifier le contenu ou l’apparence de la page.

Exemple détection d’un clic de souris :

$(document).keyup(function (e) {
      console.log(e.keyCode); // option de debug : permet d'afficher la valeur de la touche pressée.
         if (e.keyCode == 82) {  // 82 valeur de la touche 'r'
          $("#makemegreen").css("color", "red");// modification de la couleur
          var nouveauTexte = $("#makemegreen").text().replace("vert", "rouge"); // remplacement de  "vert" par "rouge"
          $("#makemegreen").text(nouveauTexte);// affichage du texte remplacé
     };
});

 

Framework Bootstrap

Initiation à Bootstrap

(merci au site apprendre-a-coder.com)

Intégration de Bootstrap dans Visual studio

Exercice

En s’aidant de ce qui a été fait précédemment reproduire au plus près la page web suivante à l’aide du framework Bootstrap.

Pour information :

initiation WEB avec Visual Studio

Créer un site web avec Visual Studio

  • Création d’un site web et utilisation du serveur local iisExpress
  • Incorporation d’images
  • Utilisation de l’interface graphique WYSIWYG (what you see is what you get)

Mise en page à l’aide des <div>

  • présentation des div
  • mise en forme automatique Visual Studio
  • affectation d’id aux div, modification d’un style pour une div.
  • utilisation de classe pour assurer un centrage de texte

Positionnement des éléments d’une page.

  • Réalisation d’une petite galerie photo
  • Utilisation des styles float et  margin pour gérer le flottement des éléments.

Structuration d’une page grâce aux balises sectionnantes HTML5

  • Réalisation d’une page dont la structuration est la suivante :

Pour l’utilisation de Framework CSS voir la page Framework Bootstrap

Tutoriel

Créer une page web qui ressemble à ceci :

Le plan de structuration de la page est le suivant :

Aide pour le bouton bleu « See more… »
Ce bouton est créé à l’aide d’une div qui contient un texte inclus dans une balise h2. Cette div se situe dans une section ayant pour id : finArticle

Le code de la section est alors :

<section id="finArticle">
     <p>Etiam rhoncus nunc quis nulla dapibus pellentesque vitae vitae risus. </p>
     <div>
          <h2> See more...</h2>
     </div>
</section>

On peut constater que dans la section tous les textes sont centrés , nous pouvons donc déclarer le style suivant :

#finArticle{
             text-align:center;
           }

Ce style ne s’applique qu’à la balise ayant pour id : finArticle.
Pour appliquer des modifications à une balise div incluse dans finArticle, il faut faire comme ceci :

#finArticle div {
              width: 200px; /*largeur*/
              height: 75px; /*hauteur*/
              margin: auto; /*centrage de la div dans la page*/
              background-color: #0094ff;/*couleur de fond (bleu)*/
              border: 1px solid #ccc;/*bordure grise du "bouton"*/
              border-radius:8px;/*angles arrondis*/
              text-shadow: -1px -1px rgba(0, 0, 0, 0.31);/*effet d'ombrage du texte "See more" */
              color: #FFFFFF;/*couleur du premier plan : le texte écrit "See more..."*/
}

Technologie Web

Html, css, javascript…

Introduction

Le web est un système client/ serveur qui permet au client d’afficher des pages web sur un terminal. L’envoi de requêtes et leurs décryptages est assuré par un logiciel nommé navigateur. Les terminaux qui peuvent se connecter à internet sont de type et de technologie différente (ordinateur, tablette, téléphone portable, objet connecté…).

Le format web doit pouvoir s’adapter à tous ces types de connexions. Le web s’appuie sur 3 langages :

  • HTML pour véhiculer le contenu (texte, images, son, vidéo…),
  • CSS pour la mise en forme et l’adaptation aux différents terminaux
  • JS pour rendre la page interactive avec l’utilisateur ou le serveur.

Requête http

Le diagramme de séquence d’une requête web est la suivante :

  • Le client appelle la page web grâce à son URI. On parle de requête de type GET.
  • Le serveur renvoie la page demandée au format html.
  • La page contenant des liens hypertextes dans son entête reformule elle-même des requêtes GET afin de rapatrier les autres éléments de la page web : JS et CSS.
  • Une fois l’ensemble des éléments reçus, la page s’affiche avec sa mise en forme et sa capacité à interagir avec l’utilisateur.
  • La page html peut aussi appeler des images, des vidéos, du son…

En fait tous les éléments constituant la page sont rapatriés et c’est le navigateur qui s’occupe de tout mettre en place en fonction du flux html et du fichier CSS

HTML[1]

L’HyperText Markup Language, généralement abrégé HTML, est le format de données conçu pour représenter les pages web. C’est un langage de balisage permettant d’écrire de l’hypertexte, d’où son nom HTML.

Les balises sont invisibles à l’écran du navigateur, mais elles permettent à ce dernier de comprendre ce qu’il doit afficher. Les balises se repèrent facilement. Elles sont entourées de « chevrons », c’est-à-dire des symboles < et >, comme ceci :

<balise>

Elles indiquent la nature du texte qu’elles encadrent. Elles veulent dire par exemple : «Ceci est le titre de la page», «Ceci est une image», «Ceci est un paragraphe de texte», etc.

<titre>Ceci est un titre</titre>

On distingue une balise ouvrante <titre> et une balise fermante </titre> qui indique que le titre se termine.

Il existe des balises dites orphelines c’est-à-dire qu’on n’a pas de texte à insérer entre l’ouverture de la balise et sa fermeture. Par exemple <br/> (back return = saut de ligne) ou <image/> qui permet l’insertion d’image…

Les documents HTML ont une véritable structure en arbre, avec un élément racine contenant tous les autres éléments. Par exemple <html> contient <head> et <body>. Cet « arbre du document » est utilisé pour repérer les éléments de la page et permettre leur formatage ou l’interaction avec l’utilisateur par le biais de scripts.

liste des balises HTML voir le memento HTML

Tutoriel

On veut réaliser la page suivante :
(pour savoir comment créer un site et des pages avec Visual Studio cliquer ici):

Le code html est le suivant:


<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Démonstration des éléments de section HTML5</title>
    <style>
        span.elem {
            display: none;
        }
    </style>
</head>

<body>
    <!-- Header -->
    <header>
        <h1>Nouveaux éléments de section, article, header, footer, aside, nav</h1>
        <!-- nav principale -->
        <nav>
            <ul>
                <li><a href="#">Rubrique 1</a></li>
                <li><a href="#">Rubrique 2</a></li>
                <li><a href="#">Rubrique 3</a></li>
                <li><a href="#">Rubrique 4</a></li>
            </ul>
        </nav>
    </header>
    <!-- Main -->
    <section id="main">
        <span class="elem">section</span>
        <article>
            <span class="elem">article</span>
            <header>
                <span class="elem">header</span>
                <h2>En tête de l'article</h2>
                <p>Date de publication, auteur</p>
            </header>
            <p>Contenu de l'article, contenu de l'article, contenu de l'article, contenu de l'article, contenu de l'article,
                contenu de l'article, contenu de l'article...</p>
        </article>
        <article>
            <span class="elem">article</span>
            <header>
                <span class="elem">header</span>
                <h2>Harry Gruyaert</h2>
                <p>Par François, le
                    <time datetime="2016-11-05">5 novembre 2016</time>
                </p>
            </header>
            <p>Harry Gruyaert (né le 25 août 1941 à Anvers) est un photographe belge. Il est membre de l'agence Magnum Photos
                depuis 1981</p>
            <aside>
                <span class="elem">aside</span>
                <p><b>Magnum Photos</b> est une coopérative photographique. Créée en 1947 par Robert Capa, Henri Cartier-Bresson,
                    George Rodger, William Vandivert et David Seymour, elle fut la première de ce genre à voir le jour1.</p>
            </aside>
            <footer>
                <span class="elem">footer</span>
                <p>Source : <a href="http://fr.wikipedia.org/wiki/Magnum_Photos">Wikipedia</a></p>
            </footer>
        </article>
    </section>
    <!-- sidebar -->
    <aside id="sidebar">
        <span class="elem">aside</span>
        <h2>Sidebar</h2>
        <h3>Navigation</h3>
        <nav>
            <span class="elem">nav</span>
            <ul>
                <li><a href="#">Lien 1</a></li>
                <li><a href="#">Lien 2</a></li>
                <li><a href="#">Lien 3</a></li>
            </ul>
        </nav>
        <h3>Autre titre</h3>
        <p>Autres informations...</p>
    </aside>
    <!-- footer -->
    <footer>
        <span class="elem">footer</span>
        <p class="mentions">Page de démonstration de l'article <a href="/article/lire/1376-html5-section-article-nav-header-footer-aside.html">Nouveaux éléments de section, article, header, footer, aside, nav</a></p>
    </footer>
</body>

</html>

L’appel de cette page dans un navigateur montre le contenu mais la mise en page est linéaire est suit le flux, c’est-à-dire l’ordre d’apparition des éléments dans l’ordre d’écriture de la page

En reprenant la page et en faisant apparaitre les éléments de section HTML5, on peut voir que les sections s’imbriquent ou s’empilent les unes sur les autres.

Ajout de style afin de modifier l’aspect de la page

C’est grâce aux feuilles de style  CSS (Cascade Style Sheet) que le positionnement des sections va avoir lieu. Si on ajoute les instructions suivantes dans la balise <style> de l’entête du fichier html :

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Démonstration des éléments de section HTML5</title>
    <style>
        span.elem {
            display: none;
        }

        body {
            width: 980px;
            margin: 0 auto 0 auto;
        }

        #main {
            float: left;
            width: 75%;
            margin: 0 0 50px 0;
        }

        #sidebar {
            float: left;
            width: 22%;
            margin-left: 1%;
            margin-top: 1em;
        }

        footer {
            clear: both;
        }

        nav ul li {
            display: inline-block;
        }
    </style>
</head>
<body>
    <!-- Header -->
...

La page prend l’aspect attendu. En faisant apparaitre les sections, on peut constater que la section “aside” est passée à droite (en rose).

Conclusion : Le code html contient le texte, les balises permettent de structurer la page c’est-à-dire définir les zones de contenu. Les feuilles de styles quant à elles, permettent de dimensionner et positionner les balises sectionnantes. Les feuilles de styles jouent aussi sur les éléments décoratifs de la page (couleurs, polices, encadrement…). Le code html contient le fond (la structure et le texte), le code css contient la forme (mise en page). Cette séparation permet à la page de s’adapter à n’importe quel terminal (écran d’ordinateur, tablette, téléphone…) En ajoutant à CSS quelques scripts, on peut créer une  mise en page « responsive » ou page réactive qui s’auto-adapte à l’écran.

Pour en savoir plus, voir le Memento CSS

  1. https://fr.wikipedia.org/wiki/Hypertext_Markup_Language#Description_de_HTML