Alors voilà, nous y sommes : Mon premier article ! Soyez indulgent hein...

Pour ce premier article abordons un problème que j'ai rencontré il y a quelques temps et qu'un de mes collègues a également rencontré récemment :

Utiliser un query param pour stocker l'ID d'un model

Simple me direz-vous, oui sauf que ça peut poser problème si vous souhaitez utiliser cette ID pour une comparaison (dans un helper {{eq qpModelId model.id}}) pour l'afficher dans un élément <select> par exemple.

Prenons donc l'exemple d'une liste <select> dans lequelle vous affichez une liste de ville. Une ville étant un model Ember.js.

Votre liste ressemblera sûrement à quelque chose comme ça :

<select onchange={{action (mut ville) value="target.value"}}>
  <option disabled selected>Aucune séléction</option>
  {{#each model as |villeChoice|}}
    <option selected={{eq villeChoice.id ville}} value={{villeChoice.id}}>
      {{villeChoice.nom}}
    </option>
  {{/each}}
</select>

Et votre controller prendra en charge un query param "ville" (avec comme valeur par défaut null) :

import Ember from 'ember';

export default Ember.Controller.extend({
  queryParams: ['ville'],
  ville: null
});

Super ça marche, mon query param se met bien à jour :
query-param-change

Il est où le problème ?

Essayez donc de revenir sur l'URL précédente ou de raffraîchir votre page. Voilà ce qui se passe lorsque vous changez votre query param sans toucher à votre liste :
query-param-change-bug
La liste n'est pas mise à jour par rapport au query param. Ici la ville de Paris n'est pas sélectionnée (pourtant c'est bien l'ID 1), idem avec l'ID 3 pour Bordeaux.

Voici un Twiddle qui reproduit le problème : Query param et ID de model.

Pourtant si on affiche la valeur du query param, la valeur semble correcte:

Valeur du query param : {{ville}}

Capture-d-e-cran-2018-03-15-a--21.02.00

Alors que se passe t-il ?

C'est en fait assez simple : il s'agit d'un problème de cast. En effet, le query param extrait de l'URL est converti en string.

D'un côté nous avons donc des ID de model de type int et de l'autre un paramètre de type string sauf que le helper {{eq}} lui, effectue une comparaison stricte.

L'élément de notre liste <select> n'est donc jamais sélectionné car la comparaison entre le query param et l'Id du model n'est jamais true.

Les solutions

À première vue il y a plusieurs moyens pour résoudre notre problème.

Alert Spoiler: En fait il n'y en a qu'une seule de correcte, je vous recommande de passer directement à la troisième solution si vous êtes pressé !

1 - Une computed property :

Utiliser une computed property pour "caster" notre query param en int puis utiliser cette propriété dans notre helper :

villeInt : Ember.computed('ville', function(){ return parseInt(this.get('ville')); })
{{eq villeChoice.id villeInt}}

2 - Créer un helper :

Créer un helper personnalisé, qui effectue une comparaison non stricte à l'aide d'un double égal et utiliser ce nouvel helper :

//helpers/eq-non-strict.js 
import Ember from 'ember';

export function eqNonStrict(params/*, hash*/) {
  return params[1] == params[0];
}

export default Ember.Helper.helper(eqNonStrict);
<option selected={{eq-non-strict villeChoice.id ville}} value={{villeChoice.id}}>

3 - Lire la doc

Oui oui, lire la documentation Ember.js... C'est clairement la meilleure solution ! Regardons alors ce que nous apprend la documentation :

Query param values are cast to the same datatype as the default value, e.g. a URL change from /?page=3 to /?page=2 will set controller:articles's page property to the number 2, rather than the string "2". The same also applies to boolean default values.
Voir ici : Query params - Default values and deserialization

Simple non ? "La valeur d'un query param est "castée" avec le même type que sa valeur par défaut". Bon c'est sûr : il faut le savoir ! Moi-même la première fois que j'ai rencontré ce "bug", j'ai utilisé la solution n°1...

Reprennons notre exemple, si vous le voulez bien: la valeur par défaut de notre query param est null. La valeur null n'étant pas de type int Ember.js cast logiquement notre paramètre sous forme de string (type par défaut).

Je ne m'attarderai pas sur des questions du type "mais null et 0 c'est pareil non !?", il y a déjà de nombreux posts sur le sujet (notamment sur StackOverflow). Notez également que le problème est le même en utilisant undefined à la place de null.

Pour résoudre notre problème il suffit donc de remplacer notre null par un 0 et le tour est joué :

import Ember from 'ember';

export default Ember.Controller.extend({
  queryParams: ['ville'],
  ville: 0
});

Le résultat :
query-param-change-ok

Et voilà ! Comme je dis souvent à mes collègues :

Si ça te semble compliqué à faire ou que tu penses que ça devrait être le faire "de base", regarde la doc de l'API Ember.js, tu n'as sûrement pas la bonne "Ember Way" pour le faire...