Micro App 11 : » Evalue tes Shoes » . Routing Angular et sous élèments de modèle dynamiques.

Introduction :

WISTERIA

WISTERIA

Pour tester  mon appli (En cours de dev !):

Log In : ADMIN, Pass :Admin

Log in : DURAND, Pass : pas de mot de passe

Log in : Hammed, Pass : Hammed


Evalue tes Shoes, Micro App d’évaluation commerciale et d’aide à la décision  avec routing AngularJs et création d’élèments de modèle à la volée par l’utilisateur( et un anglicisme dans le titre, j’ai pas pu m’empécher, lol). Depuis 3 jours, j’étais sur un exercice de  micro Application dérivée de truc19. je suis parti de zéro, comme d’hab. Evolution majeure dans mon code, j’utilise désormais le routing de vues et les controleurs dédiés par vues, ce qui provoque une structuration et un gain de performance indéniable, qui permet d’envisager l’élaboration de vraies grosses applications robustes en temps réel.

Synopsys :


Un agent commercial a besoin d’un logiciel en ligne pour évaluer les produits qu’il compte acheter en gros. En l’occurence, ce sont des baskets.

Descriptif de l’application :


  • Type : Micro- Application Desktop Web de type Saas.
  • Langage : Js, Angular, Bootstrap UI, Html 5.0, Css3.0
  • Bibliothèques : Jqplot, nd3.js pour angular
  • Format d’échanges de Données : JSON
  • Back-end : Oui, une factory Angular pour Php.
  • Difficulté : +++/+++++
  • Test avec jasmine : Non.
  • Durée de codage : Entre 3 Jours et 1 Semaine. ( 2 semaines pour la version collborative)
  • UML 2.0 : non.
  • Webstorage(stockage des données dans le navigateur) : Une factory Local Storage.
  • Gestion de la concurence : non.
  • Webservice : non.
  • Authentification : Oui en Php Mysql.
  • Minification Grunt : non.
  • Nodes.Js : non.
  • MongoDb : non.
  • Ambition Commerciale : non.
  • Etude de Marché, Barrières commerciales  : non.

Suite:


De plus, dans cette micro app, j’ai aussi créé des sous élèments dans le modèles, directement paramétrables par l’utilisateur, en l’occurence, il peut créer ses propres évaluations à la volées en live. Les statististiques sont alors mises à jour en temps réel par le data binding d’angular, et ce sans latence . Ce qui est fou, c’est qu’un controleur peut planter si l’on y fait une erreur, mais le reste de l’appli et des autres controleurs continuent à fonctionner … C’est comme si on avait plusieurs micros-applis fonctionnant en symbiose. Le code devient particulièrement clair, grâce à l’usage des controleurs séparés, par exemple, la vue des statistiques possède toutes ses méthodes affilées dans son propre controleur, on peut y coder avec simplicité des moyennes, des pourcentages, des graphes s’appliquant sur le modèle et j’en passe, bref le programme de 1ère Es T Es en stats, avec les pourcentages d’évolution, les probabilités aussi. Comme les vues sont séparées, je n’aurais plus les problèmes que j’avais dans mes exercices précèdents, et je suis très content. Il est probable que je crée une vue dédiée aux graphes avec son controleur dédié. La factory serviceData pourra être doublée par une factory assurant le crud avec java ou Php, mettant à jour un élèment en base de donnée, en déconstruisant l’objet JSON comme d’hab. Enfin, mon code est à la norme bower -npm, mais dans un seul fichier, par soucis de commodité pour l’instant. ayant installé Npm et bower et grunt, mes prochains codes pourront être minimifiés. Je continue à travailler dessus.N’ayant pas internet, je susi très géné pour trouver de nouveaux designs de ratings, de même que pour les opérations compliquées, mais je devrais le récupérer bientot.

Journal :


J+9 :3 h de code:

  • Point par points, je réecris tout le code pour élaborer une appli collaborative.
  • Bien sur, les évaluation d’un utilisateur ne sont pas les mêmes que celles d’un autre utilisateur. On peut déjà le constater en créant une chaussure, puis en se loggant sous un autre utilisateur.
  • Soyez indulgents car c’est en cours de dev, et les stats collaboratives ne marchent pas encore, je le ferais demain.
  • Le véritable partage du modèle en temps réel  entre plusieurs ordinateurs est bien plus compliqué à coder, et il faudra probablement utiliser Node.js pour que le modèle soit mis à jour en temps réel à partir de MongoDb, de plus la gestion de la concurrence risque d’être compliquée à coder(mais pas forcément). Peut être les websockets via socket.io seraient aussi utiles. Ca devient vraiment hard et je suis mal, il faut que je réalise des exercices en real time WebSocket en Angular-mongoDb !
  • https://blog.pusher.com/making-angular-js-realtime-with-pusher/
  • https://github.com/angular-class/angular-websocket
  • https://hypedrivendev.wordpress.com/2013/12/19/angular-express-socketio/
  • http://www.html5rocks.com/en/tutorials/frameworks/angular-websockets/?redirect_from_locale=fr
  • En fait, le fait de pouvoir créer des évaluation en temps réel limite la possibilité d’utiliser une base de données relationelle, car cela devient vraiment compliqué, par rapport à simplement stocker le modèle dans mongoDb que l’on mets à jour en temps réel avec un socket, c’est vraiment une autre technologie que Php Mysql ou Java Mysql.

J+8: Voici la nouvelle version collaborative :WISTERIA

Je récapitule donc le fonctionnement de cette nouvelle version :

    • Le modèle de données est unique.
    • L’identification est gérée avec Php-Mysql.
    • Seul l’admin a le droit de supprimer un objet.
    • Tous les collaborateurs peuvent visionner les évaluations données par les autres collaborateurs (Le modèle est filtré en fonction de l’Id du collaborateur. Chaque nouvelle évaluation ajoutée au modèle comporte l’id du collaborateur)
    • Un background node.js serait bien plus flexible et facile à mettre en place qu’un background Php ou Java pour la gestion de la persistance.
    • La séparation par vue/controleur permet de retrouver plus facilement quelle partie du code modifier en fonction de la vue.

Je le mettrais en ligne ces jours ci

J+7 :

  • Authentification Php-Sql par mot de passe
  • Chargement du modèle sous contrainte d’ authentification.
  • Affichage du menu en fonction de l’id Utilisateur.

Je ne publie pour l’instant pas le nouveau code avec l’authentification PHP.

Pour obtenir un logiciel collaboratif, j’ai du affecter l’id utilisateur aux évaluations, après une certaine réflexion. Je dois ainsi changer pas mal de choses dans toutes les méthodes d’édition, d’ajout et de supression, pour prendre en compte ce changement et faire plein de vérifications.

De ce fait, tout le monde partage le modèle unique, mais des évaluations de personnes différentes sont présente dans le modèle unique. Après le login, le modèle est filtré en fonction de l’id utilisateur courant.

Cela serait bien plus facile de le faire marcher avec Node.js, car ensuite, coder la partie en Php-Sql ne sera pas forcément facile, avec les évaluations dynamiques, car il va falloir les stocker en BDD.. mais bon je vais essayer.

J’ai séparé les fichiers Apps, controleurs, Css, factory, lib sonore (norme bower-Grunt) et php, cela demeure assez simple, en tous les cas bien plus simple qu’une application ancienne.

Pour exemple, le modèle est légèrement plus compliqué, avec les ids utilisateurs  précisés pour chaque évaluation(cela sert ensuite à filtrer)  exemple :


var listeObjetsTest =[
					{"id":"0",	"inactif":false	,"nom":"Air",		"marque":"Nike",	"prix":32.99,	"photo":"nike1.jpg","notes" :[
						{"idEval":"0","idt":"0","evaluation":"Look"	,					"note":15,"photo":"look.jpg"},
						{"idEval":"1","idt":"1","evaluation":"Qualité de Fabrication",	"note":15,"photo":"fabrication.png"},
						{"idEval":"2","idt":"2","evaluation":"Satisfaction Client"	,	"note":60,"photo":"satisfaction.png"},
						{"idEval":"3","idt":"1","evaluation":"Ventes"	,				"note":15,"photo":"ventes.png"}
					]
					,"moyenneEvaluation":""
					},
					{"id":"1",	"inactif":false	,"nom":"x20",		"marque":"Reebok",	"prix":54.25,	"photo":"reebok1.jpg","notes" :[
						{"idEval":"0","idt":"0","evaluation":"Look"	,					"note":30,"photo":"look.jpg"},
						{"idEval":"1","idt":"0","evaluation":"Qualité de Fabrication",	"note":30,"photo":"fabrication.png"},
						{"idEval":"2","idt":"1","evaluation":"Satisfaction Client"	,	"note":45,"photo":"satisfaction.png"},
						{"idEval":"3","idt":"2","evaluation":"Ventes"	,				"note":80,"photo":"ventes.png"}
					]
					,"moyenneEvaluation":""
					},
				];

j+5 :

  • Possibilité de modifier les photos des évaluations.
  • Début d’élaboration du système collaboratif avec un background mysql-php, et une factory Angular dédiée, ainsi qu’un système d’authentification ainsi que de droits en lecture.
  • En fait, chque commercial va pouvoir consulter les évaluations que les autres commerciaux ont donné à la même série de chaussures, en graphes et en chiffres.

j+4 :

  • En train de créer les graphiques, j’ai repéré la lib de graphs dédiées à angular basée sur D3.js :https://cmaurer.github.io/angularjs-nvd3-directives/, je vais essayer de m’en servir, car j’ai essayé d’intégrer du d3.js, et ça ne passait pas dans la vue dédiée à cela.
  • Par ailleurs, pas mal de problèmes pour générer une stat qui arrive dans un objet mal formé et doivent petre converties en un tableau Js, pour être interprété par le graphe. C’est plus difficile qu’en SQL, largement , quand on a pas l’habitude, heureusement je journalise tous les algoritmes de ce style( exemple : Donner le pourcentage d’objets par marques dans un tableau d’objets Json), ce qui me permettra d’aller 10 fois plus vite à l’avenir.
  • L’idiot de bouton d’édition d’un objet fonctionne dans chrome  mais pas dans firefox.
  • J’ai redesigné les range html5.0 avec ça :http://codepen.io/aronwoost/pen/nlyrf

MON CODE


Le fichier Index.html de la V2 à j+8 qui contient également les vues:

<!DOCTYPE html>
<html ng-app="myApp">
  <head>
  <!-- <base href="truc21.html#/accueil"></base> -->
   <meta charset="utf8" >
    <title>Gestion d'objets avec Angular Avec Routing de vues.</title>
		<!--	APPEL LIB ANGULAR -->
		<script	type="text/javascript" 	src="angular-1.3.13/angular.min.js"></script>
		<script src="librairies/angular-locale_fr-fr.js"></script>
		<script src="angular-route.min.js"></script>
		<script src="angular-filter.min.js"></script>
		
		<!--	APPEL LIB BOOTSTRAP -->
		<script src="librairies/ui-bootstrap.min.js"></script>
		<link rel="stylesheet" href="librairies/bootstrap/css/bootstrap.min.css">
		<link rel="stylesheet" href="librairies/font-awesome-4.3.0/css/font-awesome.min.css">
		
		<!--	APPEL LIB JQUERY -->
		<script	type="text/javascript" 	src="librairies/jquery.min.js"></script>
	
		<!--	APPEL LIB JQPLOT -->
		<script type="text/javascript" src="librairies/jquery.jqplot.js"></script>
		<link 	rel="stylesheet" type="text/css" href="librairies/jquery.jqplot.css" />
		
		<!-- 	Plug ins JQPLOT -->
		<script type="text/javascript" src="librairies/plugins/jqplot.barRenderer.min.js">			</script>
		<script type="text/javascript" src="librairies/plugins/jqplot.pieRenderer.min.js">			</script>
		<script type="text/javascript" src="librairies/plugins/jqplot.donutRenderer.min.js">		</script>
		
		<script type="text/javascript" src="librairies/plugins/jqplot.categoryAxisRenderer.min.js">	</script>
		<script type="text/javascript" src="librairies/plugins/jqplot.pointLabels.min.js">			</script>
		<script type="text/javascript" src="librairies/plugins/jqplot.json2.min.js">				</script>
		
		
		<!-- FICHIERS DE L APPLICATION -->
		<script type="text/javascript" src="truc21controllerscollab.js"></script>
		<script type="text/javascript" src="truc21libSonore.js"></script>
		<script	type="text/javascript" 	src="truc21Factorycollab.js"></script>
	</head>

<body>
	<div class="global-wrapper" ng-app="myApp">
		
		
<!-- LE MENU -->
		  <nav class="navbar navbar-default" ng-controller="accueilCtrl" >
			<div class="container-fluid">
			  <div class="nav-header" ng-hide="checkPresenceId()">
				<img src="evaluetesshoes.jpg" class="logo right"> </img>
				
				<a onmouseover="mouseoversound.playclip()" href="#/accueil" class="navbar-brand hidden-xs"><button class="btn"><img src="home.png" class="imgMenu">Accueil</img></button></a>
				
				<a onmouseover="mouseoversound.playclip()" href="#/nouveau" ng-click="nouvelObjet()" class="menuhide navbar-brand hidden-xs"><button  class="btn"><img src="commande.gif"  class="imgMenu">Nouveau</img></button></a>
				
				<a onmouseover="mouseoversound.playclip()" href="#/statistiques" class="menuhide navbar-brand hidden-xs"><button class="btn"><img src="pourcentage.png" class="imgMenu">Stats</img></button></a>
				
				<a onmouseover="mouseoversound.playclip()" href="#/statistiquesG" class="menuhide navbar-brand hidden-xs"><button class="btn"><img src="stats.png" class="imgMenu">Graphes</img></button></a>
				
				
				<a onmouseover="mouseoversound.playclip()" href="#/sauver"  class="menuhide navbar-brand hidden-xs"><button  class="btn"><img src="ok.jpg"  class="imgMenu">Sauvegarder en Bdd</img></button></a>
				
				
				<a onmouseover="mouseoversound.playclip()" href="#/collaboratif"  class="menuhide navbar-brand hidden-xs"><button  class="btn"><img src="entretien.jpg"  class="imgMenu">Stats Collaboratives</img></button></a>
				
				<img src="userblue.png"  class="imgMenu"></img> <input type="text" ng-model="name.value"></input>
			</div>
		  </nav>
  
	
<!-- LES FILTRES -->
	<div class="container">
		<div class="row panel panel-primary" ng-controller="accueilCtrl">
			<div class="panel-heading" ng-hide="checkPresenceId()">
				<h2 class="panel-title">Filtres</h2>	
				<select class="lettresNoires selectpicker" data-ng-model="filtreMarque" ng-options="option.marque as option.marque for option in objets | unique:'marque' "  >
				<option value="">Trier par marques ...</option>
				</select>{{compte}}
				
				<select class="lettresNoires " data-ng-model="rangePrixMin">
					<option value="1">Par Prix Minimum...</option>
					<option value="30">20€</option>
					<option value="50">50€</option>
					<option value="100">100€</option>
				</select>
				
				<select class="lettresNoires" data-ng-model="rangePrixMax">
					<option value="100000">Par Prix Maximum...</option>
					<option value="30">30€</option>
					<option value="50">50€</option>
					<option value="100">100€</option>
				</select>
				
				<select class="lettresNoires" ng-model="rangemoyenneEvaluation"   >
					<option value="99">Par Evaluation...</option>
					<option value="30">0%->30%</option>
					<option value="60">0%->60%</option>
					<option value="99">0%->100%</option>
				</select>
				
				<select class="lettresNoires"  ng-model="choixFiltre"  >
					<option value="ordre" selected>Par Ordre...</option>
					<option  value="marque">->Alphabétique</option>
					<option value="prix">->De prix...</option>
					<option value="moyenneEvaluation">->D'évaluation</option>
				</select>
				
			</div>
			

<!-- <LISTE DES OBJETS> -->
			<div class="ascenceur col-xs-3 panel-body ">
				
			<div ng-repeat="objet in objets |filter:filtreMarque|filter:filtrePrixMin|filter:filtrePrixMax|filter:filtreMoyenneEval|orderBy:choixFiltre "  class="list-group-item cart">
					
					<a ng-href="#details" ng-click="setIdCourant(objet.id)"><button class="btn	max-w" ng-href="#details" ng-click="setIdCourant(objet.id)"> 
					<image src="{{objet.photo}}" class="titeClasse"></img>{{objet.marque}}  {{objet.nom}}       <br> <b>Evaluation Commerciale</b> :{{getMoyenneEvaluationParIdUtilisateur(objet.id)}}%<br>
					{{objet.prix}} Euros<br>
					<!-- id = {{objet.id}} -->
					</button></a>
					
					<button type="button" class="btn btn-danger pull-right "  ng-click="supprimerObjet(objet.id)"><i class="fa fa-trash-o"></i></a></button> 
					
					<button type="button" class="btn btn-edit pull-right " ng-href="#editDetails" ng-click="setIdCourant(objet.id)"><a ng-href="#editDetails" ng-click="setIdCourant(objet.id)"><i  class="glyphicon glyphicon-pencil" ></i></a></button>
					
					<input type="checkbox"    ></input>
				</div>
				
			</div>
			
			<!--   ng-view Plus bas  se rafraichit sur demande avec la vue appropriée     -->
			<div class="col-xs-9">
				<div class="form-group panel-body list-group-item">
					<div ng-view></div>
				</div>
			</div>
		</div>
	</div>

  
 <!--  LES VUES -->
  
 <!--  VUES HTML habituellement contenus dans des fichiers HMTL séparés, Chaque vue dispose généralement de son controleur angular,  défini dans .config -->
 
 
<!--  VUE DU LOGIN -->
<script type="text/ng-template" id="default.html">
    <div class="login alert alert-success text-center" ng-class="{hidden : !removed}"> {{removed}}</div>
	Bonjour, Veuillez vous identifier : 
	<div class="login list-group-item text-center">
		
		<select placeholder="Nom" ng-model="choixUser" ng-options="option.nom for option in utilisateurs  " >
		</select>
		<input placeholder="Entrez votre Mot de Passe" ng-model="passwordTest" type = "text">
		</input>
		<button type="button" class="btn btn-info pull-right " ng-click="loginUtilisateur()" ng-href="#tableauDeBord"><span class="glyphicon glyphicon-ok"></span>
		</button>
		{{informationLogin}}
	</div>
	  
</script>

 
 
 
 
 <!--VUE HTML  CARACTERISTIQUES DE LOBJET-->
    <script type="text/ng-template" id="details.html">
			<!-- <ma-directive></ma-directive> -->
			<h1>{{objets[idCourant].marque}} {{objets[idCourant].nom}}</h1>
			<p><image src="{{objets[idCourant].photo}}" class="imgClass"></img></p>
			<p>{{objets[idCourant].id}}<p>
			<p>{{objets[idCourant].nom}}<p>
			<p>{{objets[idCourant].marque}}</p>
			<p>{{objets[idCourant].prix}} Euros	</p>
			<p>{{objets[idCourant].photo}} 	</p>
			<a ng-href="#editDetails" ><button class="right btn" ng-href="#editDetails" > 
			<span class="glyphicon glyphicon-pencil"></span>
			Editer
			</button> </a>
	</script>
 
 
 
 
 
 <!--  VUE HTML : EDITER UN OBJET-->
    <script type="text/ng-template" id="editDetails.html">
		<tabset>
			<tab heading="Généralités">
				<div class="params">
					
					<!-- GENERALITES DE LA CHAUSSURE -->
					<h1>{{objets[idCourant].marque}} {{objets[idCourant].nom}} </h1>
					<p><image src="{{objets[idCourant].photo}}" class="imgClass" style="float:right;"></img></p>
						<textarea ng-model="objets[idCourant].note" class="right textarea" placeholder="Notes..."></textarea>
					<p><label for="nom">Nom:</label> <br><input ng-model="::objets[idCourant].nom"></input></p>
				
					<p><label for="marque">Marque:</label><br> <select ng-model="objets[idCourant].marque" ng-options="option.marque as option.marque for option in objets | unique:'marque'  " ></select>
					<input ng-model="nouvelleMarque.content"  placeholder="Nouvelle Marque..."></input>
					<button class=" " ng-click="ajouteMarque()"> <span class="glyphicon glyphicon-plus-sign" ng-click="ajouteMarque()"></span></button> 
					</p>
					
					<p><label for="nom">Prix:</label><br> <input type="number" ng-model="objets[idCourant].prix">Euros		</input></p>
					<p><label for="nom">Photo:</label><br><input ng-model="objets[idCourant].photo" disabled>		</input></p>

				</div>
			</tab>	
		
		<!-- EVALUATION PAR CURSEURS -->
		<tab heading="Evaluation commerciale">
			<div class="params  "> 
				
				<h1>Evaluation Commerciale:</h1> 
				
				
				
				<h3>Moyenne des évaluations: {{getMoyenneEvaluation(idCourant)}}%</h3>
				
				<div  ng-repeat="element in objets[idCourant].notes | filter:filtreIdt " class="slider">
					<img src="{{element.photo}}" class="imgMenu"></img>
					{{element.evaluation}}:{{element.note}}%
					<input ng-model="element.note" type="range" min="0" max="100" class="range"  /></input> 
					<button type="button" class="btn btn-danger pull-right " ng-click="deleteEval(idCourant,element.idEval)"><i class="fa fa-trash-o"></i></button>
				</div>
				
				
				<!-- AJOUT DUNE EVALUATION -->
				
				<br><br>
				<div id ="ajoutEval">
					
					
					<img class="imgMenu" src="{{choixImgEval}}"></img>
					<input type="text" ng-model="newEval.content" placeholder="Nouvelle Evaluation..." ></input>
					<button class="" ng-click="ajoutEval(idCourant)"> <span class="glyphicon glyphicon-plus-sign"  ></span></button>
					<div id="listePhotos">Image
						<div class="listePhotoUnique" ng-repeat="stuff in listeimgEval">
							<img src="{{stuff.photo}}" ng-click="setImgEval(stuff.photo)" class="imgMenu"></src>
						</div>
					</div>	
				</div>
			</div>
		</tab>
		
		<tab heading="Comparatif statistique">
		</tab>
		<br>
		
		
		</tabset>
		
		<!-- BOUTONS DE FIN DE PAGE -->
		<a onmouseover="mouseoversound.playclip()" ng-href="#details" >
			<button class="right btn btn-info" ng-href="#details"> 
			<span class="glyphicon glyphicon-ok"></span>
			Valider
		</button></a>
		
		<a onmouseover="mouseoversound.playclip()" ng-href="#details" >
			<button class="right btn btn-warning" ng-href="#details"> 
				<span class="glyphicon glyphicon-ok"></span>
			Annuler
		</button> </a>
			
	</script>
	
	
	
	
	
	

<!--  VUE HTML : STATISTIQUES-->
    <script type="text/ng-template" id="stats.html">
		<h1>Les Statistiques</h1>
		<tabset>
			<tab heading="Enumérations">
				<div class="form-group panel-body">
					<p>Nombre total d'objets :</h4> {{getTotal()}} Objets</p>
					<p>Cout total des objets :</h4> {{getCoutTotal()}} Euros</p>
			
					<p>Nombre d'objets par marque:</h4>{{getNbParMarque()}} </p>
				</div>
			</tab>
			<tab heading="Moyennes">
				<div class="form-group panel-body">
					<p>Moyenne totale du prix des objets :</h4>{{getPrixMoyen()}} Euros</p>
				</div>
			</tab>
			<tab heading="Pourcentages">
				<div class="form-group panel-body">
					A venir
				</div>
			</tab>
		</tabset>
    </script>
	

	
	
	
	
<!-- VUE HTML STATISTIQUES GRAPHIQUES -->
	 <script type="text/ng-template" id="statsG.html">
		<h1>Les Statistiques Graphiques</h1>
		<div id= "statsConteneur">
		
				<div class="wide" id="stats" ng-model="G1()"> </div>
				
			
				<div class=" " id="stats2" ng-model="G2()">test </div>
				
			
			
		
		</div>
	</script>
	

	
	
	
	
<!-- VUE HTML STATISTIQUES COLLABORATIVES -->
	 <script type="text/ng-template" id="collaboratif.html">
		<h1>Les Statistiques Collaboratives</h1>
		{{choixUserEval.id}}
				<img src="userblue.png"  class="imgMenu">
				<select placeholder="Nom" ng-model="choixUserEval" ng-options="option.nom for option in utilisateurs  " >
				</select>
				<select placeholder="Nom" ng-model="choixObjetEval" ng-options="option.nom for option in objets  " >
				</select>
				
				<div  ng-init="isDisabled = true" ng-repeat="element in objets[choixObjetEval.id].notes|filter:choixUserEval.id  " class="slider" >
					<img src="{{element.photo}}" class="imgMenu"></img>
					{{element.evaluation}}:{{element.note}}%
					<input disabled="{{isDisabled}}" ng-init="isDisabled = true" ng-model="element.note" type="range" min="0" max="100" class="range"  /></input> 
					
				</div>
	</script>

	
	<!-- VUE HTML TABLEAU DE BORD -->
	 <script type="text/ng-template" id="tabBord.html">
		<h1>Votre tableau de Bord</h1>
		<div id= "">
		</div>
	</script>
	
	
	
	
	
	
	
	
	
	
	
	
	<link rel="stylesheet" href="truc21collab.css"></link>
	

Le fichier des controleur à j+8( les controleurs doivent être dans des fichiers séparés) :



		
		var myApp = angular.module('myApp', ['ngRoute','angular.filter','ui.bootstrap'])
		
		/* <!-- Configuration des vues avec ng-route --> */
		.config(function ($routeProvider, $locationProvider){
		  $routeProvider
			.when('/accueil', {
			  templateUrl: 'default.html',
			   controller: 'loginCtrl'
			})
			.when('/tableauDeBord', {
			  templateUrl: 'tabBord.html',
			   controller: 'tabBordCtrl'
			})
			.when('/details', {
			  templateUrl: 'details.html',
			  controller: 'detailsCtrl'
			})
			.when('/editDetails', {
			  templateUrl: 'editDetails.html',
			  controller: 'editDetailsCtrl'
			})
			.when('/statistiques', {
			  templateUrl: 'stats.html',
			  controller: 'stats'
			})
			.when('/statistiquesG', {
			  templateUrl: 'statsG.html',
			  controller: 'statsG'
			})
			.when('/nouveau', {
			  templateUrl: 'editDetails.html',
			  controller: 'nouveauCtrl'
			})
			.when('/collaboratif', {
			  templateUrl: 'collaboratif.html',
			  controller: 'collaboratifCtrl'
			})
			.otherwise({redirectTo: '/accueil'});
		})
	
		
		
/* <!-- Des Controleurs différents sont présents  pour gérer chaque vues --> */
			
/* CONTROLEUR DE LA VUE LOGIN		 */	
		
		.controller('loginCtrl', function ($scope,ServiceData,$http,$rootScope){
		
			/* PHASE DE LOGIN */
			$scope.informationLogin = "En attente de votre Login";
			
			
			/* Remplissage du select avec les noms d utilisateurs */
			 $scope.getUtilisateurs = function() {
					$http.get("dbtruc21.php?action=get_utilisateurs").success(function(data)
					{  
						$scope.utilisateurs= data;
						$rootScope.utilisateurs= $scope.utilisateurs;
					});
			}
			$scope.getUtilisateurs(); // chargement des utilisateurs
			
			
			/* Initialisation des champs du Login */
			$scope.passwordTest = "";
			$scope.choixUser = "";
			$scope.removed = " Plus d'objets à afficher";
			
			
			/* Test puis validation du couple login-mot de passe */
			$scope.loginUtilisateur =  function(){
					
				 $http.post('dbtruc21.php?action=get_login', 
					{
						'idUtilisateur' : $scope.choixUser.id, 
						'password'     : $scope.passwordTest, 
					}
				).success(function (data, status, headers, config) {
					
					$scope.resultatLogin = data;
					
					if ($scope.resultatLogin=="true"){
						
						$scope.informationLogin = "Vous êtes loggé en tant que "+$scope.choixUser.nom;
						$scope.idUtilisateurCourant = $scope.choixUser.id;// fixe la variable $scope.idUtilisateurCourant
						window.idUtilisateurCourant = 	$scope.idUtilisateurCourant;// FIxe la variable globale window.idUtilisateurCourant 
						$scope.setModele($scope.idUtilisateurCourant);// Charge le modele pour l'utilisateur 
						$rootScope.name = { value:$scope.choixUser.nom};
							
						
					}
					
					else{
						$scope.informationLogin = "Login raté, recommencez SVP";	
					}
					
					
				})

				.error(function(data, status, headers, config){
					alert('pas de connexion vers le serveur');
				});
			};			
		
		/* FIN DU CONTROLEUR */
		})
		

		
		
/* <!-- CONTROLEUR DU MENU, DES FILTRES ET DE LA LISTE DES OBJETS--> */
		.controller('accueilCtrl', function ($scope,ServiceData,$http,$rootScope){
			
			$scope.checkPresenceId = function(){
				if (window.idUtilisateurCourant){
				  return false;
				}
				else{
					return true;
				}
			};
	
			$scope.rangePrixMin= 1 ;
			$scope.rangePrixMax= 100000 ;
			$scope.io = 1;
			$scope.rangemoyenneEvaluation =99;
			$scope.choixFiltre = 'marque';
			$scope.objets =[{}];
			
			
			/* Charger le modele*/
			$scope.setModele = function(){
				$scope.objets =ServiceData.getObjets();
				
			
			}
			/* <!-- FONCTION DECLENCHEEE SUR CLICK, QUI PERMET DAFFECTER lID OBJET COURANT AUX VUES HTML QUI DETAILLENT L ES cARACTERISTIQUES DE LOBJET, EN DAUTRES TERMES, CELA PERMET DE CHOISIR UN OBJET ET DE RECUPERER SON ID POUR LINJECTER DANS LES VUES HTML --> */
			$scope.setIdCourant = function(id){
				
				for(x=0;x<$scope.objets.length;x++){
					if($scope.objets[x].id==id){
						$scope.idCourant=x;/* <!-- Permet de choisir un objet et de lafficher dans la vue details. Algo Un peu compliqué car lalgo doit trier et continuer à fonctionner en cas de suppression dun objet--> */
					};
				};
			};	
			  
			$scope.idCourant=1;
			
			$scope.supprimerObjet = function(id){
				
				if (window.idUtilisateurCourant==0) { // on Peut supprimer un objet seulement qi lid courant est celui de l admin
					var idASupprimer = id;
					for(x=0;x<$scope.objets.length;x++){
						var z = $scope.objets[x].id;
						if(z==idASupprimer){
							$scope.objets.splice(x,1);
						}
					};
				}
				else{
					alert("vous n avez pas les droits pour supprimer un objet, demandez à l'admin ")
				};
			
			};
			
			$scope.compte = "." ;
			
			$scope.$watch('filtreMarque', function (filtreMarque) {
				var compte=0;
				for(x=0;x<$scope.objets.length;x++){
					if($scope.objets[x].marque==filtreMarque){
						compte += 1;        
					};	
				};
				$scope.compte = compte;
					if($scope.compte==0){
					$scope.compte = "" ;
				};
				
			}, true);
			
				/* 	<!-- FILTRE LE MODELE PAR PRIX --> */
				$scope.filtrePrixMin = function(objet) {
					return objet.prix > $scope.rangePrixMin ;
				};
				$scope.filtrePrixMax = function(objet) {
					return objet.prix < $scope.rangePrixMax ;
				};
				
				
				/* 	<!-- FILTRE LE MODELE PAR MOYENNE DEVALUATION --> */
			 	$scope.filtreMoyenneEval = function(objet) {
					return objet.moyenneEvaluation < $scope.rangemoyenneEvaluation;
				}; 
			
			
			$scope.getMoyenneEvaluationParIdUtilisateur = function(idCourant){
				
				/* Copie toutes les notes attribuées par les utilisateurs (leur id est idt) d'un objet dans la var u */
				
				var u = $scope.objets[idCourant].notes;
				
				
				var nbEvaluations = 0 ;
				var totalEvaluations = 0;
				var count = 0;
				/* 
				Compte le nombre dévaluation d un seul utilisateur dans le modèle*/
				for(s=0;s<u.length;s++){
					/* console.log(u[s].idt) */
					if(u[s].idt==idUtilisateurCourant){
						nbEvaluations +=1;
						totalEvaluations += eval(u[s].note);
					};
					
				};
				console.log("nbeval: "+nbEvaluations);
				console.log("totaleval: "+totalEvaluations); 
				if (nbEvaluations!==0){
				MoyenneEvaluation = totalEvaluations/nbEvaluations;
				$scope.objets[idCourant].moyenneEvaluation = MoyenneEvaluation.toFixed(2);
				return MoyenneEvaluation.toFixed(2);
				}
				
				else return "Non Evalué ";
				
			};
			
			
			
			
			
			/* FIN DU CONTROLEUR */
			})
		
		
/* CONTROLEUR DE LA VUE NOUVEL OBJET */
		.controller('nouveauCtrl', function ($scope, $routeParams,ServiceData){
				$scope.filtreIdt = function(element) {
				return element.idt == window.idUtilisateurCourant ;
			};
				
				<!-- Obtenir lid du nouvel lobjet : cet algoritme recherche lID max et y ajoute 1 pour le nouvel objet.-->
				function getIdObjet(){
					
					var arr = $scope.objets;
					
					
					<!-- Si la liste des objets est vide, alors lid de la commande est de 1 -->
					if ($scope.objets.length === 0 ){
						id=1;
						return id;
					}
					
					 <!-- Si la liste des objets est présente, alors lid de lobjet est de idMAX +1 -->
					if ($scope.objets.length != 0){
						var idnew= 0;
						var idmax=0;
						var z=0;
						for (z = 0;z < $scope.objets.length; z++){
							var idtotest=$scope.objets[z].id;
							console.log(idtotest);
							if (eval(idtotest)>eval(idmax)){
								var idmax = eval(idtotest);
								}
							};
						var idnew = eval(idmax)+1;
						console.log(idnew);
						return idnew;
						
					}
				};
			
			$scope.objets.push({id : getIdObjet(),	"inactif":false	,"nom":"Nouveau",		"marque":"Nouvelle",	"prix":1.10,	"photo":"nike1.jpg","notes" :[
						{"idEval":"0","idt":window.idUtilisateurCourant,"evaluation":"Look"	,					"note":1,"photo":"look.jpg"},
						{"idEval":"1","idt":window.idUtilisateurCourant,"evaluation":"Qualité de Fabrication",	"note":1,"photo":"fabrication.png"},
						{"idEval":"2","idt":window.idUtilisateurCourant,"evaluation":"Satisfaction Client"	,	"note":1,"photo":"satisfaction.png"},
						{"idEval":"3","idt":window.idUtilisateurCourant,"evaluation":"Ventes"	,				"note":1,"photo":"ventes.png"}
					]
					,"moyenneEvaluation":""
					});
				
			
			
			
			$scope.idCourant=$scope.objets.length-1; 
			
			$scope.newEval = {
			content: ''
			};
			$scope.nouvelleMarque={
			content: ''
			};
			
			$scope.listeimgEval = ServiceData.getImgEval();
			
			$scope.choixImgEval = "fabrication.png";
			
			$scope.ajouteMarque= function(){
				$scope.objets[$scope.idCourant].marque = $scope.nouvelleMarque.content;
			}
			
			$scope.sauverEdit = function(){
			};
			
			$scope.ajoutEval = function(idCourant,newEval){
				$scope.objets[idCourant].notes.push({"evaluation":$scope.newEval.content,"note":1,"photo":$scope.choixImgEval});
			};
			
			$scope.deleteEval = function(idCourant,index){
				$scope.objets[idCourant].notes.splice(index, 1);
			};
			
			$scope.setImgEval= function(nomImage){
				
				$scope.choixImgEval = nomImage;
			
			};
			
			$scope.getMoyenneEvaluation = function(idCourant){
				
				var totalEvaluations = 0;
				var nbEvaluations = $scope.objets[idCourant].notes.length;
				var toutesLesNotes = $scope.objets[idCourant].notes;
				
				for(f=0;f<toutesLesNotes.length;f++){
					totalEvaluations = totalEvaluations+eval(toutesLesNotes[f].note);
				};
				
				MoyenneEvaluation = totalEvaluations/nbEvaluations;
				$scope.objets[idCourant].moyenneEvaluation = MoyenneEvaluation.toFixed(2);
				return MoyenneEvaluation.toFixed(2);
			};
		
		<!-- FIN DU CONTROLEUR -->	
		})
		
		
		
		.controller('collaboratifCtrl', function ($rootScope,$scope, $routeParams,ServiceData){
			
		
		})
		
		
		
		
		
		.controller('detailsCtrl', function ($rootScope,$scope, $routeParams,ServiceData){
		<!-- FIN DU CONTROLEUR -->	
		})
		
		
/* CONTROLEUR DE LEDITION DUN OBJET */
		.controller('editDetailsCtrl', function ($scope, $routeParams,ServiceData){
			
			/* Ce filtre affiche les évaluations dun utilisateur, en fonction de son id window.idUtilisateurCourant , acquerit lors du login */
			$scope.filtreIdt = function(element) {
				return element.idt == window.idUtilisateurCourant ;
			};
				
			$scope.listeimgEval = ServiceData.getImgEval();
			
			$scope.choixImgEval = "fabrication.png";
			
			$scope.changes = 0;
			
			$scope.newEval = {
				content: ''
			};
			
			$scope.nouvelleMarque={
				content: ''
			};
			
			$scope.ajouteMarque= function(){
				$scope.objets[$scope.idCourant].marque = $scope.nouvelleMarque.content;
			};
			
			$scope.ajoutEval = function(idCourant,newEval){
				$scope.objets[idCourant].notes.push({"idEval":$scope.objets[idCourant].notes.length+1,"idt":window.idUtilisateurCourant,"evaluation":$scope.newEval.content,"note":1,"photo":$scope.choixImgEval});
			};
			
			$scope.deleteEval = function(idCourant,idEval){
				var j = $scope.objets[idCourant].notes;
				for(z=0;z<j.length;z++){
					if(j[z].idEval==idEval){
						j.splice(z, 1);  
					}
				};
			};
			
			$scope.setImgEval= function(nomImage){
				$scope.choixImgEval = nomImage;
			};
			
			$scope.getMoyenneEvaluation = function(idCourant){
				
				/* Copie toutes les notes attribuées par les utilisateurs (leur id est idt) d'un objet dans la var u */
				var u = $scope.objets[idCourant].notes;
				var nbEvaluations = 0 ;
				var totalEvaluations = 0;
				var count = 0;
				/* 
				Compte le nombre dévaluation dun seul utilisateur dans le modèle*/
				for(s=0;s<u.length;s++){
					/* console.log(u[s].idt) */
					if(u[s].idt==idUtilisateurCourant){
						nbEvaluations +=1;
						totalEvaluations += eval(u[s].note);
					};
					
				};
				/* console.log("nbeval: "+nbEvaluations);
				console.log("totaleval: "+totalEvaluations); */
				
				MoyenneEvaluation = totalEvaluations/nbEvaluations;
			
				return MoyenneEvaluation.toFixed(2);
			};
			
			
			
			<!-- FIN DU CONTROLEUR -->	
			
		})
		
		
		
		
		
/* 	<!-- CONTROLEUR DES STATISTIQUES --> */
		.controller('stats', function ($scope, $routeParams,ServiceData){
			
			$scope.getTotal = function(){
				return $scope.objets.length;
			}
			
			$scope.getCoutTotal = function(){
				var total = 0;
				for(x=0;x<$scope.objets.length;x++){
					total += $scope.objets[x].prix;
				}
				return total.toFixed(2);
			}
			
			$scope.getPrixMoyen = function(){
				var total = 0;
				for(x=0;x<$scope.objets.length;x++){
					total += $scope.objets[x].prix;
				}
				var total = total/$scope.objets.length;
				return total.toFixed(2);
			}
			
			$scope.getNbParMarque = function(){
				
				tableauMarques = [];
				/* <!-- Cette fonction compte les marques par objet dans lobjet $scope.objets --> */
				for(x=0;x<$scope.objets.length;x++){
					tableauMarques.push($scope.objets[x].marque);
				}
				console.log(tableauMarques);
			
				/* <!-- fonction qui compte le nombre de doublons par key dans un tableau JS, du coup cela compte le nombre dobjets par marques : --> */
				var counts={};
				tableauMarques.forEach(function(x){
					counts[x] = (counts[x]||0)+1;
				});
				console.log(counts);
				<!-- Retourne un objet js -->
				return counts;	
				
			};
			
			
			<!-- FIN DU CONTROLEUR -->	
		})
		
		
		
			/* 
<!-- CONTROLEUR DES STATISTIQUES GRAPHIQUES CLASSEMENT PAR EVALUATIONS--> */
		.controller('statsG', function ($scope, $routeParams,ServiceData){
			
		$scope.G1=function() {
				<!--  EXEMPLE DE CE QUI EST ATTENDU PAR LE GRAPHE : var response = [[["Afpa",8],["Cnam",1],["HEC",1],["Supelec",2],["Ecole Microsoft",1],["Cisco School",1]]];   -->
				$('#stats').empty();
				<!-- GENERATION DU GRAPHE 1 -->
				plot= 	$.jqplot('stats',[G1go()], {// La librairie Jqplot
						 async: false,
							title:'Votre classement par évaluations:',
							seriesDefaults:{
								renderer:$.jqplot.BarRenderer,pointLabels: { show: true   },
								rendererOptions: { varyBarColor: true }},	
							axes:{
								  xaxis: {
									renderer: $.jqplot.CategoryAxisRenderer,
									},
								  yaxis: {
									tickOptions:{
									   <!-- formatString: "%'.2f Euros" -->
									}
								 }
							},
						});
			};
		
		<!-- GET DES DONNEES DESTINEES AU GRAPHE 1 : CLASSEMENT PAR EVALUATIONS-->
		G1go = function(){

			t = [];
			for (x=0;x<$scope.objets.length;x++){
				var i = $scope.objets[x].nom;
				var p = eval($scope.objets[x].moyenneEvaluation);
				t.push([i,p]);
				
			}
			t = t.sort(function(a,b){return a[1]>b[1]});
			return t;
		}
		
		$scope.G2=function() {
				var response = [[["Afpa",8],["Cnam",1],["HEC",1],["Supelec",2],["Ecole Microsoft",1],["Cisco School",1]]];
				$('#stats2').empty();
				<!-- GENERATION DU GRAPHE 1 -->
				plot= 	$.jqplot('stats2',[G2go()], {// La librairie Jqplot
						 async: false,
							title:'Le Nombre de chaussures par marques:',
							seriesDefaults:{
								renderer:$.jqplot.BarRenderer,pointLabels: { show: true   },
								rendererOptions: { varyBarColor: true }},	
							axes:{
								  xaxis: {
									renderer: $.jqplot.CategoryAxisRenderer,
									},
								  yaxis: {
									tickOptions:{
									 
									}
								 }
							},
						});
			};
		
	
		<!-- GET DES DONNEES DESTINEES AU GRAPHE 2 -->
		G2go = function(){
				var tableauMarques = [];
			
				<!-- Cette fonction compte les marques par objet dans lobjet $scope.objets -->
				for(x=0;x<$scope.objets.length;x++){
					tableauMarques.push($scope.objets[x].marque);
		

				};
				
				
			
			
				<!-- fonction qui compte le nombre de doublons par key dans un tableau JS, du coup cela compte le nombre dobjets par marques et retourne un objet: -->
				var counts={};
				tableauMarques.forEach(function(x){
					
					counts[x] = (counts[x]||0)+1;
				});
				
				
			
				console.log(counts);
				r=[];
				
				<!-- Transmutation de lobjet Json counts en Tableau r: -->
				for (key in counts){
					console.log( key + ' => ' + counts[key]);
					var i = key;
					var p = counts[key];
					r.push([i,p])
				}
				console.log(r);
				r = r.sort(function(a,b){return a[1]>b[1]});
				return r;
				
				
		}
	<!-- FIN DU CONTROLEUR -->	
		})

		
	
	
	<!-- NE SERT PAS -->
	.directive('maDirective',function(){
		return{
			restrict:'E',
			template:"<p>Hello {{objets[0].nom}}</p>"
		}
		})
		
	
				
		

Le fichier Php qui gère l’identification:

<?php
//indique que le type de la réponse renvoyée au client sera du Texte
header("Content-Type: text/plain");
//anti Cache pour HTTP/1.1
header("Cache-Control: no-cache , private");
//anti Cache pour HTTP/1.0
header("Pragma: no-cache");
 error_reporting(0);



include('configtruc21.php'); 

/**  Switch Case pour récupérer l'action demandée par le controleur  Angular **/

switch($_GET['action'])  {
    
	case 'get_utilisateurs':
			get_utilisateurs();
			break;
	
	case 'get_login' :
            get_login();
            break;

}



/* function qui retrouve la liste des utilisateurs: */
	function get_utilisateurs() {
		$qry = mysql_query('SELECT * from utilisateurs');
		$data = array();
		while($rows = mysql_fetch_array($qry))
		{
        $data[] = array(
                    "id"      => $rows['idUtilisateur'],
                    "nom"     => $rows['nom'],
                    "password"=> $rows['password']
                    );
		}
		print_r(json_encode($data));
		return json_encode($data);  

	exit();
	}

/* function qui teste le login: */
	function get_login() {
	
	/* Reception des données login a tester en base de donnee */
	$data 		= json_decode(file_get_contents("php://input"));     
    $index 		= $data->idUtilisateur; 
	$passwordAtester = $data->password; 
	
	/* Lecture du password en base de données. */
		
		$qry = mysql_query("SELECT * from utilisateurs where idUtilisateur = ".$index);
		
		while($rows = mysql_fetch_array($qry))
		{
        $data= array(
                    "id"      => $rows['idUtilisateur'],
                    "nom"     => $rows['nom'],
                    "password"=> $rows['password']
                    );
		}
		
	
	/* 	Teste si le password écrit par l'utilisateur est egal au password dans la base de données. */
		if($passwordAtester==$data[password]){
			
			echo('true');
			return true;
			
		}
		
		else {
			
			echo('false');
			return false;
		}
		
		
	exit();

		  

	}





?>

La factory de Data (version non uglifiée):


// service qui gère les datas.
		myApp.factory('ServiceData', [function () {
			var factory = {};

			  factory.getObjets = function () {
				return listeObjets;
			  };
			
			  
			   factory.getImgEval = function () {
				return listeImgEvaluation;
			  }; 
			   
			 /* Ne marche pas je ne sais pas pourquoi 
			 
			 factory.getUtilisateurs =  function ($http) {
				
				return $http.get("dbtruc21.php?action=get_utilisateurs").success(function(data){  
						
					});
			  };  */
			// Liste des images d évaluation
			  var listeImgEvaluation = [
				{"photo":"look.jpg"},
				{"photo":"ventes.png"},
				{"photo":"satisfaction.png"},
				{"photo":"fabrication.png"},
				{"photo":"sorties.png"},
				{"photo":"euro.png"},
				{"photo":"client2.png"},
				{"photo":"calendrier.png"}
			  ];
				
				
				
				
				
				// Liste des objets 
			  var listeObjets =[
					{"id":"0",	"inactif":false	,"nom":"Air",		"marque":"Nike",	"prix":32.99,	"photo":"nike1.jpg","notes" :[
						{"idEval":"0","idt":"0","evaluation":"Look"	,					"note":15,"photo":"look.jpg"},
						{"idEval":"1","idt":"1","evaluation":"Qualité de Fabrication",	"note":15,"photo":"fabrication.png"},
						{"idEval":"2","idt":"2","evaluation":"Satisfaction Client"	,	"note":60,"photo":"satisfaction.png"},
						{"idEval":"3","idt":"1","evaluation":"Ventes"	,				"note":15,"photo":"ventes.png"}
					]
					,"moyenneEvaluation":""
					},
					{"id":"1",	"inactif":false	,"nom":"x20",		"marque":"Reebok",	"prix":54.25,	"photo":"reebok1.jpg","notes" :[
						{"idEval":"0","idt":"0","evaluation":"Look"	,					"note":30,"photo":"look.jpg"},
						{"idEval":"1","idt":"0","evaluation":"Qualité de Fabrication",	"note":30,"photo":"fabrication.png"},
						{"idEval":"2","idt":"1","evaluation":"Satisfaction Client"	,	"note":45,"photo":"satisfaction.png"},
						{"idEval":"3","idt":"2","evaluation":"Ventes"	,				"note":80,"photo":"ventes.png"}
					]
					,"moyenneEvaluation":""
					},
				];
				return factory;
			}
		]);
Publicités