Le Jeu (2/2) : Afficher le déroulement de la compétition des voitures d’une course fictive

L’objectif du projet était de simuler une ‘course’ pour des voitures su un même trajet, mais roulant à des dates différentes.

Attaquons-nous à la dernière étape : la course !

1.         La définition de la course

Si les voitures ne partent pas à la même heure, on doit pouvoir trouver comment comparer leur avancée dans la course par rapport à leur date de départ.

Une course dans notre jeu sera d’aller d’une place à l’autre le plus vite possible.

On va créer un objet Car Race qui mémorise :

  • le nom de la course
  • le point de départ
  • le point d’arrivée

Et pour chaque participant, un objet Racer qui va mémoriser

  • la course
  • la voiture
  • la date/heure de début
  • la date/heure de fin
  • la position enregistrée de début (car status)
  • la position enregistrée de fin (car status)

Voici la configuration des deux objets dans l’Object Manager :

2.         Afficher la compétition

Maintenant nous pouvons comparer le parcours complet des participants même s’ils partent à des dates différentes. On va afficher avec un composant mis sur l’objet Course la position des participants à x minutes, x étant saisi par l’utilisateur (10’ après le départ fictif, 20’ après le départ, etc.).

a)              Le service coté serveur

On a besoin d’un code Apex qui va calculer la position des participants après x minutes de course.

public class CarRaceController {

@AuraEnabled(Cacheable=true)
public static Car_Status__c[] getRacePositions(ID raceID, Integer chronoInMinutes) {
// on retrouve la liste des participants
List<Racer__c> lRacers = [
SELECT
Id,
Car_Race__c,
Car__c,
Start_Date__c
FROM Racer__c
where Car_Race__c =:raceID
];

// le tableau où on stocke les position qu'on a trouvées.
List<Car_Status__c> knowPositions = new List<Car_Status__c>(); 

// pour chaque participant, on cherche sa dernière position à cette minute de la course
for (Racer__c racer : lRacers) {
// calcule l'heure pour se participant
DateTime positionTime =  racer.Start_Date__c;
positionTime = positionTime.addMinutes(chronoInMinutes);
// recherche la dernière position connue pour cette heure
List<Car_Status__c> carPosition = [
SELECT
Id,
Location__Latitude__s,
Location__Longitude__s,
Requested_On__c,
Car__c,
Car__r.Name
FROM Car_Status__c
WHERE Car__c = :racer.Car__c
AND Requested_On__c >= :racer.Start_Date__c
AND Requested_On__c <= :positionTime
ORDER BY Requested_On__c DESC
LIMIT 1
];

// si on a trouvé une position, on la met de coté
if (carPosition.size()>0) {
knowPositions.add(carPosition[0]);
}
}

// on renvoie les positions qu'on a trouvée
return knowPositions;
}

}

b)              Le composant

Comme d’habitude on dit où il doit s’afficher : sur les pages des objets Courses (Car Race).

<?xml version="1.0" encoding="UTF-8"?>

<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="RaceCarPositionsMap">

<apiVersion>45.0</apiVersion>
<isExposed>true</isExposed>

<targets>
<target>lightning__RecordPage</target>
</targets>

<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<objects>
<object>Car_Race__c</object>
</objects>

</targetConfig>
</targetConfigs>

</LightningComponentBundle>

La partie Html est un peu plus compliquée : elle a un champ inputMinutes pour saisir le nombre de minutes que l’on veut. Quand ce champ est modifié avec la valeur x, le code javascript minutesChangeHandler est appelé pour demander à Salesforce les positions à x minutes. La carte fonctionne sur le même mode que celle qu’on avait faite dans la partie précédente : elle affiche les marqueurs mapMarkers.

<template>
<article class="slds-card">

<div class="slds-m-around_medium">
<p>Race Id at {recordId}</p>
<p>Race Info of {raceName}</p>
<p>Position at {currentRaceMinutes}</p>
</div>

<lightning-input
name="inputMinutes" label="Minutes"
type="number"
value={currentRaceMinutes}
step="5" max="1000" min="0"
onchange={minutesChangeHandler}>
</lightning-input>

<div class="slds-m-around_medium">
<lightning-map
map-markers={mapMarkers}
zoom-level="11"
markers-title="Positions"
>
</lightning-map>
</div>
</article>
</template>

Le code Java script est fait sur le même modèle que le précédent, et va charger les positions des voitures à x minutes en appelant le code Apex getRacePositions avec l’id de la course et le nombre de minutes après le départ.

On fait le mapping deux fois :

  • une fois quand la page se charge
@wire(getRacePositions, { raceID :  '$recordId',  chronoInMinutes : '$currentRaceMinutes'  })
  • et une fois quand les minutes sont changées.
getRacePositions( { raceID :  this.recordId,  chronoInMinutes : this.currentRaceMinutes})

Le code pour créer les marqueurs ressemble à celui du composant précédent

this.mapMarkers = result.data.map(carPosition => {
const CarName = carPosition.Car__r.Name;
const Latitude = carPosition.Location__Latitude__s;
const Longitude = carPosition.Location__Longitude__s;

return {
location: { Latitude, Longitude },
title: CarName,
description: 'Coords: ${Latitude}, ${Longitude}',
icon: 'utility:animal_and_nature'
};

});

Voici le code complet javascript :

import { LightningElement, api, wire, track } from 'lwc';
import { getRecord , getFieldValue  } from 'lightning/uiRecordApi';
import getRacePositions from '@salesforce/apex/CarRaceController.getRacePositions';
import RACE_NAME from '@salesforce/schema/Car_Race__c.Name';
const raceFields = [
RACE_NAME
];

export default class RaceCarPositionsMap extends LightningElement {
@api recordId;
@wire(getRecord, { recordId: '$recordId', fields: raceFields })
raceInformation;

// les minutes où on se trouve (30 pour le test)
@track currentRaceMinutes = 0;

// les marqueurs pour la carte
@track mapMarkers = [];

 // les positions
@track carsPositions;

get raceName() {
return getFieldValue(this.raceInformation.data, RACE_NAME);
}

@wire(getRacePositions, { raceID :  '$recordId',  chronoInMinutes : '$currentRaceMinutes'  })
loadCar(result) {
if (result.data) {
this.carsPositions = result.data;
this.mapMarkers = result.data.map(carPosition => {
const CarName = carPosition.Car__r.Name;
const Latitude = carPosition.Location__Latitude__s;
const Longitude = carPosition.Location__Longitude__s;
return {
location: { Latitude, Longitude },
title: CarName,
description: 'Coords: ${Latitude}, ${Longitude}',
icon: 'utility:animal_and_nature'
};
});
}
}

minutesChangeHandler(event) {
this.currentRaceMinutes = event.target.value;
this.refreshMapForNewMinutes();
}

refreshMapForNewMinutes() {
getRacePositions( { raceID :  this.recordId,  chronoInMinutes : this.currentRaceMinutes})
.then(result => {
if (result.data) {
this.carsPositions = result.data;
this.mapMarkers = result.data.map(carPosition => {
const CarName = carPosition.Car__r.Name;
const Latitude = carPosition.Location__Latitude__s;
const Longitude = carPosition.Location__Longitude__s;
return {
location: { Latitude, Longitude },
title: CarName,
description: 'Coords: ${Latitude}, ${Longitude}',
icon: 'utility:animal_and_nature'
};
});
}
})
.catch(error => {
alert('Error '+JSON.stringify(error));
this.checkIfTimeEntryCanBeCreated();
});
}
}

 

c)              Le résultat

Une fois le composant installé dans la page des courses, voici ce que cela donne pour différentes minutes de course (85’, 90’).

Maintenant si on fait bouger les minutes, on voit les marqueurs des voitures se déplacer ! C’était l’objectif de cette partie ‘course virtuelle’ !

B.        Création de l’application Car Races

1.         Configuration

Pour accéder facilement à tous les écrans et objets de notre jeu, on peut créer « une application ».

Cela permet aux utilisateurs d’arriver plus facilement aux écrans concernant les voitures, la connexion, les courses.

Dans le menu Setup on choisit : App / App Manager puis on clique « New Lightning App ». On lui donne un nom et on choisit ce qu’elle doit afficher.

2.         Utilisation

Pour aller dans la nouvelle application, on clique en haut à gauche sur l’icône avec les neuf petits carrés, on choisit « Car Races » …

… et quand on est dans l’application, on ne voit dans la barre d’onglets que les objets configurés. C’est plus facile pour s’y retrouver.

Par exemple pour l’accès direct à la liste des voitures

Ou voir la carte des voitures avec leurs dernières positions enregistrées sur la home page

Ou encore … … voir une course !