Zum Inhalt

Power Pages Suchfilter mit URL, React und Browserhistorie

Power Pages React synchronisiert Suchfilter mit URL und Browserhistorie. pushState, replaceState und popstate machen Suchzustände teilbar und wiederherstellbar.

Power Pages React synchronisiert Suchfilter mit URL und Browserhistorie. pushState, replaceState und popstate machen Suchzustände teilbar und wiederherstellbar.

Inhalt

Warum React State für Power Pages Suchseiten nicht ausreicht

Eine komplexe Suche kann fachlich korrekt filtern und trotzdem einen grundlegenden Bedienfehler besitzen.

Die Filter leben nur im React State.

Nach einem Reload ist die Auswahl verschwunden. Ein kopierter Link öffnet nicht dieselbe Suche. Die Schaltflächen für Vor und Zurück zeigen zwar eine andere URL, aber Formular und Ergebnisse bleiben unverändert.

Für eine Ergebnisseite ist das kein Detail. Der Suchzustand muss teilbar und wiederherstellbar sein.

In meiner Power Pages Enterprise-Suche bildet deshalb die URL den serialisierten Zustand der angewendeten Filter. React rendert diesen Zustand. Die URL kann ihn nach einem Reload oder einer Bewegung durch die Browserhistorie erneut herstellen.

Das gilt bewusst nur für die Ergebnisseite im Modus update-url. Auf der Landingpage bleibt der bearbeitete Formularzustand bis zum Absenden in React. Danach navigiert die Anwendung mit den gültigen Filtern zur Ergebnisseite.

Die Architekturübersicht der Power Pages Enterprise-Suche zeigt, wo dieser URL Vertrag zwischen React Controller, Browser und Suchrequest liegt.

Welche URL-Modi braucht eine Power Pages React Suche?

Landingpage und Ergebnisseite verwenden dasselbe React Bundle. Sie benötigen aber unterschiedliche Abläufe.

Ich beschreibe diesen Unterschied als discriminated union:

type SearchSubmitBehavior =
  | {
      readonly type: 'navigate';
      readonly targetUrl: string;
    }
  | {
      readonly type: 'update-url';
      readonly historyMode: 'push';
      readonly eventName?: string;
    };

Im Modus navigate validiert und serialisiert das Widget die Filter. Anschließend öffnet es die konfigurierte Ziel URL mit window.location.assign.

Vor dieser Navigation startet es keine vollständige Suche. Vorschläge während der Eingabe können weiterhin eigene Requests verwenden. Der Deep Dive zu Auto-Suggest in Power Pages mit React zeigt, warum diese Requests weder Navigation noch URL Zustand auslösen.

Im Modus update-url liest das Widget den initialen Zustand aus window.location.search. Eine neue Suche erweitert die Browserhistorie. Vor und Zurück stellen den Zustand wieder her.

Der zentrale React Host mit JSON-Konfiguration liefert diesen Modus an dasselbe Bundle. Dadurch bleiben URL Verhalten und Seitentemplate voneinander getrennt.

Der optionale Event Name gehört ebenfalls zum Laufzeitvertrag. Seine Consumer sind ein eigener Integrationsbaustein und bleiben außerhalb dieses URL Deep Dives.

Vergleich der Laufzeitmodi navigate und update-url

Aspekt

navigateupdate-url

Einsatzort

LandingpageErgebnisseite

Ausgangs-URL

Konfigurierte Ziel URLAktuelle Browser URL

Browser Operation

location.assign

pushState und bei Bedarf replaceState

Vollständige Suche

Erst nach NavigationNach akzeptierter URL Aktualisierung

Vor und Zurück

Normale Seitennavigation

popstate stellt Suchzustand wieder her

Wie serialisiert React Power Pages Suchfilter in der URL?

Ich schreibe nicht den vollständigen React State in die URL.

Entwurfszustände eines geöffneten Modals, Ladeindikatoren und Fehlermeldungen sind nicht teilbar. In die Query gehören nur angewendete fachliche Filter.

Welche Zustände in die URL gehören und welche lokal in React bleiben

Zustand

Speicherort

Begründung

Angewendeter Suchtext

URLMuss teilbar und nach Reload wiederherstellbar sein

Angewendete Filter

URLBestimmen sichtbare Ergebnisse und Browserhistorie

Nicht abgesendete Eingaben

React StateSind noch kein akzeptierter Suchzustand

Geöffnetes Modal

React StateReiner Darstellungszustand ohne fachliche Bedeutung

Ladezustand und Fehlermeldung

React StateTemporäre Folge einer laufenden oder fehlgeschlagenen Anfrage

Ein generalisierter Vertrag sieht so aus:

interface SearchFilters {
  readonly searchText: string;
  readonly placeTypes: readonly number[];
  readonly areaMin: number | null;
  readonly areaMax: number | null;
}

interface FilterPayload {
  readonly lcid: 1031 | 1033;
  readonly searchText?: string;
  readonly placeTypes?: readonly number[];
  readonly areaMin?: number;
  readonly areaMax?: number;
}

interface SerializedSearchQuery {
  readonly queryString: string;
  readonly payload: FilterPayload;
  readonly filters: SearchFilters;
}

interface PreparedSearch {
  readonly filters: SearchFilters;
  readonly serializedSearch: SerializedSearchQuery;
}

Vor der Serialisierung trimme ich Text, entferne ungültige Werte, normalisiere Listen und lasse leere Filter weg.

function serializeSearchFilters(filters: SearchFilters, lcid: 1031 | 1033): SerializedSearchQuery {
  const query = new URLSearchParams();
  const searchText = filters.searchText.trim();
  const placeTypes = [...new Set(filters.placeTypes)].sort((left, right) => left - right);

  query.set('lcid', lcid.toString());

  if (searchText.length > 0) {
    query.set('searchText', searchText);
  }

  if (placeTypes.length > 0) {
    query.set('placeTypes', placeTypes.join(','));
  }

  if (filters.areaMin !== null) {
    query.set('areaMin', filters.areaMin.toString());
  }

  if (filters.areaMax !== null) {
    query.set('areaMax', filters.areaMax.toString());
  }

  return {
    queryString: query.toString(),
    payload: {
      lcid,
      ...(searchText.length > 0 ? { searchText } : {}),
      ...(placeTypes.length > 0 ? { placeTypes } : {}),
      ...(filters.areaMin !== null ? { areaMin: filters.areaMin } : {}),
      ...(filters.areaMax !== null ? { areaMax: filters.areaMax } : {}),
    },
    filters: {
      ...filters,
      searchText,
      placeTypes,
    },
  };
}

Die Browser API URLSearchParams übernimmt dabei das Encoding. Die fachliche Normalisierung bleibt Aufgabe meiner Anwendung.

Der Controller liefert Query String, normalisierte Filter und Backend Payload aus derselben Vorbereitung. Dadurch können URL und Request nicht unabhängig voneinander aus zwei leicht unterschiedlichen Zuständen entstehen. Der Beitrag über typisierte Verträge zwischen React und Dataverse beschreibt diese Controller Grenze im vollständigen Datenfluss.

Im vorherigen Beitrag hatte ich den vorbereiteten Vertrag auf payload und queryString reduziert. Im vollständigen Widget bündelt serializedSearch diese Werte zusammen mit den normalisierten Filtern.

Warum müssen fremde Query-Parameter erhalten bleiben?

Eine Power Pages URL gehört selten nur dem Suchwidget.

Sie kann Sprachparameter, Kampagnenwerte oder Kontextinformationen enthalten. Wenn die Suche bei jedem Absenden die vollständige Query ersetzt, gehen diese Werte verloren.

Ich verwalte deshalb eine feste Liste eigener Keys:

const SEARCH_QUERY_KEYS = ['searchText', 'placeTypes', 'areaMin', 'areaMax', 'lcid'] as const;

function mergeSearchQuery(currentQueryString: string, searchQueryString: string): string {
  const merged = new URLSearchParams(currentQueryString);
  const nextSearch = new URLSearchParams(searchQueryString);

  for (const key of SEARCH_QUERY_KEYS) {
    merged.delete(key);
  }

  for (const [key, value] of nextSearch.entries()) {
    merged.set(key, value);
  }

  return merged.toString();
}

Die Funktion entfernt nur die Parameter, die dem Suchwidget gehören. Danach setzt sie die vereinheitlichten Suchwerte.

Auf der Ergebnisseite beginnt dieser Merge mit der aktuellen URL. Ein vorhandenes utm_campaign oder ein Sprachparameter bleibt deshalb bestehen.

Bei der Landingpage beginnt der Merge mit der konfigurierten Ziel URL. Wenn der Zielpfad bereits einen Sprachparameter enthält, bleibt er erhalten. Query Parameter der aktuellen Landingpage werden nicht automatisch auf ein anderes Ziel kopiert.

Diese Unterscheidung verhindert eine falsche Annahme. Parameter bleiben nur erhalten, wenn sie Teil der URL sind, auf deren Basis der neue Zielzustand gebaut wird.

Wie aktualisiert React Power Pages Such-URLs mit pushState?

Eine akzeptierte Suche auf der Ergebnisseite ist ein neuer navigierbarer Zustand.

Dafür verwende ich pushState:

function updateCurrentSearchUrl(searchQueryString: string): string {
  const queryString = mergeSearchQuery(window.location.search, searchQueryString);
  const url = new URL(window.location.href);
  url.search = queryString;

  window.history.pushState(null, '', url);
  return url.toString();
}

Anschließend startet die Anwendung genau eine Suche mit dem vorbereiteten Vertrag:

function submitResultsSearch(nextFilters: SearchFilters): void {
  const preparation = controller.prepare(nextFilters);

  if (!preparation.isValid) {
    showValidationError(preparation.validationErrors[0] ?? 'Die Suchfilter sind ungültig.');
    return;
  }

  updateCurrentSearchUrl(preparation.prepared.serializedSearch.queryString);
  runPreparedSearch(preparation.prepared);
}

Ich speichere kein zweites Filterobjekt im History State. Der State Parameter bleibt null. Die URL ist bereits der wiederherstellbare Vertrag.

Die History API Dokumentation beschreibt, dass pushState einen neuen Eintrag in der Session History erzeugt, ohne die Seite vollständig neu zu laden.

Für die Suche bedeutet das: Jede akzeptierte neue Suche erhält einen eigenen Schritt für Vor und Zurück.

Nach einer URL Aktualisierung oder Reparatur veröffentlicht das Widget den akzeptierten Zustand. Der nächste Deep Dive über CustomEvents in Power Pages React Microfrontends trennt dieses Signal von der späteren Ergebnisantwort.

Wie liest eine Power Pages React Suche die initiale URL?

Beim Öffnen der Ergebnisseite muss React den bereits vorhandenen Query String lesen.

Der Controller parst bekannte Suchparameter bereits für den ersten Render. Die normalisierten Filter initialisieren den sichtbaren Formularzustand. Danach startet ein Effekt genau eine Suche mit demselben vorbereiteten Vertrag.

const initialPreparation = useMemo(
  () => (config.type === 'update-url' ? controller.prepareFromUrl(window.location.search) : null),
  [config.type, controller]
);

const urlInitialFilters =
  initialPreparation?.isValid === true ? initialPreparation.prepared.filters : EMPTY_SEARCH_FILTERS;

const filters = useSearchFilters(urlInitialFilters);
const initialSearchStarted = useRef(false);

useEffect(() => {
  if (config.type !== 'update-url' || initialSearchStarted.current) {
    return;
  }

  initialSearchStarted.current = true;

  if (initialPreparation?.isValid !== true) {
    showValidationError(initialPreparation?.validationErrors[0] ?? 'Die Suchparameter sind ungültig.');
    return;
  }

  if (initialPreparation.invalidParameters.length > 0) {
    replaceCurrentSearchUrl(initialPreparation.prepared.serializedSearch.queryString);
  }

  runPreparedSearch(initialPreparation.prepared);
}, [config, initialPreparation, runPreparedSearch]);

Die URL Vorbereitung liegt vor dem Effekt. Dadurch erscheint der wiederhergestellte Formularzustand bereits beim ersten Render. Die Ref verhindert anschließend, dass der initiale Effekt denselben Suchvorgang zweimal startet. Das ist besonders bei Entwicklungsumgebungen mit zusätzlichen React Effektprüfungen wichtig.

Fehlende optionale Parameter lösen nicht automatisch eine Reparatur aus. replaceState wird nur verwendet, wenn der Parser ungültige oder noch nicht vereinheitlichte bekannte Suchparameter meldet.

Wie repariert React Such-URLs mit replaceState?

Eine geteilte URL kann teilweise ungültig sein.

Zum Beispiel kann ein Listenparameter einen gültigen und einen unbekannten Wert enthalten:

?searchText=Berlin&placeTypes=1,unknown&utm_campaign=partner

Der Parser muss deshalb nicht die vollständige Suche ablehnen.

Er kann placeTypes=1 erhalten, unknown als ungültig markieren und den Kampagnenparameter unverändert lassen. Danach ersetzt die Anwendung den aktuellen History Eintrag durch die vereinheitlichte Form:

?utm_campaign=partner&lcid=1031&searchText=Berlin&placeTypes=1

Die Reparatur verwendet replaceState:

function replaceCurrentSearchUrl(searchQueryString: string): string {
  const queryString = mergeSearchQuery(window.location.search, searchQueryString);
  const url = new URL(window.location.href);
  url.search = queryString;

  window.history.replaceState(null, '', url);
  return url.toString();
}

replaceState erzeugt keinen zusätzlichen Schritt. Die Zurück Schaltfläche führt deshalb nicht zuerst zur ungültigen Variante derselben Suche.

Diese Normalisierung verbessert Wiederherstellung und Bedienung. Sie ist keine Sicherheitsgrenze. Clientseitige Query Werte bleiben veränderbar. Der Beitrag zur Power Pages Server Logic Sicherheitsgrenze zeigt, warum Autorisierung, Allowlisting und OData Aufbau weiterhin auf dem Server stattfinden.

Wie stellt popstate den Power Pages Suchzustand wieder her?

Der Browser stellt bei Vor und Zurück die URL wieder her. Er kennt aber nicht meinen React Context, die angewendeten Filter oder die sichtbare Ergebnisliste.

Deshalb registriert das Widget im Modus update-url einen popstate Listener:

useEffect(() => {
  if (config.type !== 'update-url') {
    return;
  }

  return addSearchPopStateListener(() => {
    const preparation = controller.prepareFromUrl(window.location.search);

    if (!preparation.isValid) {
      showValidationError(preparation.validationErrors[0] ?? 'Die Suchparameter sind ungültig.');
      return;
    }

    replaceAppliedFilters(preparation.prepared.filters);

    if (preparation.invalidParameters.length > 0) {
      replaceCurrentSearchUrl(preparation.prepared.serializedSearch.queryString);
    }

    runPreparedSearch(preparation.prepared);
  });
}, [config, controller, runPreparedSearch]);

Der Listener führt vier Aufgaben aus:

  1. Er liest die aktuelle Browser URL erneut.
  2. Er ersetzt den angewendeten React Zustand.
  3. Er repariert ungültige oder noch nicht vereinheitlichte bekannte Suchparameter bei Bedarf.
  4. Er startet genau eine Suche für den wiederhergestellten Zustand.

Die Dokumentation des popstate Events ist an dieser Stelle wichtig. popstate bedeutet nicht, dass die Anwendung ihren Zustand automatisch zurückerhält. Es ist das Signal, die neue aktive URL selbst auszuwerten.

Welche History-Operationen nutzt eine Power Pages React Suche?

Ich behandle Browserhistorie nicht als allgemeinen Seiteneffekt. Jede Operation besitzt einen klaren Anlass.

Browser Operationen für Navigation, neue Suchen und Wiederherstellung

Situation

Browser Operation

Verhalten

Absenden auf der Landingpage

location.assignZur Ergebnisseite navigieren, vorher keine vollständige Suche starten

Initialer Aufruf der Ergebnisseite

Optional replaceState

URL lesen, React Zustand setzen, ungültige Werte reparieren, genau einmal suchen

Neue Suche auf der Ergebnisseite

pushStateNeuen navigierbaren Zustand erzeugen und genau einmal suchen

Vor oder Zurück

popstate, optional replaceState

Aktuelle URL erneut lesen, Zustand ersetzen und genau einmal suchen

Diese Trennung verhindert zwei typische Fehler.

Der erste Fehler ist eine neue Suche mit replaceState. Dann kann der Portalnutzer nicht zur vorherigen Suche zurückkehren.

Der zweite Fehler ist eine URL Reparatur mit pushState. Dann erzeugt die Anwendung einen zusätzlichen History Schritt nur für die bereinigte Schreibweise.

Welchen Nutzen bietet ein URL-basierter Suchzustand in Power Pages?

Power Pages React URL Parameter sind für mich kein reines SEO Thema.

Sie bilden einen kleinen Vertrag zwischen Formular, Browserhistorie und Suchrequest. Dieser Vertrag macht eine komplexe Suche überprüfbar:

  1. Ein geteilter Link öffnet dieselben angewendeten Filter.
  2. Ein Reload stellt Zustand und Ergebnisse wieder her.
  3. Vor und Zurück wechseln zwischen vollständigen Suchzuständen.
  4. Ungültige Teilwerte zerstören nicht die gesamte Query.
  5. Fremde Parameter bleiben erhalten.
  6. Landingpage und Ergebnisseite verwenden dasselbe Bundle mit klar getrenntem Verhalten.

In der ECE Fallstudie zur Power Pages Enterprise-Suche zeige ich den Projektkontext, in dem dieses Zustandsmodell als Teil einer vollständigen React und Dataverse Architektur eingesetzt wurde.

Du planst eine Power Pages Suche, einen Konfigurator oder einen mehrstufigen Prozess mit komplexem URL Zustand? Der Bereich Power Pages Pro-Code zeigt, wie ich solche Frontends und Server Logic Verträge für produktive Portale aufbaue.

Ä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