Zum Inhalt

Power Pages Auto-Suggest mit React: Debounce und Requests

Power Pages Auto-Suggest mit React: Debounce, Request-Sequenzen und klare UI-Zustände verhindern veraltete Antworten und unnötige Suchanfragen.

Power Pages Auto-Suggest mit React: Debounce, Request-Sequenzen und klare UI-Zustände verhindern veraltete Antworten und unnötige Suchanfragen.

Inhalt

Warum ist Auto-Suggest in Power Pages mehr als ein Dropdown?

Ein Suchfeld mit Vorschlägen wirkt zunächst klein.

Nach drei Zeichen erscheint eine Liste. Du wählst einen Eintrag. Der Text landet im Feld.

Die schwierige Arbeit liegt zwischen diesen sichtbaren Schritten.

Was passiert, wenn du schnell weitertippst, während ein Request bereits läuft? Wie unterscheidet die Oberfläche eine erfolgreiche leere Liste von einem technischen Fehler? Darf eine Auswahl sofort navigieren? Und welche Daten darf der Browser überhaupt an Dataverse weitergeben?

Ich behandle Auto-Suggest deshalb als eigenen Ablauf. Er besitzt einen eigenen UI-Zustand, eine eigene Server-Operation und einen eigenen Response Parser.

Die Architekturübersicht der Power Pages Enterprise-Suche zeigt den vollständigen Datenfluss zwischen React, Power Pages Server Logic und Dataverse.

Wie funktionieren 3 Zeichen und 300 Millisekunden Debounce?

Der Hook arbeitet mit drei festen Grenzen:

const SUGGESTION_DEBOUNCE_MS = 300;
const MIN_SUGGESTION_LENGTH = 3;
const MAX_SUGGESTION_LENGTH = 100;

Vor jeder Entscheidung trimme ich die Eingabe.

Ein Text mit zwei sichtbaren Zeichen und zusätzlichen Leerzeichen bleibt deshalb zu kurz. Ein Text mit mehr als 100 getrimmten Zeichen startet ebenfalls keinen Vorschlagsrequest.

In beiden Fällen leert der Hook die vorhandenen Vorschläge und setzt den Zustand auf idle.

Ab drei und bis 100 Zeichen passiert etwas anderes. Die vorhandene Liste wird sofort geleert. Der Zustand wechselt sofort zu loading. Erst nach 300 Millisekunden ohne neuere Eingabe ruft der Hook den Controller auf.

function requestSuggestions(value: string): void {
  const normalizedValue = value.trim();

  clearCurrentTimer();

  if (normalizedValue.length < 3 || normalizedValue.length > 100) {
    setSuggestions([]);
    setStatus('idle');
    return;
  }

  setSuggestions([]);
  setStatus('loading');

  timer.current = setTimeout(() => {
    void controller.fetchSuggestions(normalizedValue);
  }, 300);
}

Der Ausschnitt ist für den Artikel reduziert. Die produktive Sequenzprüfung folgt im nächsten Schritt.

Das Debounce verhindert unnötige Starts während schneller Eingaben. Es ist keine Netzwerk-Cancellation.

Auto-Suggest Verhalten für zu kurze, gültige und zu lange Eingaben

Getrimmte Eingabe

Sofortiger Zustand

Vorschlagsliste

Request

Weniger als 3 Zeichen

idleWird geleertKein Request

3 bis 100 Zeichen

loadingWird vor neuen Ergebnissen geleertStart nach 300 Millisekunden ohne neue Eingabe

Mehr als 100 Zeichen

idleWird geleertKein Request

Wie schützt eine Sequenznummer React Auto-Suggest vor alten Antworten?

Ein Debounce löst nur die Zeit vor dem Request.

Wenn ein Request bereits gesendet wurde und du den Text änderst, läuft dieser HTTP Request weiter. Die aktuelle Implementierung nutzt keinen AbortController und keine Abbruchfunktion des Transports.

Ich verhindere stattdessen, dass eine alte Antwort den neuen Zustand überschreibt.

const requestSequence = { current: 0 };

function requestSuggestions(value: string): void {
  const normalizedValue = value.trim();
  const sequence = requestSequence.current + 1;
  requestSequence.current = sequence;

  clearCurrentTimer();

  if (normalizedValue.length < 3 || normalizedValue.length > 100) {
    setSuggestions([]);
    setStatus('idle');
    return;
  }

  setSuggestions([]);
  setStatus('loading');

  timer.current = setTimeout(() => {
    void controller.fetchSuggestions(normalizedValue).then(
      (nextSuggestions) => {
        if (requestSequence.current !== sequence) {
          return;
        }

        setSuggestions(nextSuggestions);
        setStatus(nextSuggestions.length > 0 ? 'ready' : 'empty');
      },
      () => {
        if (requestSequence.current !== sequence) {
          return;
        }

        setSuggestions([]);
        setStatus('error');
      }
    );
  }, 300);
}

Jede neue Eingabe erhöht die Sequenznummer. Dasselbe passiert bei einer Auswahl und beim Unmount des Hooks.

Trifft eine ältere Antwort später ein, stimmt ihre lokale Sequenz nicht mehr mit dem aktuellen Wert überein. Der Hook ignoriert sowohl das Ergebnis als auch einen möglichen Fehler.

Das schützt die sichtbare Liste vor Race Conditions. Es reduziert nicht nachträglich die Last eines bereits gesendeten Requests.

Welche UI-Zustände braucht Auto-Suggest in Power Pages?

Ich verwende keinen einzelnen Boolean wie isLoading.

Der Vorschlagsablauf besitzt fünf Zustände:

type SearchSuggestionStatus = 'idle' | 'loading' | 'ready' | 'empty' | 'error';
Bedeutung und sichtbare Reaktion der Auto-Suggest Zustände

Zustand

Bedeutung

Sichtbare Reaktion

idleKeine gültige Eingabe, Auswahl abgeschlossen oder Zustand geleertKeine Vorschlagsmeldung
loadingDebounce läuft oder Request ist aktivVorschläge werden geladen
readyMindestens ein geprüfter Vorschlag ist verfügbarGruppierte Vorschlagsliste
emptyRequest war erfolgreich, besitzt aber keine TrefferKeine passenden Vorschläge gefunden
errorTransport, Server Logic, Custom API oder Parser meldet FehlerVorschläge konnten nicht geladen werden

Die sichtbaren Texte unterscheiden diese Situationen:

const NO_RECORDS_TEMPLATES = {
  loading: 'Vorschläge werden geladen ...',
  error: 'Vorschläge konnten nicht geladen werden.',
  empty: 'Keine passenden Vorschläge gefunden.',
} as const;

Ein leeres Ergebnis ist kein technischer Fehler. Ein technischer Fehler ist keine leere Ergebnisliste.

Beide Zustände bleiben auf den Vorschlagsbereich begrenzt. Du kannst den Freitext weiterhin absenden.

Wie nutzen Suchfeld und Filtermodal denselben Auto-Suggest-Baustein?

Das SearchInput wird an zwei Stellen eingesetzt.

Im Hauptsuchfeld schreibt onChange direkt in den angewendeten Suchtext:

<SearchInput
  value={filters.searchText}
  onChange={(value) => {
    setAppliedFilter('searchText', value);
  }}
/>

Der Text ist damit Teil des sichtbaren Formularzustands. Eine vollständige Suche startet trotzdem erst über die Suchschaltfläche.

Im erweiterten Filtermodal schreibt derselbe Baustein nur in den Entwurf:

<SearchInput
  value={draftFilters.searchText}
  onChange={(value) => {
    onDraftChange('searchText', value);
  }}
  placeholder="Stadt, Postleitzahl, Region, Standort"
/>

Eine Auswahl im Modal wendet keine anderen Entwurfsfilter an. Sie schließt das Modal nicht und sendet keine Suche ab.

Erst die Aktion zum Anwenden der Filter überträgt den gesamten Entwurf in den angewendeten Zustand. Abbrechen verwirft die Änderungen.

Der Vorschlagsablauf bleibt in beiden Widget-Modi identisch. Der Beitrag über URL Zustand und Browserhistorie zeigt, warum erst der spätere Submit zwischen Navigation und URL Aktualisierung unterscheidet.

Warum sind Suggest und Search getrennte Power Pages Operationen?

Auto-Suggest verwendet denselben Power Pages Server Logic Endpunkt wie die vollständige Suche. Der Request Vertrag ist trotzdem kleiner.

type SupportedLcid = 1031 | 1033;

interface SuggestServerLogicRequest {
  readonly operation: 'suggest';
  readonly lcid: SupportedLcid;
  readonly searchText: string;
}

interface SearchServerLogicRequest {
  readonly operation: 'search';
  readonly lcid: SupportedLcid;
  readonly searchText?: string;
  readonly filters?: Readonly<Record<string, unknown>>;
}

Der Controller trimmt den Text und blockiert Werte außerhalb der Länge von drei bis 100 Zeichen erneut:

function fetchSuggestions(searchText: string): Promise<readonly SearchSuggestion[]> {
  const normalizedSearchText = searchText.trim();

  if (normalizedSearchText.length < 3 || normalizedSearchText.length > 100) {
    return Promise.resolve([]);
  }

  return service.fetchSuggestions(normalizedSearchText, resolveLocale());
}

Der Service ergänzt ausdrücklich operation: 'suggest'. Der Transport sendet den Request über den zentralen Power Pages AJAX Wrapper und übergibt die Antwort an den Suggestion Parser.

Die vollständige Suche besitzt ihren eigenen Weg. Tippen startet keine Search Operation. Nur die Suchaktion startet Navigation oder einen vollständigen Request.

Dieser Vergleich bleibt bewusst auf Eingabe und Suchaktion begrenzt. Der gemeinsame POST-Endpunkt besitzt zusätzlich getPortalCatalogDetails für bekannte GUIDs. Diese dritte Operation löst weder Debounce noch Vorschlagsauswahl aus.

Der separate Beitrag zu Power Pages Favoriten mit Batch-GUIDs dokumentiert diesen Anwendungsfall.

Unterschiede zwischen Suggest Operation und vollständiger Search Operation

Aspekt

Auto-Suggest

Vollständige Suche

Operation

suggestsearch

Auslöser

Gültige Texteingabe nach DebounceAusdrückliche Suchaktion

Request Inhalt

Locale und SuchtextLocale, Suchtext und angewendete Filter

URL und Navigation

Keine ÄnderungNavigation oder Aktualisierung der Ergebnisseiten-URL

Fehlerwirkung

Freitextsuche bleibt möglichTechnischer Suchfehler bleibt sichtbar

Der Beitrag über typisierte React und Dataverse Verträge beschreibt diese Schichtentrennung im vollständigen Kontext.

Warum bleibt Power Pages Server Logic die Suggest-Grenze?

Der Browser darf die Suggest Regeln nicht allein bestimmen.

Power Pages Server Logic prüft die Operation erneut. Für suggest muss searchText eine Zeichenkette sein. Der Server trimmt den Wert und lehnt mehr als 100 Zeichen ab.

Unter drei Zeichen liefert Server Logic eine erfolgreiche leere Liste zurück, ohne die Dataverse Custom API aufzurufen.

Ab drei Zeichen baut der Server selbst eine kontrollierte Freitextabfrage für vier Kategorien:

  1. Stadt
  2. Postleitzahl
  3. Region
  4. Standort

Der Browser übermittelt keine Feldnamen und keinen fertigen OData Ausdruck.

Die konkrete Dataverse Implementierung bleibt im öffentlichen Beispiel bewusst verborgen. Der Architekturvertrag ist trotzdem klar. Server Logic bestimmt erlaubte Felder, Operatoren, Locale und Autorisierung. Erst danach wird die Custom API aufgerufen.

Die Microsoft Übersicht zu Power Pages Server Logic beschreibt diese serverseitige Ausführungsgrenze.

Mein Beitrag zur Power Pages Server Logic Sicherheitsgrenze zeigt zusätzlich, wie Request-Größe, Operations Allowlist, serverseitige Autorisierung und OData Escaping zusammenspielen.

Wie priorisiert Power Pages direkte Auto-Suggest-Treffer?

Ein passender Backend-Datensatz kann mehrere verwertbare Werte liefern.

Passt der Suchtext zum Standortnamen, können Stadt, Postleitzahl und Region desselben Datensatzes ebenfalls als zugehörige Vorschläge erscheinen.

Jeder Vorschlag besitzt deshalb ein Merkmal:

type SearchSuggestionKind = 'city' | 'postalCode' | 'region' | 'center';

interface SearchSuggestion {
  readonly kind: SearchSuggestionKind;
  readonly label: string;
  readonly value: string;
  readonly isDirectMatch: boolean;
}

isDirectMatch ist wahr, wenn der rohe Wert der jeweiligen Kategorie den getrimmten Suchtext ohne Beachtung der Großschreibung enthält.

Die Sortierung arbeitet auf Kategorieebene:

  1. Kategorien mit mindestens einem direkten Treffer kommen zuerst.
  2. Kategorien ohne direkten Treffer folgen danach.
  3. Bei gleicher Direktheit gilt Stadt, Postleitzahl, Region und Standort.
  4. Innerhalb jeder Kategorie bleibt die Reihenfolge der Backend-Datensätze erhalten.

Das ist eine wichtige Genauigkeitsgrenze. Direkte Einträge werden innerhalb derselben Kategorie nicht zusätzlich vor indirekten Einträgen sortiert.

Die Implementierung nutzt auch keine weitere Relevanzbewertung und keine eigene Diakritika-Normalisierung.

Wie trennt Auto-Suggest Deduplizierung und Labels?

Die Deduplizierung kombiniert Kategorie und normalisierten Rohwert.

Eine Stadt und ein Standort mit demselben Text bleiben deshalb zwei unterschiedliche Vorschläge. Zwei Städte mit demselben kleingeschriebenen Wert werden nur einmal ausgegeben.

Die Antwort ist auf zehn Vorschläge begrenzt.

Postleitzahlen besitzen eine zusätzliche Darstellungsregel:

const suggestion: SearchSuggestion = {
  kind: 'postalCode',
  label: '39104 - Beispielstadt',
  value: '39104',
  isDirectMatch: true,
};

Das Label hilft bei der Einordnung. Der ausgewählte Wert bleibt die reine Postleitzahl.

Im Browser werden die Typen mit festen Gruppenlabels verbunden:

const GROUP_LABELS: Record<SearchSuggestionKind, string> = {
  city: 'STADT',
  postalCode: 'POSTLEITZAHL',
  region: 'REGION',
  center: 'CENTER',
};

Die UI rendert label. Bei der Auswahl schreibt sie value in das Suchfeld.

Wie validiert React jeden Auto-Suggest-Vorschlag?

TypeScript Typen enden an der Netzwerkgrenze.

Der Response Parser prüft zuerst, ob die Serverantwort zur Operation suggest gehört. Danach muss data.suggestions ein Array sein.

Jeder Eintrag wird einzeln geprüft:

function parseSuggestion(value: unknown): SearchSuggestion {
  if (!isRecord(value)) {
    throw new Error('Suggestion must be an object.');
  }

  if (!isSuggestionKind(value['kind'])) {
    throw new Error('Suggestion has an invalid kind.');
  }

  if (typeof value['label'] !== 'string' || value['label'].trim() === '') {
    throw new Error('Suggestion needs a label.');
  }

  if (typeof value['value'] !== 'string' || value['value'].trim() === '') {
    throw new Error('Suggestion needs a value.');
  }

  if (typeof value['isDirectMatch'] !== 'boolean') {
    throw new Error('Suggestion needs isDirectMatch.');
  }

  return {
    kind: value['kind'],
    label: value['label'],
    value: value['value'],
    isDirectMatch: value['isDirectMatch'],
  };
}

Ein falscher Kind-Wert oder ein leeres Label wird nicht als leere Liste behandelt. Der Parser lehnt die Antwort ab. Der Hook zeigt anschließend den Zustand error.

Damit bleibt ein Vertragsfehler sichtbar.

Warum löst die Auswahl eines Vorschlags keine Suche aus?

Die Auswahl besitzt bewusst einen kleinen Effekt:

function handleSelect(value: string): void {
  onChange(value);
  selectSuggestion(value);
  autoCompleteRef.current?.hidePopup();
}

selectSuggestion erhöht die Request-Sequenz, löscht den Timer, leert die Liste und setzt den Zustand auf idle.

Zusätzlich merkt sich der Hook den ausgewählten getrimmten Wert. Wenn die AutoComplete Komponente denselben Wert unmittelbar wieder als Filtereingabe meldet, startet dafür kein neuer Suggest Request.

Die Auswahl:

  1. setzt den Suchtext
  2. schließt das Vorschlagsfenster
  3. navigiert nicht
  4. aktualisiert die URL nicht
  5. löst kein search-changed Event aus
  6. startet keine vollständige Suche

Der Beitrag über CustomEvents in Power Pages React Microfrontends beschreibt, wann der Suchzustand und wann Ergebnisse tatsächlich veröffentlicht werden.

Warum bleibt Freitext in Power Pages Auto-Suggest gültig?

Auto-Suggest ist eine Hilfe. Es ist keine Validierung gegen eine Stammdatenliste.

Du kannst einen Text eingeben und die Suche absenden, ohne einen Vorschlag auszuwählen. Das gilt auch nach einer leeren Vorschlagsliste oder einem technischen Suggest Fehler.

Diese Entscheidung hält Suggest und Search fachlich getrennt.

Ein Suggest Fehler darf keine vollständige Suche blockieren. Umgekehrt darf die Auswahl eines Vorschlags nicht heimlich die teurere Suche starten.

Für Partnerprojekte ist diese Grenze wichtig. Vorschläge verbessern Orientierung und Eingabegeschwindigkeit. Die eigentliche Suchoperation bleibt kontrollierbar und nachvollziehbar.

Wie aktualisiert Syncfusion Auto-Suggest vor dem Paint?

Die AutoComplete Komponente rendert ihren Inhalt für leere Daten, bevor React den neuen Template-Prop vollständig sichtbar gemacht hat.

Ohne zusätzlichen Schritt kann kurz ein veralteter Fallback erscheinen, obwohl der Hook bereits loading, empty oder error gesetzt hat.

Ich aktualisiere deshalb den Text und die gefilterten Daten in useLayoutEffect:

useLayoutEffect(() => {
  const autoComplete = autoCompleteRef.current;

  if (status === 'idle' || document.activeElement !== autoComplete?.element) {
    return;
  }

  autoComplete.noRecordsTemplate = noRecordsTemplate;
  autoComplete.filter(suggestionItems);
}, [noRecordsTemplate, status, suggestionItems]);

Der Effekt läuft nur, wenn das Eingabefeld fokussiert ist und der Zustand nicht idle lautet.

Die React Dokumentation zu useLayoutEffect erklärt, warum diese Arbeit vor dem erneuten Paint abgeschlossen wird.

Das ist keine allgemeine Empfehlung für jedes AutoComplete. Es ist eine konkrete Anpassung an das Renderverhalten der verwendeten Komponente.

Was verspricht diese Power Pages Auto-Suggest-Architektur nicht?

Die Implementierung besitzt klare Grenzen:

  1. Sie bricht laufende HTTP Requests nicht physisch ab.
  2. Sie ignoriert überholte Antworten über eine Sequenznummer.
  3. Sie sortiert Kategorien nach direkten Treffern, nicht jeden Eintrag nach einem Relevanzwert.
  4. Sie normalisiert Großschreibung, aber keine Diakritika.
  5. Sie erzwingt keine Auswahl aus der Vorschlagsliste.
  6. Sie behandelt Auto-Suggest nicht als Sicherheitsgrenze.
  7. Sie startet durch eine Auswahl keine vollständige Suche.

Diese Aussagen sind weniger spektakulär als ein pauschales Versprechen von intelligenter Suche. Sie sind dafür überprüfbar.

Power Pages Auto-Suggest: Erfahrungen aus dem Enterprise-Projekt

Ich habe diesen Ablauf für eine Enterprise-Suche in Power Pages umgesetzt.

Die öffentliche ECE Fallstudie zur Power Pages Enterprise-Suche zeigt den Projektkontext. Der Beitrag veröffentlicht keine kundenspezifischen Endpunkte, Schemanamen, IDs oder Produktionswerte.

Für mich liegt der Nutzen nicht nur in schnelleren Eingaben. Der größere Wert entsteht durch getrennte Operationen, sichtbare Fehlerzustände und einen kontrollierten Übergang von React über Server Logic zur Dataverse Custom API.

Du planst eine Power Pages Suche mit React, Auto-Suggest und serverseitigen Geschäftsregeln? Im Power Pages Pro-Code Bereich zeige ich, wie ich Verträge, Sicherheitsgrenzen und Bedienverhalten vor der Entwicklung kläre.

Ähnliche Beiträge

Was soll Ihr Portal können?

Beschreiben Sie Ihren Workflow. Wir klären gemeinsam, welcher nächste Schritt sinnvoll ist.

Workflow besprechen