X

Ecrire un filtre orderBy insensible à la casse avec VueJS


Il y a un nouveau projet open source sympa appelé koel qui est sorti cette semaine. C’est un lecteur de musique (un clone de Spotify) qui sert votre propre musique et fonctionne sur Laravel et VueJS. J’ai pensé que j’allais l’essayer et voir si je pouvais envoyer des demandes d’extraction pour aider.

Une chose que j’ai remarquée, c’est que lorsque je triais par artiste (ou autre chose), tous les éléments commençant par une lettre minuscule étaient triés après tous les éléments commençant par une lettre majuscule. J’ai jeté un coup d’œil pour voir ce qui se passait, et il semble que la fonction orderBy de VueJS soit sensible à la casse par défaut, ce qui signifie que les lettres majuscules sont triées en premier, puis les minuscules. J’ai cherché sur Google et j’ai trouvé un problème GitHub fermé qui indiquait que c’était le comportement prévu pour orderByj’ai donc décidé d’écrire un filtre orderBy insensible à la casse.

Trouver le natif de Vue orderBy

Tout d’abord, je savais que je voulais imiter le natif de Vue orderBy fonctionner le plus étroitement possible. Donc, j’avais besoin de le chasser. Il existe différentes façons de le faire – grep, votre IDE, etc. – mais finalement nous atterrissons sur vuejs/vue/src/filters/array-filters.js.

/**
 * Filter filter for arrays
 *
 * @param {String} sortKey
 * @param {String} reverse
 */

export function orderBy (arr, sortKey, reverse) {
  arr = convertArray(arr)
  if (!sortKey) {
    return arr
  }
  var order = (reverse && reverse < 0) ? -1 : 1
  // sort on a copy to avoid mutating original array
  return arr.slice().sort(function (a, b) {
    if (sortKey !== '$key') {
      if (isObject(a) && '$value' in a) a = a.$value
      if (isObject(b) && '$value' in b) b = b.$value
    }
    a = isObject(a) ? getPath(a, sortKey) : a
    b = isObject(b) ? getPath(b, sortKey) : b
    return a === b ? 0 : a > b ? order : -order
  })
}

Qu’est-ce que ça fait ? À la base, il duplique le tableau et le trie en extrayant les valeurs de la clé fournie et en les comparant.

Vous avez peut-être remarqué que nous utilisons quelques fonctions non natives : convertArray, isObjectet getPath.

Comment ajouter un filtre personnalisé

Nous connaissons donc la structure de notre nouveau filtre. Où le met-on ?

Vue simplifie l’ajout d’un filtre personnalisé. Voici la structure, que vous pouvez placer dans n’importe quel fichier que vous utilisez pour faire vos liaisons Vue principales :

Vue.filter('reverse', function (value) {
    return value.split('').reverse().join('');
});

Essayons un exemple où nous faisons quelque chose à un tableau. Remarquez que nous voulons dupliquer le tableau avec .slice() nous ne manipulons donc pas le tableau d’origine.

Vue.filter('uppercaseArray', function (array) {
    return array.map(function (item) {
        return item.toUppercase();
    });
});

Nous avons mappé chaque élément du tableau dupliqué, supposé qu’il s’agissait d’une chaîne, l’avons mis en majuscules, puis avons renvoyé le tableau mappé. Cela signifie que nous pouvons maintenant, n’importe où dans notre application, utiliser ce filtre :

<tr v-for="item in items | uppercaseArray"> ... etc.

Remarque : Dans cet exemple, nous n’avons pas utilisé slice() avant d’intervenir sur la baie. Pourquoi? Parce que, comme me l’ont fait remarquer des gens formidables sur Twitter, map() en JavaScript crée déjà un doublon, vous n’avez donc pas besoin slice() comme tu le fais avec sort().

Portage et mise à jour de la commandeBy

Super. Maintenant, écrivons le nôtre. Prenons orderBy et ajoutons quelques lignes pour le rendre insensible à la casse.

Vue.filter('caseInsensitiveOrderBy', function (arr, sortKey, reverse) {
  arr = convertArray(arr)
  if (!sortKey) {
    return arr
  }
  var order = (reverse && reverse < 0) ? -1 : 1
  // sort on a copy to avoid mutating original array
  return arr.slice().sort(function (a, b) {
    if (sortKey !== '$key') {
      if (isObject(a) && '$value' in a) a = a.$value
      if (isObject(b) && '$value' in b) b = b.$value
    }
    a = isObject(a) ? getPath(a, sortKey) : a
    b = isObject(b) ? getPath(b, sortKey) : b

    // Our new lines
    a = a.toLowerCase()
    b = b.toLowerCase()

    return a === b ? 0 : a > b ? order : -order
  })
});

Comme vous pouvez le voir, j’ai juste ajouté la minuscule à la fin, rien d’autre. Ce qui se produit?

ERREUR. Erreur de référence non interceptée : convertArray n’est pas défini. Eh bien, merde.

Il s’avère que toutes ces fonctions personnalisées ne sont pas simplement là pour vous, vous devez trouver où elles sont disponibles. J’ai pu tous les trouver sauf convertArray (Je l’ai trouvé mais je pense que c’est peut-être une méthode entièrement privée) alors mettons-le à jour avec l’endroit où Vue expose chacun (getPath J’ai trouvé avec l’aide d’Evan sur GitHub, et isObject J’ai trouvé par essais et erreurs).

Vue.filter('caseInsensitiveOrderBy', function (arr, sortKey, reverse) {
  // arr = convertArray(arr)
  if (!sortKey) {
    return arr
  }
  var order = (reverse && reverse < 0) ? -1 : 1
  // sort on a copy to avoid mutating original array
  return arr.slice().sort(function (a, b) {
    if (sortKey !== '$key') {
      if (Vue.util.isObject(a) && '$value' in a) a = a.$value
      if (Vue.util.isObject(b) && '$value' in b) b = b.$value
    }
    a = Vue.util.isObject(a) ? Vue.parsers.path.getPath(a, sortKey) : a
    b = Vue.util.isObject(b) ? Vue.parsers.path.getPath(b, sortKey) : b

    a = a.toLowerCase()
    b = b.toLowerCase()

    return a === b ? 0 : a > b ? order : -order
  })
});

Comme vous pouvez le voir, certaines de ces méthodes principales de Vue sont exposées via des objets tels que Vue.util et Vue.parsers.

Maintenant, jetons un coup d’œil à convertArray pour voir si nous nous soucions. Il s’avère que c’est un alias pour _postProcess:

_postProcess: function _postProcess(value) {
  if (isArray(value)) {
    return value;
  }
  ... etc
}

Eh bien, vérifiez-le! Ce n’est pas parfait, mais si nous transmettons un tableau, nous récupérons simplement le tableau. Donc, pendant que je continuerai à chercher comment intégrer convertArray correctement, nous pouvons le supprimer en toute sécurité pour toute utilisation de cette nouvelle fonction qui consiste, en fait, à obtenir un tableau transmis.

Et c’est tout. Nous avons maintenant une fonction caseInsensitiveOrderBy filtre.

<tr v-for="item in items | caseInsensitiveOrderBy title"> ... etc.

Bien. Pour. Aller.