Novembre 2018

Volume 33 numero 12

Cutting Edge - componenti Blazor personalizzati

Dal Dino Esposito

Dino EspositoL'esperienza di programmazione di Blazor comporta naturalmente verso un ampio uso di componenti. In Blazor, un componente è una classe .NET che implementa una logica di rendering dell'interfaccia utente basata su uno stato. I componenti Blazor si avvicinano l'idea dei componenti Web dalla specifica W3C imminente e le implementazioni analoghe nel Framework di applicazione a singola pagina (SPA). Un componente Blazor è una combinazione di HTML, C# e interoperativa codice JavaScript e CSS che insieme agire come un singolo elemento con un'interfaccia di programmazione comune. Un componente Blazor può generare eventi ed esporre le proprietà in gran parte allo stesso modo un elemento DOM HTML.

Nel mio articolo recente su Blazor, ho presentato un paio di componenti dedicati. Il primo disponibile un'interfaccia utente completa per digitare un testo, eseguire una query e generare un evento esterno con i dati recuperati come argomento. Il secondo componente nell'esempio fungeva da una griglia non personalizzabile, su misura che verrà gestire l'evento, recuperare i dati e popolare la tabella interna. Il campo di input la raccolta il testo della query offerto completamento automatico tramite l'estensione digitazione Bootstrap. In questo articolo, compilare da quel momento e discutere la progettazione e implementazione di un componente completamente basato su Blazor per la funzionalità Completamento automatico. Al termine dell'attività, è necessario un file con estensione CSHTML riutilizzabile senza dipendenze qualsiasi valore tranne l'aggregazione di Bootstrap di 4 core.

L'interfaccia pubblica di completamento automatico

L'obiettivo consiste nel produrre un componente nativo Blazor che si comporta come una versione estesa del componente classico typeahead. js (vedere twitter.github.io/typeahead.js). Il componente finale si trova all'interno di una casella di testo e un elenco a discesa. Come digitato dagli utenti nel campo di input di testo, il componente esegue una query un URL remoto e ottiene gli hint di possibili valori da immettere. Il completamento automatico JavaScript vengono forniti suggerimenti, ma non impone agli utenti di accettare uno dei suggerimenti. Testo personalizzato diverso da suggerimenti in altre parole, è comunque accettabile.

In alcuni casi, tuttavia, si desidera che gli utenti immettere testo e quindi selezionare da un elenco di possibili valori. Si pensi, ad esempio, un campo di input in cui è necessario fornire un nome di paese o un nome di cliente. Sono disponibili più di 200 centinaia ai clienti di paesi in tutto il mondo e spesso centinaia (o più) in un sistema software. È opportuno effettivamente utilizzare un elenco a discesa? Un componente di digitazione intelligente può consentire agli utenti di digitare, ad esempio, la parola "Uniti" e quindi di presentare un elenco di paesi, ad esempio Emirati Arabi Uniti, Regno Unito e Stati Uniti. Se viene digitato alcun testo libero, il codice cancella automaticamente il buffer.

Un altro problema correlato: Quando testo libero non è un'opzione, è probabilmente necessario deve avere anche un codice. Idealmente si desidera digitare il nome del cliente o paese e sono registrati da modulo di hosting l'ID del paese o l'identificatore univoco per il cliente. In HTML e JavaScript puro, è necessario alcuni script aggiuntivo che aggiunge un campo nascosto e gestisce le selezioni dal plug-in typeahead. Un componente nativo Blazor disporrà di tutte queste funzionalità nascoste dietro le quinte. Vediamo come scrivere un componente di questo tipo.

Progettazione del componente

Il componente Typeahead deve essere un campo di input aggiuntivo da usare all'interno di un form HTML. Si è costituito da due campi di input standard, uno di tipo text e uno nascosto. Il campo di input nascosto sarà dotato di un attributo NAME che renderà tutto interoperabile se usato all'interno di un form. Ecco alcuni markup di esempio per l'utilizzo del nuovo componente:

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

Come può notare, il componente speculare alcuni attributi di specifiche di HTML, ad esempio nome, classe, stile e da segnaposto side-by-side con gli attributi personalizzati, ad esempio Url, SelectionOnly ed eventi, ad esempio onSelectionMode. I set di attributi Url endpoint remoto da chiamare per gli hint, mentre l'attributo booleano SelectionOnly controlla il comportamento del componente e indica se input deve provenire solo da una selezione oppure se liberamente digitato del testo è consentita come input. È possibile esaminare il markup Razor e correlati C# del componente nel figura 1. Sono disponibili i dettagli completi nel file di progetto di esempio in typeahead.cshtml bit.ly/2ATgEKm.

Figura 1 Markup del componente Typeahead

<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>

Figura 2 sono elencate le proprietà definiti e supportati dal componente.

Figura 2 proprietà del componente di completamento automatico

Nome Descrizione
Classe Ottiene e imposta la raccolta delle classi CSS da applicare agli elementi HTML interni del componente.
Nome Ottiene e imposta il valore dell'attributo NAME quando il componente è incorporare in un form HTML.
Segnaposto Ottiene e imposta il testo per fungere da segnaposto per gli elementi HTML viene eseguito il rendering.
SelectedText Ottiene e imposta il testo visualizzato. Questo serve come valore iniziale e il testo selezionato, se digitato dall'utente o selezionati da un menu a discesa.
SelectedValue Ottiene e imposta il valore selezionato. Può supportare o potrebbe non corrispondere al valore di SelectedText. Il valore selezionato è associato al campo nascosto, mentre SelectedText è associato al campo di testo.
SelectionOnly Valore booleano che determina se è consentito testo libero o gli utenti sono obbligati a selezionare solo uno degli hint specificati.
Stile di visualizzazione Ottiene e imposta la raccolta di stili CSS da applicare agli elementi HTML interni del componente.
URL Indirizzo dell'endpoint remoto da chiamare per gli hint.

È importante notare che il layout HTML del componente Typeahead è più complesso rispetto a un paio di campi di input. È progettato per ricevere suggerimenti sotto forma di una classe TypeAheadItem definita come illustrato di seguito:

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

Eventuali hint suggerito è costituito da un testo di visualizzazione (ad esempio il nome di paese) che consente di impostare il campo di input, un testo di menu (ad esempio, un testo più complessa basata su HTML) che viene visualizzato nell'elenco a discesa e il valore (ad esempio, il codice di paese) che identifica in modo univoco l'elemento selezionato. Il valore dell'attributo è facoltativo, ma serve a uno scopo fondamentale nel caso in cui il componente Typeahead viene utilizzato come un elenco a discesa intelligente. In questo caso, il ruolo dell'attributo Value è quello utilizzato per l'attributo value dell'elemento HTML Option. Il codice che si trova in corrispondenza dell'endpoint remoto a cui fanno riferimento l'attributo Url deve restituire una matrice delle entità TypeAheadItem. Figura 3 viene fornito un esempio di un endpoint che restituisce l'elenco di nomi di paesi che corrispondono a una stringa di query.

Figura 3, restituendo un elenco di nomi di paese

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);
}

Sono disponibili un paio di aspetti da notare in questo caso ed entrambi hanno a che fare con espressività. Deve essere presente una sorta di intimacy tra il server di hint e il componente di digitazione. Gli sviluppatori presente la parola finale sui cui. Nel codice di esempio illustrato in questo articolo, l'endpoint del servizio Web restituisce una raccolta di oggetti TypeAheadItem. In un'implementazione personalizzata, tuttavia, è possibile avere un endpoint restituiscono una raccolta di proprietà specifiche dell'applicazione e consentire al componente di decidere in modo dinamico la proprietà da utilizzare per il testo e per il valore. L'oggetto TypeAheadItem, tuttavia, fornisce una terza proprietà, ovvero MenuText, che contiene una stringa HTML da assegnare alla voce di elenco a discesa, come illustrato nella figura 4.

Componente Blazor Typeahead in azione
Figura 4 Typeahead Blazor componente in azione

Il MenuText è impostato per la concatenazione del nome di paese e continent, insieme al nome del capitale giustificati a destra nel controllo. È possibile usare qualsiasi formattazione HTML adatta alle proprie esigenze visual.

Con il markup di determinare in modo statico nel server non è ideale, tuttavia. Un approccio molto migliore potrebbe trovarsi a inviare che eventuali dati importanti per il client e il markup ti permettono di essere specificato come parametro di modello per il componente. Non a caso, i componenti basati su modelli sono una funzionalità imminente ad accesso sporadico di Blazor che affronterà il tema in un articolo futuro.

Inoltre, si noti che in figura 4 l'utente digita il nome di un continente e riceve gli hint per tutti i paesi in tale continente. L'implementazione di endpoint di paesi, in realtà, corrisponde alla stringa di query ("oce" nella schermata) per la concatenazione del paese e nome continent, come la corrispondenza di variabile LINQ che vedere nel frammento di codice precedente.

Questo è il primo passaggio di un modo ovviamente più potente per cercare dati correlati. Ad esempio, è possibile, suddividere la stringa di query con virgole o spazi per ottenere una matrice di stringhe di filtro e combinarli da OR o operatori AND. Ancora un altro miglioramento è il modello di elemento dell'elenco a discesa che nell'esempio corrente è hardcoded nell'implementazione di endpoint, ma può essere specificato come un modello HTML con la stringa di query.

La conclusione è che il componente Typeahead presentato in questo articolo usa il plug-in Twitter JavaScript typeahead solo come punto di partenza. I componenti Blazor consentono agli sviluppatori di nascondere con facilità i dettagli di implementazione, in ultima analisi l'aumento del livello di astrazione del codice scritta dagli sviluppatori Web.

I meccanismi del componente

Nelle figura 1, aver esaminato il codice dietro il componente Blazor Typeahead. Lo si basa su 4 di Bootstrap e conta su alcuni stili CSS personalizzati definiti nel file di origine con estensione CSHTML stesso. Se si desidera che gli sviluppatori di personalizzare gli stili, è sufficiente fornire documentazione per loro. In ogni caso, desideri utilizzare il componente Typeahead gli sviluppatori non debbano conoscere stili CSS personalizzati, ad esempio menu scorrevole.

Il componente è articolato come Bootstrap 4 input gruppo costituito da un campo di input di testo, un campo nascosto e un pulsante a discesa. Il campo di input di testo è in cui l'utente digita qualsiasi stringa di query. Il campo nascosto è dove il valore dell'hint per l'accettati verrà archiviato da inoltrare tramite qualsiasi host form HTML. Solo il campo nascosto con il nome dell'attributo HTML impostato. Il pulsante a discesa fornisce il menu con hint. L'elenco di voci di menu viene popolato qualsiasi momento il nuovo testo viene digitato nel campo di input. Il pulsante non è visibile per impostazione predefinita, ma la relativa finestra di riepilogo a discesa a livello di codice viene illustrata quando necessario. Questa operazione viene eseguita sfruttando le funzionalità di associazione dati del Blazor. Una variabile booleana interna viene definita (_isOpen) che determina se la classe CSS "Mostra" pari a 4 Bootstrap deve essere aggiunto alla sezione elenco a discesa del pulsante. Il codice è il seguente:

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

L'operatore di bind Blazor viene utilizzato per associare la proprietà SelectedText alla proprietà value del campo di input di testo e la proprietà SelectedValue alla proprietà value del campo nascosto.

Come si attiva la query per gli hint remota? Nel semplice codice HTML 5, verrà definito un gestore per l'evento di input. L'evento di modifica non è appropriato per le caselle di testo come attiva solo una volta lo stato attivo è stata interrotta, che non è applicabile in questo caso specifico. Nella versione di Blazor utilizzato per l'articolo (versione 0.5.0), è possibile associare alcune C# codice per l'evento di input, ma non funziona come previsto ancora.

Come soluzione temporanea, è associato il codice che consente di popolare l'elenco a discesa all'evento blur e aggiunta codice JavaScript per gestire l'evento di input che eseguita una chiamata blur e concentrare l'attenzione al livello DOM. Come si può notare nel figura 1, il metodo TryAutoComplete che viene eseguito in risposta all'evento blur inserisce la chiamata remota, acquisisce una matrice JSON degli oggetti TypeAheadItem e popola la raccolta di elementi interna:

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;
}

Quando accade questo elenco a discesa viene popolato con menu di scelta degli elementi e visualizzata, come illustrato di seguito:

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

Si noti il cast a MarkupString che è la controparte Blazor per HTML. raw in ASP.NET MVC Razor. Per impostazione predefinita è codificato qualsiasi testo elaborato da Razor, tranne quando viene eseguito il cast dell'espressione nel tipo MarkupString. Di conseguenza, se si desidera HTML da visualizzare, è necessario passare attraverso il cast MarkupString. Ogni volta che si fa clic su una voce di menu, l'esecuzione del metodo TrySelect, nel modo seguente:

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

Il metodo riceve l'oggetto TypeAheadItem associato all'elemento selezionato. Successivamente, chiude l'elenco a discesa impostando _isOpen su false e aggiornamenti SelectedText e SelectedValue come appropriato. Infine, chiama StateHasChanged per aggiornare l'interfaccia utente e genera l'evento SelectionMade personalizzato.

La connessione al componente Typeahead

Una vista Blazor che utilizza il componente Typeahead assocerà parti dell'interfaccia utente per l'evento SelectionMade. Anche in questo caso, le modifiche abbiano effetto il StateHasChanged metodo deve essere richiamato, con il seguente codice:

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

Nel frammento di codice, dati in arrivo con l'evento sono associati alle proprietà locale della visualizzazione e una volta che viene aggiornato il modello DOM la visualizzazione viene aggiornata automaticamente (vedere figura 5).

La vista aggiornata
Figura 5, la vista aggiornata

Conclusioni

Modern Web front-end più sono costituite da componenti. Componenti di aumentare il livello di astrazione del linguaggio di markup e forniscono un modo molto più semplice per creare contenuto Web. Come altri framework lato client, Blazor ha una propria definizione di componenti personalizzati per velocizzare e semplificare lo sviluppo. Il codice sorgente per questo articolo è reperibile in bit.ly/2ATgEKm, side-by-side con scorso del mese sull'uso di plug-in typeahead JavaScript.


Dino Espositoha scritto più di 20 libri e articoli 1,000-plus della sua carriera di 25 anni. Autore di "The congedo Sabbatico Break," Mostra un stile: manifestazioni teatrali, Esposito è occupato di scrittura di software per un mondo ecocompatibile come il strategist digitale in BaxEnergy. Seguirlo su Twitter: @despos.

Grazie al seguente esperto tecnico Microsoft per la revisione dell'articolo: Daniel Roth


Discutere di questo articolo nel forum di MSDN Magazine