{"id":806,"date":"2019-06-26T10:54:00","date_gmt":"2019-06-26T09:54:00","guid":{"rendered":"http:\/\/wollef.org\/?p=806"},"modified":"2019-06-26T10:54:00","modified_gmt":"2019-06-26T09:54:00","slug":"le-jeu-22-simuler-une-course","status":"publish","type":"post","link":"https:\/\/wollef.org\/blog\/le-jeu-22-simuler-une-course\/","title":{"rendered":"Le Jeu (2\/2) : Afficher le d\u00e9roulement de la comp\u00e9tition des voitures d&#8217;une course fictive"},"content":{"rendered":"<p>L&#8217;objectif du projet \u00e9tait de simuler une &#8216;course&#8217; pour des voitures su un m\u00eame trajet, mais roulant \u00e0 des dates diff\u00e9rentes.<\/p>\n<blockquote><p>Attaquons-nous \u00e0 la derni\u00e8re \u00e9tape : la course !<\/p><\/blockquote>\n<h3><a name=\"_Toc6859861\"><\/a>1.\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 La d\u00e9finition de la course<\/h3>\n<p>Si les voitures ne partent pas \u00e0 la m\u00eame heure, on doit pouvoir trouver comment comparer leur avanc\u00e9e dans la course par rapport \u00e0 leur date de d\u00e9part.<\/p>\n<p>Une course dans notre jeu sera d\u2019aller d\u2019une place \u00e0 l\u2019autre le plus vite possible.<\/p>\n<p>On va cr\u00e9er un objet Car Race qui m\u00e9morise\u00a0:<\/p>\n<ul>\n<li>le nom de la course<\/li>\n<li>le point de d\u00e9part<\/li>\n<li>le point d\u2019arriv\u00e9e<\/li>\n<\/ul>\n<p>Et pour chaque participant, un objet Racer qui va m\u00e9moriser<\/p>\n<ul>\n<li>la course<\/li>\n<li>la voiture<\/li>\n<li>la date\/heure de d\u00e9but<\/li>\n<li>la date\/heure de fin<\/li>\n<li>la position enregistr\u00e9e de d\u00e9but (car status)<\/li>\n<li>la position enregistr\u00e9e de fin (car status)<\/li>\n<\/ul>\n<p>Voici la configuration des deux objets dans l\u2019Object Manager\u00a0:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-807\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu2-300x168.png\" alt=\"\" width=\"486\" height=\"272\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-808\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu3-300x182.png\" alt=\"\" width=\"475\" height=\"288\" \/><\/p>\n<h3><a name=\"_Toc6859862\"><\/a>2.\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Afficher la comp\u00e9tition<\/h3>\n<p>Maintenant nous pouvons\u00a0comparer le parcours complet des participants m\u00eame s\u2019ils partent \u00e0 des dates diff\u00e9rentes. On va afficher avec un composant mis sur l\u2019objet Course la position des participants \u00e0 x minutes, x \u00e9tant saisi par l\u2019utilisateur (10\u2019 apr\u00e8s le d\u00e9part fictif, 20\u2019 apr\u00e8s le d\u00e9part, etc.).<\/p>\n<h4>a)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Le service cot\u00e9 serveur<\/h4>\n<p>On a besoin d\u2019un code Apex qui va calculer la position des participants apr\u00e8s x minutes de course.<\/p>\n<pre><code>public class <strong>CarRaceController<\/strong> {\n\n@AuraEnabled(Cacheable=true)\npublic static Car_Status__c[] <strong>getRacePositions<\/strong>(ID raceID, Integer chronoInMinutes) {\n\/\/ on retrouve la liste des participants\nList&lt;Racer__c&gt; lRacers = [\nSELECT\nId,\nCar_Race__c,\nCar__c,\nStart_Date__c\nFROM Racer__c\nwhere Car_Race__c =:raceID\n];\n\n\/\/ le tableau o\u00f9 on stocke les position qu'on a trouv\u00e9es.\nList&lt;Car_Status__c&gt; knowPositions = new List&lt;Car_Status__c&gt;();\u00a0\n\n\/\/ pour chaque participant, on cherche sa derni\u00e8re position \u00e0 cette minute de la course\nfor (Racer__c racer : lRacers) {\n\/\/ calcule l'heure pour se participant\nDateTime positionTime =\u00a0 racer.Start_Date__c;\npositionTime = positionTime.addMinutes(chronoInMinutes);\n\/\/ recherche la derni\u00e8re position connue pour cette heure\nList&lt;Car_Status__c&gt; carPosition = [\nSELECT\nId,\nLocation__Latitude__s,\nLocation__Longitude__s,\nRequested_On__c,\nCar__c,\nCar__r.Name\nFROM Car_Status__c\nWHERE Car__c = :racer.Car__c\nAND Requested_On__c &gt;= :racer.Start_Date__c\nAND Requested_On__c &lt;= :positionTime\nORDER BY Requested_On__c DESC\nLIMIT 1\n];\n\n\/\/ si on a trouv\u00e9 une position, on la met de cot\u00e9\nif (carPosition.size()&gt;0) {\nknowPositions.add(carPosition[0]);\n}\n}\n\n\/\/ on renvoie les positions qu'on a trouv\u00e9e\nreturn knowPositions;\n}\n\n}<\/code><\/pre>\n<h4>b)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Le composant<\/h4>\n<p>Comme d\u2019habitude on dit o\u00f9 il doit s\u2019afficher : sur les pages des objets Courses (Car Race).<\/p>\n<pre><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n\n&lt;LightningComponentBundle xmlns=\"http:\/\/soap.sforce.com\/2006\/04\/metadata\" fqn=\"RaceCarPositionsMap\"&gt;\n\n&lt;apiVersion&gt;45.0&lt;\/apiVersion&gt;\n&lt;isExposed&gt;true&lt;\/isExposed&gt;\n\n&lt;targets&gt;\n&lt;target&gt;lightning__RecordPage&lt;\/target&gt;\n&lt;\/targets&gt;\n\n&lt;targetConfigs&gt;\n&lt;targetConfig targets=\"lightning__RecordPage\"&gt;\n&lt;objects&gt;\n&lt;object&gt;Car_Race__c&lt;\/object&gt;\n&lt;\/objects&gt;\n\n&lt;\/targetConfig&gt;\n&lt;\/targetConfigs&gt;\n\n&lt;\/LightningComponentBundle&gt;\n<\/code><\/pre>\n<p>La partie Html est un peu plus compliqu\u00e9e\u00a0: elle a un champ <strong>inputMinutes<\/strong> pour saisir le nombre de minutes que l\u2019on veut. Quand ce champ est modifi\u00e9 avec la valeur <strong>x<\/strong>, le code javascript <strong>minutesChangeHandler<\/strong> est appel\u00e9 pour demander \u00e0 Salesforce les positions \u00e0 <strong>x<\/strong> minutes. La carte fonctionne sur le m\u00eame mode que celle qu\u2019on avait faite dans la partie pr\u00e9c\u00e9dente\u00a0: elle affiche les marqueurs <strong>mapMarkers<\/strong>.<\/p>\n<pre><code>&lt;template&gt;\n&lt;article class=\"slds-card\"&gt;\n\n&lt;div class=\"slds-m-around_medium\"&gt;\n&lt;p&gt;Race Id at {recordId}&lt;\/p&gt;\n&lt;p&gt;Race Info of {raceName}&lt;\/p&gt;\n&lt;p&gt;Position at {currentRaceMinutes}&lt;\/p&gt;\n&lt;\/div&gt;\n\n&lt;lightning-input\nname=\"inputMinutes\" label=\"Minutes\"\ntype=\"number\"\nvalue={currentRaceMinutes}\nstep=\"5\" max=\"1000\" min=\"0\"\nonchange={minutesChangeHandler}&gt;\n&lt;\/lightning-input&gt;\n\n&lt;div class=\"slds-m-around_medium\"&gt;\n&lt;lightning-map\nmap-markers={mapMarkers}\nzoom-level=\"11\"\nmarkers-title=\"Positions\"\n&gt;\n&lt;\/lightning-map&gt;\n&lt;\/div&gt;\n&lt;\/article&gt;\n&lt;\/template&gt;\n<\/code><\/pre>\n<p>Le code Java script est fait sur le m\u00eame mod\u00e8le que le pr\u00e9c\u00e9dent, et va charger les positions des voitures \u00e0 x minutes en appelant le code Apex <strong>getRacePositions <\/strong>avec l\u2019id de la course et le nombre de minutes apr\u00e8s le d\u00e9part.<\/p>\n<p>On fait le mapping deux fois\u00a0:<\/p>\n<ul>\n<li>une fois quand la page se charge<\/li>\n<\/ul>\n<pre><code>@wire(getRacePositions, { raceID :\u00a0 '$recordId',\u00a0 chronoInMinutes : '$currentRaceMinutes'\u00a0 })<\/code><\/pre>\n<ul>\n<li>et une fois quand les minutes sont chang\u00e9es.<\/li>\n<\/ul>\n<pre><code>getRacePositions( { raceID :\u00a0 this.recordId,\u00a0 chronoInMinutes : this.currentRaceMinutes})<\/code><\/pre>\n<p>Le code pour cr\u00e9er les marqueurs ressemble \u00e0 celui du composant pr\u00e9c\u00e9dent<\/p>\n<pre><code>this.mapMarkers = result.data.map(carPosition =&gt; {\nconst CarName = carPosition.Car__r.Name;\nconst Latitude = carPosition.Location__Latitude__s;\nconst Longitude = carPosition.Location__Longitude__s;\n\nreturn {\nlocation: { Latitude, Longitude },\ntitle: CarName,\ndescription: 'Coords: ${Latitude}, ${Longitude}',\nicon: 'utility:animal_and_nature'\n};\n\n});<\/code><\/pre>\n<p>Voici le code complet javascript\u00a0:<\/p>\n<pre><code>import { LightningElement, api, wire, track } from 'lwc';\nimport { getRecord , getFieldValue\u00a0 } from 'lightning\/uiRecordApi';\nimport getRacePositions from '@salesforce\/apex\/CarRaceController.getRacePositions';\nimport RACE_NAME from '@salesforce\/schema\/Car_Race__c.Name';\nconst raceFields = [\nRACE_NAME\n];\n\nexport default class RaceCarPositionsMap extends LightningElement {\n@api recordId;\n@wire(getRecord, { recordId: '$recordId', fields: raceFields })\nraceInformation;\n\n\/\/ les minutes o\u00f9 on se trouve (30 pour le test)\n@track currentRaceMinutes = 0;\n\n\/\/ les marqueurs pour la carte\n@track mapMarkers = [];\n\n\u00a0\/\/ les positions\n@track carsPositions;\n\nget raceName() {\nreturn getFieldValue(this.raceInformation.data, RACE_NAME);\n}\n\n@wire(getRacePositions, { raceID :\u00a0 '$recordId',\u00a0 chronoInMinutes : '$currentRaceMinutes'\u00a0 })\nloadCar(result) {\nif (result.data) {\nthis.carsPositions = result.data;\nthis.mapMarkers = result.data.map(carPosition =&gt; {\nconst CarName = carPosition.Car__r.Name;\nconst Latitude = carPosition.Location__Latitude__s;\nconst Longitude = carPosition.Location__Longitude__s;\nreturn {\nlocation: { Latitude, Longitude },\ntitle: CarName,\ndescription: 'Coords: ${Latitude}, ${Longitude}',\nicon: 'utility:animal_and_nature'\n};\n});\n}\n}\n\nminutesChangeHandler(event) {\nthis.currentRaceMinutes = event.target.value;\nthis.refreshMapForNewMinutes();\n}\n\nrefreshMapForNewMinutes() {\ngetRacePositions( { raceID :\u00a0 this.recordId,\u00a0 chronoInMinutes : this.currentRaceMinutes})\n.then(result =&gt; {\nif (result.data) {\nthis.carsPositions = result.data;\nthis.mapMarkers = result.data.map(carPosition =&gt; {\nconst CarName = carPosition.Car__r.Name;\nconst Latitude = carPosition.Location__Latitude__s;\nconst Longitude = carPosition.Location__Longitude__s;\nreturn {\nlocation: { Latitude, Longitude },\ntitle: CarName,\ndescription: 'Coords: ${Latitude}, ${Longitude}',\nicon: 'utility:animal_and_nature'\n};\n});\n}\n})\n.catch(error =&gt; {\nalert('Error '+JSON.stringify(error));\nthis.checkIfTimeEntryCanBeCreated();\n});\n}\n}\n<\/code><\/pre>\n<p>&nbsp;<\/p>\n<h4>c)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Le r\u00e9sultat<\/h4>\n<p>Une fois le composant install\u00e9 dans la page des courses, voici ce que cela donne pour diff\u00e9rentes minutes de course (85\u2019, 90\u2019).<\/p>\n<p><em>Maintenant si on fait bouger les minutes, on voit les marqueurs des voitures se d\u00e9placer\u00a0! C\u2019\u00e9tait l\u2019objectif de cette partie \u2018course virtuelle\u2019\u00a0!<\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-809\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu4-300x208.png\" alt=\"\" width=\"488\" height=\"338\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-810\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu5-300x207.png\" alt=\"\" width=\"472\" height=\"326\" \/><\/p>\n<h2><a name=\"_Toc6859863\"><\/a>B.\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Cr\u00e9ation de l\u2019application Car Races<\/h2>\n<h3><a name=\"_Toc6859864\"><\/a>1.\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Configuration<\/h3>\n<p>Pour acc\u00e9der facilement \u00e0 tous les \u00e9crans et objets de notre jeu, on peut cr\u00e9er \u00ab\u00a0une application\u00a0\u00bb.<\/p>\n<p>Cela permet aux utilisateurs d\u2019arriver plus facilement aux \u00e9crans concernant les voitures, la connexion, les courses.<\/p>\n<p>Dans le menu Setup on choisit\u00a0: App \/ App Manager puis on clique \u00ab\u00a0New Lightning App\u00a0\u00bb. On lui donne un nom et on choisit ce qu\u2019elle doit afficher.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-811\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu6-300x236.png\" alt=\"\" width=\"475\" height=\"374\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-812\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu7-300x236.png\" alt=\"\" width=\"472\" height=\"371\" \/><\/p>\n<h3><a name=\"_Toc6859865\"><\/a>2.\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Utilisation<\/h3>\n<p>Pour aller dans la nouvelle application, on clique en haut \u00e0 gauche sur l\u2019ic\u00f4ne avec les neuf petits carr\u00e9s, on choisit \u00ab\u00a0Car Races\u00a0\u00bb \u2026<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-813\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu8-300x168.png\" alt=\"\" width=\"480\" height=\"269\" \/><\/p>\n<p>\u2026 et quand on est dans l\u2019application, on ne voit dans la barre d\u2019onglets que les objets configur\u00e9s. C\u2019est plus facile pour s\u2019y retrouver.<\/p>\n<p>Par exemple pour l\u2019acc\u00e8s direct \u00e0 la liste des voitures<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-814\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu9-300x169.png\" alt=\"\" width=\"476\" height=\"268\" \/><\/p>\n<p>Ou voir la carte des voitures avec leurs derni\u00e8res positions enregistr\u00e9es sur la home page<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-815\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu10-300x181.png\" alt=\"\" width=\"472\" height=\"285\" \/><\/p>\n<blockquote><p><strong>Ou encore \u2026 \u2026 voir une course\u00a0!<\/strong><\/p><\/blockquote>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-816\" src=\"http:\/\/wollef.org\/wp-content\/uploads\/2019\/10\/jeu12-300x160.png\" alt=\"\" width=\"478\" height=\"255\" \/><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>L&#8217;objectif du projet \u00e9tait de simuler une &#8216;course&#8217; pour des voitures su un m\u00eame trajet, mais roulant \u00e0 des dates diff\u00e9rentes. Attaquons-nous \u00e0 la derni\u00e8re \u00e9tape : la course ! 1.\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 La d\u00e9finition de la course Si les voitures ne partent pas \u00e0 la m\u00eame heure, on doit pouvoir trouver comment comparer leur avanc\u00e9e dans <a class=\"read-more\" href=\"https:\/\/wollef.org\/blog\/le-jeu-22-simuler-une-course\/\">Continue Reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30,33,46,48,1,18,20],"tags":[],"class_list":["post-806","post","type-post","status-publish","format-standard","hentry","category-cars-and-clouds","category-apex","category-javascript","category-lightning-web-component","category-non-classe","category-salesforce","category-soql"],"_links":{"self":[{"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/posts\/806","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/comments?post=806"}],"version-history":[{"count":0,"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/posts\/806\/revisions"}],"wp:attachment":[{"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/media?parent=806"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/categories?post=806"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wollef.org\/blog\/wp-json\/wp\/v2\/tags?post=806"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}