Truc 25 : Gérer un Crud partagé en ligne avec Mongolab et AngularJs. (Avec $http )

Introduction


Lors de la création d’une application AngularJs, on peut enregistrer le modèle de données de l’application dans la mémoire du navigateur(// web storage html 5.0) ou dans une base de données MongoDB. Si on a pas MongoDB et Mongoose d’installé en local, on peut en attendant utiliser le site MongoLab en ligne pour stocker notre modèle de données assez simplement.

Mais pourquoi utiliser MongoDB ?

Pour créer une architecture partagée Restfull, des applications partagées par plusieurs utilisateurs, modernes et rapides, qui créent les champs dynamiquement. Par exemple, si dans mon programme, je permet à l’utilisateur de créer un nouveau contact avec des données en plus que la normale, une base de données relationelle doit être remaniée par un DBA, par contre, une base MongoDb répercute l’ajout automatiquement.

Pour faire du Big Data, également.

Pourquoi est-ce intéressant de créer des applications partagées en temps réel avec AngularJs-MongoDb?

Parce que c’est un plus par rapport aux applications standalone, dans un contexte commercial.

Cela permet de générer des statistiques en temps réel.

Y-a-t-il un problème ?

Par contre notre APIKey et notre mot de passe sont stockés en clair dans le code AngularJs.

Note : On peut également utiliser une base de données relationnelle, mais ce n’est pas le sujet de ce post.

Dans mon cas, cela va me servir à m’exercer à faire du CRUD avec MongoDb.

Comment faire?


  1. Créer un compte sur le site de Mongolab, nommer notre base de données, et créer notre API key qui va servir à la connexion.
  2. Ensuite 2 choix sont possibles : utiliser $http fourni avec Angular pour récupérer et mettre à jour notre modèle, OU utiliser un module spécifique nommé angular-mongolab.js qu’il faut télécharger et charger en tant que librairie additionnelle (module angular). Cette librairie est destinée à faciliter le CRUD dans l’application Angular.

Utiliser $http et gérer notre base de données Mongolab


 truc25

Tiré de cet exemple  en anglais :

http://stackoverflow.com/questions/24958580/angularjs-resource-mongolab-restful-api-whats-wrong-with-my-sample

La documentation du CRUD avec Mongolab est ici :

http://docs.mongolab.com/data-api/

La documentation de $http Angular est ici :

https://docs.angularjs.org/api/ng/service/$http

Explication :

On crée donc notre collection sur Mongolab, ensuite, voici le premier exemple qui permet la lecture et l’enregistrement en ligne de notre Objet Json directement dans Mongolab.

  • 1 controleur(et sa vue affiliée) permet la lecture des données,
  • 1 autre controleur(et sa vue affiliée) permet l’enregistrement des données sur MONGOLAB.

Le controleur de lecture étant « relu » lors du clic sur le lien « LISTE », cela mets automatiquement la liste des noms à jour, à partir de notre base de données Mongolab.

Le Code(Temps de réalisation 10 minutes) :

<!DOCTYPE html>
<html ng-app="monApp">
  <head>
   <meta charset="utf8" >
    <title>UN CRUD  dans une BDD MongoLab avec AngularJs</title>
        <!-- CHARGEMENT DES LIBRAIRIES -->
        <!-- APPEL LIB ANGULAR -->
        <script type="text/javascript" src="angular-1.3.13/angular.min.js"></script>
        <script src="angular-route.min.js"></script>
        <script src="librairies/angular-locale_fr-fr.js"></script>
                 <!-- CSS DE L'APPLICATION -->
        <link href="app10.css" media="screen" rel="stylesheet" type="text/css" />
		<!-- FIN DE CHARGEMENT DES LIBRAIRIES -->
	   <script>

        <!-- Déclaration des modules utilisés par l'application MVC Angular, ngRoute est obligatoire pour router les vues : Les modules sont spécifiés entre Crochets, on en trouve des centaines sur internet, ils extendent les capacités d'Angular, ce sont des librairies -->
         var monApp = angular.module('monApp',['ngRoute'])

		<!-- Configuration des vues et de leurs controleurs affiliés, avec ng-route . Note : Les vues peuvent partager le même controleur-->
        .config(function ($routeProvider, $locationProvider){
          $routeProvider
            .when('/accueil', {
              templateUrl: 'defaut.html',
              controller: 'accueilCtrl'
            })
            .when('/liste', {
              templateUrl: 'liste.html',
              controller: 'listeCtrl'
            })
            .when('/enregistrer', {
              templateUrl: 'enregistrer.html',
              controller: 'enregistrerCtrl'
            })

            .otherwise({redirectTo: '/accueil'});
        })

		<!-- LE CONTROLEUR DE L'ACCUEIL -->
        .controller("accueilCtrl", function($scope) {
			$scope.bonjour = "Mr Durand";
        <!-- FIN DU CONTROLEUR -->
        })

        <!-- LE CONTROLEUR DE LA LISTE -->
        .controller("listeCtrl", function($scope,$http) {

		$http.get('https://api.mongolab.com/api/1/databases/superjojo/collections/Test?apiKey=GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS')
			.success(function(data) {
				console.log(data)
				$scope.liste = data;
			});

		<!-- FIN DU CONTROLEUR -->
        })

        <!-- LE CONTROLEUR DE L ENREGISTREMENT -->
        .controller("enregistrerCtrl", function($scope,$http) {

			<!-- Initialisation des champs Input -->
			$scope.nom = "";
			$scope.age = "";

			<!-- Envoi des valeurs entrées par l'utilisateur dans notre database mongolab -->
			$scope.enregistrer = function(){
				$http.post('https://api.mongolab.com/api/1/databases/superjojo/collections/Test?apiKey=GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS', {"nom":$scope.nom,"age":$scope.age}, {
					headers: {
					'Content-Type': 'application/json; charset=UTF-8'
					}
				})
				.success(function() {
					console.log('Data saved successfully')
				});
			};

        <!-- FIN DU CONTROLEUR -->
        })

    </script>
 </head>

 <!-- La page HTML générale -->
<body ng-controller="accueilCtrl" >

<h1>UN CRUD  dans une BDD MongoLab avec AngularJs</h1>

<!-- Le menu, qui appelle,grâce à HREF, les différentes vues HTML situées plus bas, puis les affiche dans la balise ng-view -->

<div class="container ">

<div class="menu designDiv">
            <a href="#/accueil" class="">Accueil</a>
            <a href="#/liste" class="">Liste   </a>
            <a href="#/enregistrer" class="">Enregistrer</a>

        </div>

        <!-- Cette balise NG-View va afficher les différentes vues -->

<div id="main" class="designDiv">

<div ng-view></div>

        </div>

	</div>

</body>

<!-- Voici maintenant la liste des vues qui s'affichent dans la balise ng-view(voir plus haut), qui doivent généralement être dans un fichier séparé, normalement dans un réperoire "views", pour être à la norme BOWER -->
<!-- VUE HTML : DEFAUT-->
    <script type="text/ng-template" id="defaut.html">
        Bonjour {{bonjour}} je suis la vue de l'accueil.
    </script>

<!-- VUE HTML : LISTE -->
    <script type="text/ng-template" id="liste.html">
        Bonjour, je suis la vue qui présente une liste(collection) présente dans la base de donnée MongoLab.

<div ng-repeat="element in liste">Nom: {{element.nom}} Age:{{element.age}} </div>

    </script>

<!-- VUE HTML : ENREGISTRER -->
    <script type="text/ng-template" id="enregistrer.html">
        Bonjour, je suis la vue qui permet d'enregistrer un élèment dans la liste(collection)  dans la base de donnée Mongolab.

			<label>Nom</label>

				<input ng-model="nom" ></input>{{nom}} 

			<label>Age</label>

				<input type="number" ng-model="age"></input>{{age}}

		{{data}}
		<button ng-click="enregistrer(data)">Enregistrer</button>
	</script> 

Lors de l’utilisation, on voit bien dans la console la connexion à MongoDb :

crudang

Dans le controleur listrCtrl, on lit notre Json à partir de Mongolab avec $http, hors, on peut appliquer des paramêtres à cet endroit :

Optional parameters (MongoDB reference):

  • q= -restreindre les resultats à query
  • c=true – compter le nb de resultat
  • f= – specify the set of fields to include or exclude in each document (1 – include; 0 – exclude)
  • fo=true – return a single document from the result set (same as findOne() using the mongo shell
  • s= – specify the order in which to sort each specified field (1- ascending; -1 – descending)
  • sk= – specify the number of results to skip in the result set; useful for paging
  • l= – specify the limit for the number of results (default is 1000)

Examples utilisant ces paramêtres :

"q" example - return all documents with "active" field of true:
https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?q={"active": true}&apiKey=myAPIKey

"c" example - return the count of documents with "active" of true:
https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?q={"active": true}&c=true&apiKey=myAPIKey

"f" example (include) - return all documents but include only the "firstName" and "lastName" fields:
https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?f={"firstName": 1, "lastName": 1}&apiKey=myAPIKey

"f" example (exclude) - return all documents but exclude the "notes" field:
https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?f={"notes": 0}&apiKey=myAPIKey

"fo" example - return a single document matching "active" field of true:
https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?q={"active": true}&fo=true&apiKey=myAPIKey

"s" example - return all documents sorted by "priority" ascending and "difficulty" descending:
 https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?s={"priority": 1, "difficulty": -1}&apiKey=myAPIKey

"sk" and "l" example - sort by "name" ascending and return 10 documents after skipping 20
 https://api.mongolab.com/api/1/databases/my-db/collections/my-coll?s={"name":1}&sk=20&l=10&apiKey=myAPIKey
  • EXEMPLE 2 : On peut maintenant supprimer et mettre à jour nos données directement en ligne !

Le code précédent permettait uniquement d’enregistrer et de lire, maintenant, on va créer le reste du code pour pouvoir updater et supprimer un élèment de notre modèle partagé en ligne sur mongolab.

Explication :

J’ai ajouté 2 fonctions, déclenchés à partir de ng-click, une fonction appellée $scope.supprimer qui contient $http.delete, qui permet de supprimer un nom en fonction de son id (c’est l_id généré par Mongolab lors de la création d’une personne.)

une autre fonction d’update appelée $scope.miseajour permet de charger les données à modifier, puis dans un deuxième temps, de valider la MAJ, avec une seconde fonction appelée $scope.validerMaj.

Le code :


</h2>

<!DOCTYPE html>
<html ng-app="monApp">
  <head>
   <meta charset="utf8" >
    <title>UN CRUD Partagé en ligne dans une BDD MongoLab avec AngularJs</title>
        <!-- CHARGEMENT DES LIBRAIRIES -->
        <!-- APPEL LIB ANGULAR -->
			<script type="text/javascript" src="angular-1.3.13/angular.min.js"></script>
			<script src="angular-route.min.js"></script>
			<script src="librairies/angular-locale_fr-fr.js"></script>
		<!-- CSS DE L'APPLICATION -->
			<link href="truc25.css" media="screen" rel="stylesheet" type="text/css" />
		<!-- FIN DE CHARGEMENT DES LIBRAIRIES -->
	   <script>

        <!-- Déclaration des modules utilisés par l'application MVC Angular, ngRoute est obligatoire pour router les vues : Les modules sont spécifiés entre Crochets, on en trouve des centaines sur internet, ils extendent les capacités d'Angular, ce sont des librairies -->
         var monApp = angular.module('monApp',['ngRoute'])

		<!-- Configuration des vues et de leurs controleurs affiliés, avec ng-route . Note : Les vues peuvent partager le même controleur-->
        .config(function ($routeProvider, $locationProvider,$httpProvider){

		    delete $httpProvider.defaults.headers.common['X-Requested-With']; 

		  $routeProvider
            .when('/accueil', {
              templateUrl: 'defaut.html',
              controller: 'accueilCtrl'
            })
            .when('/liste', {
              templateUrl: 'liste.html',
              controller: 'listeCtrl'
            })
            .when('/creer', {
              templateUrl: 'enregistrer.html',
              controller: 'enregistrerCtrl'
            })
			.when('/miseajour', {
              templateUrl: 'miseajour.html',
              controller: 'miseajourCtrl'
            })
            .otherwise({redirectTo: '/accueil'});

        })

		<!-- LE CONTROLEUR DE L'ACCUEIL -->
        .controller("accueilCtrl", function($scope) {
			$scope.bonjour = "Mr Durand";
        <!-- FIN DU CONTROLEUR -->
        })

        <!-- LE CONTROLEUR DE LA LISTE, DE LA SUPPRESSION ET DE LA MISE A JOUR -->
        .controller("listeCtrl", function($scope,$http) {

			<!-- LIT LES DONNEES EN LIGNE SUR MONGOLAB -->
			$http.get('https://api.mongolab.com/api/1/databases/superjojo/collections/Test?apiKey=GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS')
				.success(function(data) {
					console.log(data)
					$scope.liste = data;
				});

			<!-- SUPRIME UN UTILISATEUR DANS MONGOLAB A LAIDE DE SON ID -->
			$scope.supprimer = function(id){
				$http.delete('https://api.mongolab.com/api/1/databases/superjojo/collections/Test/'+id,
						{
							params:{
							 apiKey:'GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS'
							}
						}
					)
					<!-- MISE A JOUR DE LA LISTE APRES SUPPRESSION -->
					.success(function() {
						$http.get('https://api.mongolab.com/api/1/databases/superjojo/collections/Test?apiKey=GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS')
						.success(function(data) {
							console.log(data)
							$scope.liste = data;
						});
					});
			}	 

			<!-- MISE A JOUR DES DONNEES DUN UTILISATEUR SUR MONGOLAB -->

			<!-- Chargement des donnees dans la div de modification -->
			$scope.miseajour = 	function(id){

					$http.get('https://api.mongolab.com/api/1/databases/superjojo/collections/Test/'+id,
						{
							params:{
							 apiKey:'GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS'
							}
						}
					).success(function(data) {
					console.log(data)
					$scope.majNom = data.nom;
					$scope.majAge = data.age;
					$scope.majId = data._id.$oid;

					});
			}	

			<!-- Validation d une mise à jour -->

			$scope.validerMaj = function(){

				$http.put('https://api.mongolab.com/api/1/databases/superjojo/collections/Test/'+$scope.majId,{"nom":$scope.majNom,"age":$scope.majAge},
						{
							params:{
							 apiKey:'GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS'
							}
						}
					).success(function(data) {
					$http.get('https://api.mongolab.com/api/1/databases/superjojo/collections/Test?apiKey=GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS')
						.success(function(data) {
							console.log(data)
							$scope.liste = data;
						});
					});

			};
		<!-- FIN DU CONTROLEUR -->
        })

        <!-- LE CONTROLEUR DE L ENREGISTREMENT -->
        .controller("enregistrerCtrl", function($scope,$http) {

			<!-- Initialisation des champs Input -->
			$scope.nom = "";
			$scope.age = "";

			<!-- Envoi des valeurs entrées par l'utilisateur dans notre database mongolab -->
			$scope.enregistrer = function(){
				$http.post('https://api.mongolab.com/api/1/databases/superjojo/collections/Test?apiKey=GP50LkzEgoxBnZDP9N-jCkpO5yln2gIS', {"nom":$scope.nom,"age":$scope.age}, {
					headers: {
					'Content-Type': 'application/json; charset=UTF-8'
					}
				})
				.success(function() {
					console.log('Data saved successfully');
					alert('Utilisateur ajouté en ligne dans MONGOLAB! ')
				});
			};

		<!-- FIN DU CONTROLEUR -->
        })

    </script>
 </head>

 <!-- La page HTML générale -->
<body ng-controller="accueilCtrl" >
	<img src="MongoLab.png" ></img>

<h1>UN CRUD  dans une BDD MongoLab avec AngularJs</h1>

<!-- Le menu, qui appelle,grâce à HREF, les différentes vues HTML situées plus bas, puis les affiche dans la balise ng-view -->

<div class="container ">

<div class="menu designDiv">
            <a href="#/accueil" class="">Accueil</a>
            <a href="#/liste" class="">Liste   </a>
            <a href="#/creer" class="">Créer</a>

        </div>

        <!-- Cette balise NG-View va afficher les différentes vues -->

<div id="main" class="designDiv">

<div ng-view></div>

        </div>

	</div>

</body>

<!-- Voici maintenant la liste des vues qui s'affichent dans la balise ng-view(voir plus haut), qui doivent généralement être dans un fichier séparé, normalement dans un réperoire "views", pour être à la norme BOWER -->
<!-- VUE HTML : DEFAUT-->
    <script type="text/ng-template" id="defaut.html">
        Bonjour {{bonjour}} je suis la vue de l'accueil.
    </script>

<!-- VUE HTML : LISTE -->
    <script type="text/ng-template" id="liste.html">

<div class="listStyle">
			Bonjour, je suis la vue qui présente une liste(collection) présente dans la base de donnée MongoLab.

<div class="personne" ng-repeat="element in liste">

<h2>Nom:{{element.nom}} </h2>

  Age:{{element.age}} 

			<button class="button" ng-click="supprimer(element._id.$oid)">Supprimer</button>
			<button class="button" ng-click="miseajour(element._id.$oid)">Mettre à jour</button>
			</div>

		</div>

<div class="majStyle">
			Bonjour, je suis la DIV qui permet de mettre à jour les données sur Mongolab
			<label>Nom</label>

				<input ng-model="majNom" ></input>{{majnom}} 

			<label>Age</label>

				<input type="number" ng-model="majAge"></input>{{majage}}

			<button class="button" ng-click="validerMaj()">Enregistrer sur Mongolab</button>

		</div>

    </script>

<!-- VUE HTML : ENREGISTRER -->
    <script type="text/ng-template" id="enregistrer.html">
        Bonjour, je suis la vue qui permet d'enregistrer un élèment dans la liste(collection)  dans la base de donnée Mongolab.

			<label>Nom</label>

				<input ng-model="nom" ></input>{{nom}} 

			<label>Age</label>

				<input type="number" ng-model="age"></input>{{age}}

		{{data}}
		<button ng-click="enregistrer(data)">Enregistrer</button>
	</script> 

<style>
   .listStyle{

   background-color:lightgrey;
   width : 50%;
   float:left;
   }
   .majStyle{
	background-color:ivory;
   width : 50%;
   float:left;
   }
   .personne{
   border-top:20px;
   background-color:ivory;
	border:1px solid grey;
	border-width:1px 1px 1px 1px;
	}
	.button{
   float:right;
	}
</style>

CONCLUSION de l’exemple 3.


On comprends qu’il faut utiliser un framework spécifique, qui mettra à jour automatiquement les vues, sans cela il faut à chaque fois rafraichir les vues manuellement.

De plus, si un ordinateur modifie l’age d’une personne, vu qu’il n’y a pas de rafraichissement en temps réel de programmé(pas de timer), et bien le deuxième ordinateur en réseau ne voit pas la modification s’appliquer en temps réel dans son navigateur, alors que la valeur a bel et bien changé dans la base de données mongolab… Il faut donc travailler cela (En créant un refresh automatisé, c’est pas trop dur à faire.). Aucune idée pour l’instant de comment gérer la concurrence avec ce  système.

Pour mes futurs projets collaboratifs, la piste est Socket.Io

Exemple 3 : Créer un service $Http.


Dans les 2 exemples précèdents, on constate que les accès $http ne sont pas centralisés. Il est plutôt préconisé, d’après ce que je vois sur Internet, de créer un service dédié au CRUD. Nous allons donc essayer d’en faire un, qui se connectera sur MONGOLAB.

Par ailleurs, contrairement à JQUERY, qui dispose d’une fonction très pratique  serialize() qui récupère instantanément tous les champs d’un formulaire pour les transmettre au Back End, je ne connais pas encore l’équivalent en ANGULAR.

Des exemples, en Anglais, de services $HTTP :

  • http://weblog.west-wind.com/posts/2014/Oct/24/AngularJs-and-Promises-with-the-http-Service
  • http://stackoverflow.com/questions/16227644/angularjs-factory-http-service
  • https://www.airpair.com/javascript/posts/services-in-angularjs-simplified-with-examples
Publicités