Partager via



Novembre 2018

Volume 33, numéro 12

Cutting Edge - composants Blazor personnalisés

Par Dino Esposito

Dino EspositoL’expérience de programmation de Blazor permet naturellement vers une utilisation intensive des composants. Dans Blazor, un composant est une classe .NET qui implémente une logique de rendu de l’interface utilisateur basée sur un état. Les composants Blazor sont proches l’idée de composants Web à partir de la spécification du W3C à venir et les implémentations analogues dans les infrastructures d’application monopage (SPA). Un composant Blazor est une combinaison de HTML, C# et interopérable code JavaScript et CSS qui, ensemble agissent comme un seul élément avec une interface de programmation commune. Un composant Blazor peut déclencher des événements et exposer les propriétés à peu près la même façon qu’un élément DOM HTML.

Dans mon article récent sur Blazor, j’ai présenté quelques composants dédiés. La première fourni une interface utilisateur complète pour taper du texte, exécutez une requête et déclencher un événement externe avec les données récupérées en tant qu’argument. Le deuxième composant dans l’exemple servait d’une grille de cela, non personnalisable qui gère l’événement, extraire les données et remplir la table interne. Le champ d’entrée collecte le texte de requête proposé une saisie semi-automatique par le biais de l’extension de saisie semi-automatique d’amorçage. Dans cet article, j’ai construire à partir de ce point et discuter la conception et l’implémentation d’un composant entièrement basé sur Blazor pour la fonctionnalité de saisie semi-automatique. À la fin de la tâche, vous aurez un fichier CSHTML réutilisable sans dépendances autre chose que l’offre groupée core Bootstrap 4.

L’Interface publique de prédictives

L’objectif est de produire un composant natif Blazor qui se comporte comme une version étendue du composant typeadhead.js classique (voir twitter.github.io/typeahead.js). Le dernier composant se trouve au milieu d’une zone de texte et une liste déroulante. Comme les utilisateurs tapent dans le champ d’entrée de texte, le composant interroge une URL distante et obtient les indicateurs d’entrer les valeurs possibles. Le tampon de clavier JavaScript fournit des suggestions sans impose aux utilisateurs d’accepter toutes les suggestions. En d’autres termes, le texte personnalisé différent de suggestions est toujours acceptable.

Dans certains cas, cependant, vous souhaitez que les utilisateurs à entrer du texte, puis sélectionnez dans la liste des entrées valides. Considérez, par exemple, un champ d’entrée où vous devez fournir un nom de pays ou un nom de client. Il existe des centaines de plus de 200 clients de pays dans le monde et souvent des centaines (ou plus) dans un système logiciel. Devez-vous réellement utiliser une liste déroulante ? Un composant de saisie semi-automatique intelligent peut permettre aux utilisateurs tapez, par exemple, le mot « États-Unis » et puis de les présenter une liste de mise en correspondance des pays tels qu’Émirats Arabes Unis, United Kingdom et United States. Si n’importe quel texte libre est typé, le code efface automatiquement la mémoire tampon.

Un autre problème connexe : Lorsque le texte libre n’est pas une option, vous devez probablement également un code. Vous souhaitez dans l’idéal, tapez le nom de pays ou de client et ont validées à partir de l’écran d’hébergement l’ID du pays ou l’identificateur unique pour le client. Dans pure HTML et JavaScript, vous avez besoin d’un script supplémentaire qui ajoute un champ masqué et gère des sélections à partir du tampon de clavier plug-in. Un composant natif Blazor aura tous ces fonctionnalités masquées sous le capot. Nous allons voir comment écrire un tel composant.

Conception du composant

Le composant prédictives est censé être un champ d’entrée supplémentaire à utiliser dans un formulaire HTML. Il est constitué de deux champs d’entrée standards, une de type texte et l’autre est masqué. Le champ d’entrée masqué présentera un attribut de nom qui le rendre totalement interopérable si utilisé dans un formulaire. Voici un exemple de balisage pour utiliser le nouveau composant :

<typeahead style="margin-top: 40px;"
           class="form-control"
           url="/hint/countries1"
           selectionOnly="true"
           name="country"
           placeholder="Type something"
           onSelectionMade="@ShowSelection" />

Comme vous pouvez le voir, le composant reflète certains attributs spécifiques à HTML comme Style, classe, nom et espace réservé côte à côte avec des attributs personnalisés tels que l’Url, SelectionOnly et événements tels qu’onSelectionMode. Ensembles d’attributs de l’Url du point de terminaison distant à appeler pour obtenir les indicateurs, tandis que l’attribut de type Boolean SelectionOnly contrôle le comportement du composant et si entrée doit provenir uniquement une sélection ou si librement tapé du texte est autorisée en tant qu’entrée. Nous allons examiner le balisage Razor et liés C# du composant dans Figure 1. Vous trouverez des détails complets dans le fichier typeahead.cshtml de l’exemple de projet à bit.ly/2ATgEKm.

Balisage de la figure 1 du composant prédictives

<div class="blazor-typeahead-container">
  <div class="input-group">
    <input type="text" class="@Class" style="@Style"
           placeholder="@Placeholder"
           oninput="this.blur(); this.focus();"
           bind="@SelectedText"
           onblur="@(ev => TryAutoComplete(ev))" />
    <input type="hidden" name="@Name" bind="@SelectedValue" />
    <div class="input-group-append">
      <button class="btn btn-outline-secondary dropdown-toggle"
              type="button" data-toggle="dropdown"
              style="display: none;">
      </button>
      <div class="dropdown-menu dropdown-menu-right
                  scrollable-menu @(_isOpen ? "show" : "")"
         style="width: 100%;">
        <h6 class="dropdown-header">@Items.Count item(s)</h6>
        @foreach (var item in Items)
        {
          <a class="dropdown-item"
           onclick="@(() => TrySelect(item))">
            @((MarkupString) item.MenuText)
          </a>
        }
      </div>
    </div>
  </div>
</div>

Figure 2 répertorie les propriétés définies et pris en charge par le composant.

Figure 2 propriétés du composant de prédictives

Nom Description
Classe Obtient et définit la collection de classes CSS à appliquer aux éléments HTML internes du composant.
Nom Obtient et définit la valeur de l’attribut NAME lorsque le composant est incorporé dans un formulaire HTML.
Espace réservé Obtient et définit le texte comme un espace réservé pour les éléments HTML restitué.
SelectedText Obtient et définit le texte d’affichage. Ceci sert à la fois la valeur initiale et le texte sélectionné, si saisi par l’utilisateur ou récupérés à partir d’un menu déroulant.
SelectedValue Obtient et définit la valeur sélectionnée. Cela peut ou peut ne pas correspond la valeur de SelectedText. La valeur sélectionnée est liée au champ masqué, tandis que SelectedText est lié au champ de texte.
SelectionOnly Valeur booléenne qui détermine si le texte libre est autorisé ou les utilisateurs sont obligés d’uniquement sélectionner parmi les indicateurs fournis.
Style Obtient et définit la collection de styles CSS à appliquer aux éléments HTML internes du composant.
Url Adresse de point de terminaison distant à appeler pour obtenir les indicateurs.

Il est important de noter que la présentation HTML du composant prédictives est plus complexe que quelques champs d’entrée. Il est conçu pour recevoir des conseils sous la forme d’une classe TypeAheadItem définie comme indiqué ici :

public class TypeAheadItem
{
  public string MenuText { get; set; }
  public string Value { get; set; }
  public string DisplayText { get; set; }}

N’importe quel indicateur suggérée est constitué d’un texte d’affichage (par exemple, le nom du pays) qui définit le champ d’entrée, un texte de menu (par exemple, un texte basé sur HTML plus riche) qui s’affiche dans la liste déroulante et une valeur (par exemple, l’indicatif du pays) qui identifie de façon unique l’élément sélectionné. La valeur d’attribut est facultatif, mais il joue un rôle essentiel dans le cas où le composant prédictives est utilisé comme une liste déroulante actives. Dans ce cas, le rôle de l’attribut Value est identique à l’attribut value de l’élément HTML Option. Le code situé au point de terminaison distant référencé par l’attribut Url est supposée retourner un tableau d’entités de TypeAheadItem. Figure 3 fournit un exemple d’un point de terminaison qui retourne la liste des noms de pays qui correspondent à une chaîne de requête.

Figure 3 retour d’une liste de noms de pays

public JsonResult Countries(
  [Bind(Prefix = "id")] string filter = "")
{
  var list = (from country in CountryRepository().All();
    let match =
      $"{country.CountryName} {country.ContinentName}".ToLower()
    where match.Contains(filter.ToLower())
    select new TypeAheadItem()
    {
      Value = country.CountryCode,
      DisplayText = country.CountryName,
      MenuText = $"{country.CountryName} <b>{country.ContinentName}</b>
        <span class='pull-right'>{country.Capital}</span>"
    }).ToList();
  return Json(list);
}

Il existe quelques éléments à noter ici et les deux doivent faire avec expressivité. Il doit être une sorte d’intimité entre le serveur de l’indicateur et le composant de saisie semi-automatique. En tant que développeur, vous avez le nec plus ultra à ce sujet. Dans l’exemple de code présenté dans cet article, le point de terminaison de service Web retourne une collection d’objets de TypeAheadItem. Dans une implémentation personnalisée, cependant, vous pouvez avoir le point de terminaison retournent une collection de propriétés spécifiques à l’application et que le composant décider dynamiquement dont la propriété doit être utilisée pour le texte et qui pour la valeur. L’objet TypeAheadItem, cependant, fournit une troisième propriété — MenuText, qui contient une chaîne HTML à assigner à l’élément de liste déroulante, comme indiqué dans Figure 4.

Composant de Blazor prédictives en Action
Figure 4 prédictives Blazor composant en Action

Le MenuText est défini sur la concaténation du nom du pays et le nom de continent, ainsi que le nom de la capitale justifié à droite dans le contrôle. Vous pouvez utiliser n’importe quel HTML la mise en forme qui correspond le mieux à vos besoins visual.

Avoir le balisage statiquement déterminé sur le serveur n’est pas idéale, toutefois. Une bien meilleure approche serait d’envoyer que toutes les données pertinentes au client et le balisage permettent d’être spécifiée comme paramètre de modèle pour le composant. Pas par hasard, des composants basés sur des modèles sont une fonctionnalité pratique à venir de Blazor que je vais présenter dans un prochain article.

En outre, notez que dans Figure 4 l’utilisateur tape le nom d’un continent et reçoit des indicateurs pour tous les pays dans ce continent. L’implémentation du point de terminaison de pays, correspond en fait, la chaîne de requête (« oce » dans la capture d’écran) par rapport à la concaténation de pays et continent nom, comme dans la correspondance de variable LINQ que vous voyez dans l’extrait de code ci-dessus.

Il s’agit de la première étape de manière évidemment plus puissant pour rechercher des données connexes. Vous pouvez, par exemple, fractionner la chaîne de requête par des virgules ou des espaces pour obtenir un tableau de chaînes de filtrage et de les combiner ensuite par OR ou des opérateurs AND. Encore une autre amélioration est le modèle de l’élément de liste déroulante, qui dans cet exemple est codé en dur dans l’implémentation du point de terminaison, mais peut être fourni sous la forme d’un modèle HTML, ainsi que la chaîne de requête.

L’essentiel est que le composant de prédictives présenté ici utilise le plug-in de JavaScript Twitter prédictives uniquement comme point de départ. Les composants Blazor permettent aux développeurs de facilement masquer les détails d’implémentation, en fin de compte déclenchant le niveau d’abstraction du code rédigée par les développeurs Web.

La mécanique du composant

Dans Figure 1, avoir examiné le balisage derrière le composant Blazor Typeahead. Il s’appuie sur les données d’amorçage 4 et compte sur quelques styles CSS personnalisés sont définis dans le même fichier source CSHTML. Si vous le souhaitez aux développeurs de personnaliser ces styles, vous fournissez simplement leur documentation. En soit, les développeurs prêts à utiliser le composant prédictives n’êtes pas obligé de connaître les styles CSS personnalisés tels que sur le menu déroulant.

Le composant est articulé sous la forme d’un groupe d’entrée Bootstrap 4 constitué d’un champ d’entrée de texte, un champ masqué et un bouton de liste déroulante. Le champ d’entrée de texte est où l’utilisateur tape n’importe quelle chaîne de requête. Le champ masqué est où la valeur de l’indicateur acceptée est stockée pour être transféré via n’importe quel hôte de formulaire HTML. Seul le champ masqué a l’attribut de nom HTML défini. Le bouton de liste déroulante fournit le menu avec des indicateurs. La liste d’éléments de menu est remplie à tout moment nouveau texte tapé dans le champ d’entrée. Le bouton n’est pas visible par défaut, mais sa fenêtre de liste déroulante est indiqué par programme chaque fois que nécessaire. Pour cela, en exploitant les fonctionnalités de liaison de données de Blazor. Une variable booléenne interne est défini (_isOpen) qui détermine si la classe CSS « show » de Bootstrap 4 doit être ajoutée à la section de la liste déroulante du bouton. Voici le code :

<div class="dropdown-menu
            dropdown-menu-right
            scrollable-menu
            @(_isOpen ? "show" : "")"> ...
</div>

L’opérateur bind Blazor sert à lier la propriété SelectedText à la propriété valeur du champ d’entrée de texte et la propriété SelectedValue à la propriété de valeur du champ masqué.

Comment vous déclencher la requête distante pour les indicateurs ? Dans les 5 HTML brut, vous définissez un gestionnaire pour l’événement d’entrée. L’événement de modification n’est pas approprié pour les zones de texte comme il se déclenche uniquement une fois que le focus a été perdu, ce qui ne s’applique pas dans ce cas particulier. Dans la version de Blazor utilisé pour l’article (version 0.5.0), vous pouvez associer certaines C# code à l’événement d’entrée, mais elle ne fonctionne pas vraiment comme prévu encore.

Comme solution temporaire, j’ai joint le code qui remplit la liste déroulante à l’événement flou et ajouté du code JavaScript pour gérer l’événement d’entrée qui appelle simplement le flou et concentrer au niveau du DOM. Comme vous pouvez le voir dans Figure 1, la méthode TryAutoComplete qui s’exécute en réponse à l’événement flou place l’appel distant, extrait un tableau JSON des objets de TypeAheadItem et remplit la collection d’éléments interne :

async Task TryAutoComplete(UIFocusEventArgs ev)
{
  if (string.IsNullOrWhiteSpace(SelectedText))
  {
    Items.Clear();
      _isOpen = false;
    return;
  }
  var actualUrl = string.Concat(Url.TrimEnd('/'), "/", SelectedText);
  Items = await HttpExecutor.GetJsonAsync<IList<TypeAheadItem>>(actualUrl);
    _isOpen = Items.Count > 0;
}

Lorsque cela se produit de la liste déroulante est remplie avec le menu éléments et affiché, comme suit :

@foreach (var item in Items)
{
  <a class="dropdown-item"
    onclick="@(() => TrySelect(item))">
    @((MarkupString) item.MenuText)
  </a>
}

Notez le cast à MarkupString qui est l’équivalent de Blazor de l’utilisation de Html.Raw dans ASP.NET MVC Razor. Par défaut n’importe quel texte traité par Razor est encodé, sauf lorsque l’expression est convertie vers le type MarkupString. Par conséquent, si vous souhaitez HTML à afficher, vous devez passer par le biais du cast MarkupString. Chaque fois que l’utilisateur clique sur un élément de menu, l’exécution de TrySelect (méthode), comme suit :

void TrySelect(TypeAheadItem item)
{
  _isOpen = false;
  SelectedText = item.DisplayText;
  SelectedValue = item.Value;
  OnSelectionMade?.Invoke(item);
}

La méthode reçoit l’objet TypeAheadItem associé à l’élément cliqué. Ensuite, il ferme la liste déroulante en définissant _isOpen sur false et mises à jour SelectedText et SelectedValue comme il convient. Enfin, il appelle StateHasChanged pour actualiser l’interface utilisateur et déclenche l’événement SelectionMade personnalisé.

Connexion au composant prédictives

Une vue Blazor qui utilise le composant prédictives liera à des parties de son interface utilisateur à l’événement SelectionMade. Là encore, pour que les modifications entrent en vigueur le StateHasChanged méthode doit être appelée, ce code :

void ShowSelection(TypeAheadItem item)
{
  _countryName = item.DisplayText;
  _countryDescription = item.MenuText;
  this.StateHasChanged();
}

Dans l’extrait de code, à l’événement sont lié aux données aux propriétés de la vue locales et une fois que le modèle DOM est actualisé la vue est automatiquement mis à jour (voir Figure 5).

La vue mise à jour
Figure 5 la vue mise à jour

Pour résumer

Serveurs frontaux Web moderne est de plus en plus constituée de composants. Composants augmenter le niveau d’abstraction du langage de balisage et fournissent un moyen beaucoup plus propre pour créer du contenu Web. Comme les autres frameworks côté client, Blazor possède sa propre définition des composants personnalisés pour accélérer et simplifier le développement. Le code source pour cet article, consultez bit.ly/2ATgEKm, côte à côte avec la colonne du mois dernier sur l’utilisation du plug-in JavaScript prédictives.


Dino Espositoest l’auteur de plus de 20 livres et articles 1,000-plus dans sa carrière de 25 ans. Auteur de « The un congé sabbatique Break, » une émission théâtrales-style, Esposito est occupé à écrire des logiciels pour un monde plus écologique en tant que le stratège de numérique à BaxEnergy. Vous pouvez le suivre sur Twitter : @despos.

Merci à l'expert technique Microsoft suivant d'avoir relu cet article : Daniel Roth


Discuter de cet article sur le forum MSDN Magazine