Skip to content

Messages et Replicants : communiquer dans NodeCG

Pour faire le lien entre les différentes parties d’un bundle et permettre la transmission d’informations, NodeCG propose principalement deux mécanismes : les Replicants et les Messages.

Ils permettent tous les deux de faire communiquer les différents contextes (les éléments du dashboard, des Graphic, de l’extension) d’un bundle, mais ils ne répondent pas au même besoin.

  • Les Replicants : partager un état ou une valeur.
  • Les Messages: transmettre un événement ou déclencher une action.

Une façon simple de les retenir :

Replicant → “Voici la valeur actuelle.”

Message → “Il vient de se passer quelque chose.” / “Fais ceci.”

Les deux permettent de faire communiquer les différentes parties d’un bundle, mais ils ne répondent pas au même besoin.

Un Replicant permet de partager une donnée entre les différents contextes d’un bundle NodeCG, avec deux principaux avantages.

  1. Une source de vérité

On peut le considérer comme une source de vérité pour une donnée partagée : plusieurs parties de l’application travaillent autour de la même valeur.

Par exemple, si le Dashboard modifie le score de l’équipe 1, les autres contextes qui utilisent ce Replicant sont automatiquement informés de cette modification.

Les Replicants sont particulièrement adaptés aux données qui représentent un état actuel :

  • le score actuel
  • le nom des équipes
  • la visibilité du scoreboard
  • le joueur actuellement affiché
  1. La persistance des données

Un autre intérêt majeur est la persistance des valeurs.

Par défaut, un Replicant est persistant : sa valeur peut être conservée lorsque l’on recharge une page et même lorsque NodeCG est redémarré.

Imaginons que votre Dashboard affiche un score de 3 - 1. Si vous rechargez la page, le Replicant permet au Dashboard de retrouver l’état actuel du score plutôt que de repartir de zéro.

Voici un exemple de Replicant utilisé pour gérer le scoreboard :

const scoreboardRep = nodecg.Replicant("scoreboardRep", {
defaultValue: {
team1: {
name: "Team 1",
score: 0,
},
team2: {
name: "Team 2",
score: 0,
},
isVisible: true,
},
persistent: true,
});

Un Replicant a un nom (libre à vous de choisir) et peut contenir différents types de données : une chaîne de caractères, un nombre, un booléen, un tableau ou encore un objet plus complexe.

Ici, defaultValue définit l’état initial du scoreboard, les valeurs par défaut qui s’afficheront si rien n’est indiqué :

Le contenu de defaultValue est entièrement libre : c’est à vous de définir la structure de données dont votre application a besoin.

Dans notre exemple, j’ai choisi de représenter le scoreboard avec :

  • deux équipes, chacune avec un nom et un score
  • une propriété isVisible permettant de savoir si le scoreboard doit être affiché

Mais team1, team2, name, score et isVisible ne sont pas des propriétés particulières de NodeCG. Ce sont simplement les propriétés que j’ai choisies pour structurer les données de mon scoreboard.

J’aurais tout aussi bien pu utiliser une structure différente :

const scoreRep = nodecg.Replicant("scoreRep", {
defaultValue: {
home: {
label: "Team 1",
points: 0,
},
away: {
label: "Team 2",
points: 0,
},
isDisplayed: true,
}
persistent: true,
});

NodeCG ne se préoccupe pas de savoir comment vous organisez ces données : il se charge de partager la valeur du Replicant entre les différents contextes. La structure et le contenu de cette valeur dépendent de votre application.

La propriété persistent, en revanche, est une option fournie par NodeCG. Elle indique si la valeur du Replicant doit être conservée.

Elle est activée par défaut, il est donc rarement nécessaire de la préciser explicitement. Dans certains cas, notamment pour des données temporaires qui ne doivent pas être conservées après un redémarrage, on peut utiliser persistent: false.

Dans la pratique, l’utilisation d’un Replicant repose généralement sur trois étapes :

  • déclarer le Replicant
  • modifier sa valeur
  • réagir aux changements

On peut résumer cela ainsi :

Je déclare une donnée partagée → je modifie sa valeur → les autres contextes réagissent au changement.

Il faut déclarer le Replicant dans chaque contexte (chaque élement de Dashboard, Graphics ou Extension) qui doit l’utiliser.

Dans le bundle d’initiation, par exemple, scoreboardRep est déclaré à la fois dans le Dashboard et dans le Graphic.

C’est d’ailleurs une erreur classique lorsqu’on débute : déclarer le Replicant une seule fois en pensant que cela suffit. Au contraire, chaque contexte doit récupérer le Replicant qu’il souhaite utiliser.

Une fois le Replicant déclaré, on peut modifier sa valeur.

Dans la pratique, c’est souvent la partie Dashboard ou Extension qui modifie les Replicants, tandis que les Graphics se contentent principalement de les lire pour mettre à jour leur affichage.

Ce n’est cependant pas une règle imposée par NodeCG : n’importe quel contexte peut modifier un Replicant auquel il a accès.

Dans notre bundle d’initiation, il est possible de modifier les scores des équipes avec les boutons + et -. Ainsi, lorsque l’on clique sur le bouton + de l’équipe 1, on incrémente simplement la valeur de “1” dans le Replicant

document.getElementById("t1Plus").addEventListener("click", () => {
scoreboardRep.value.team1.score++; // On incrémente la valeur de 1 dans le Replicant
});

La valeur du score est directement modifiée dans le Replicant et cette modification est ensuite répliquée dans les autres contextes qui utilisent ce même Replicant.

Un contexte peut écouter les modifications d’un Replicant grâce à l’événement “change” :

scoreboardRep.on("change", (value) => {
// Mettre à jour l'interface ou le Graphic
});

Ici, chaque fois que la valeur du Replicant change, la fonction est appelée avec la nouvelle valeur.

Dans un Dashboard, on peut par exemple s’en servir pour mettre à jour les champs d’un formulaire, dans un Graphic, on pourra mettre à jour l’affichage à l’écran :

scoreboardRep.on("change", (value) => {
team1Score.textContent = value.team1.score;
team2Score.textContent = value.team2.score;
});

Le Graphic n’a donc pas besoin de savoir pourquoi le score a changé. Il lui suffit de réagir au nouvel état du Replicant.

Pourquoi ne pas utiliser un Replicant pour tout ?

Section titled “Pourquoi ne pas utiliser un Replicant pour tout ?”

Jusqu’ici, nous avons surtout parlé de données qui représentent un état : le score actuel, le nom des équipes ou encore la visibilité du scoreboard.

Cependant, toutes les informations qui circulent dans une application ne représentent pas forcément un état. Parfois, on veut simplement prévenir un autre contexte que quelque chose vient de se produire.

Par exemple :

“L’équipe 1 vient de marquer, joue l’animation !”

Dans ce cas, il n’est pas forcément pertinent de créer un Replicant pour l’animation et de modifier sa valeur, car nous ne cherchons pas à stocker un état : nous voulons simplement déclencher une action à un instant précis.

C’est là que les Messages entrent en jeu.

Un Message peut être vu comme un signal ponctuel envoyé d'un contexte à un autre. Un contexte envoie le message, et un autre contexte peut l’écouter et réagir.

Dans NodeCG, cela se fait principalement avec deux méthodes :

  • nodecg.sendMessage() pour envoyer un message
  • nodecg.listenFor() pour écouter un message

Par exemple, imaginons que le Dashboard possède un bouton permettant de lancer une animation dans le Graphic.

Le Dashboard peut envoyer le message :

nodecg.sendMessage("playAnimation");

Le Graphic le récupère :

nodecg.listenFor("playAnimation", () => {
// Message reçu, je déclenche l'animation
});

Lorsque le bouton est utilisé, le Dashboard envoie le message et le Graphic réagit immédiatement.

Il n’y a ici aucune donnée à conserver. Une fois le message reçu et traité, l’événement est terminé.

Un Message peut aussi transmettre des données

Section titled “Un Message peut aussi transmettre des données”

Même si un Message sert principalement à signaler un événement ou à déclencher une action, il peut également transporter des données.

Par exemple, plutôt que de simplement dire au Graphic « joue l’animation », le Dashboard peut préciser quelle équipe vient de marquer :

nodecg.sendMessage("goalScored", {
team: 1,
});
nodecg.listenFor("goalScored", (data) => {
if (data.team === 1) {
// Jouer l'animation de l'équipe 1
}
});

Le Message transporte donc ici deux informations :

  • l’événement : goalScored
  • les données associées à cet événement team: 1

Cela ne transforme pas pour autant le Message en Replicant. La différence tient surtout à ce que l’on cherche à représenter.

Dans cet exemple, team: 1 indique ce qui vient de se produire. Cette information accompagne l’événement et n’a pas vocation à représenter un état permanent.

Si, au contraire, on souhaite pouvoir demander à tout moment quelle équipe a le dernier score, cette information devrait plutôt être stockée dans un Replicant.

On peut donc retenir :

Un Replicant partage un état, éventuellement complexe.

Un Message signale un événement, éventuellement accompagné de données.

J’insiste une dernière fois là-dessus car ce n’est pas toujours évident de savoir lequel utiliser.

Les deux permettent de faire communiquer les différents contextes de NodeCG, mais ils ne représentent pas la même chose.

Une façon simple de faire son choix est de se poser une question :

Est-ce que je veux partager une donnée qui représente un état, ou est-ce que je veux déclencher quelque chose ?

Votre élément a besoin de connaître la valeur ou l’état actuel d’une donnée.

Par exemple :

  • quel est le score actuel ?
  • quel est le nom de l’équipe ?
  • le scoreboard est-il visible ?
  • quel joueur est actuellement affiché ?

Dans ces situations, l’information représente un état qui peut être consulté ou modifié au fil du temps.

Vous voulez signaler qu’un événement vient de se produire ou demander à un autre contexte d’effectuer une action ponctuelle.

Par exemple :

  • lancer une animation
  • jouer un son
  • afficher une notification
  • déclencher une transition
  • demander à un autre contexte d’effectuer une action ponctuelle

Dans ces situations, on ne cherche pas à conserver l’événement. On veut simplement prévenir un autre contexte que quelque chose vient de se produire ou lui demander de faire quelque chose.

Dans notre scoreboard :

  • le score 3 - 1 est un état : il a donc naturellement sa place dans un Replicant.
  • si l’on veut déclencher une animation lorsque l’équipe 1 marque, c’est un événement, donc un Message