From cfb3e76519b8a9facc2628a9453aa9b3fbec1b43 Mon Sep 17 00:00:00 2001 From: rameel Date: Mon, 7 Sep 2026 18:23:35 +0500 Subject: [PATCH] http: remove TriggerEvents --- README.md | 2 +- docs/articles/responses.md | 2 - src/Ramstack.HtmxToolkit/HtmxResponse.cs | 27 ++------ src/Ramstack.HtmxToolkit/PendingEvents.cs | 14 ++--- .../HtmxResponseTests.cs | 14 ----- .../PendingEventsTests.cs | 62 ++++++++----------- 6 files changed, 39 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 9df7e59..fd12ac5 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ The fluent API supports: - Client navigation with `Location`, `Redirect`, `PushUrl`, and `ReplaceUrl`. - Swap control with `Reswap`, `Retarget`, and `Reselect`. - Page refresh with `Refresh`. -- Client events with `TriggerEvent` and `TriggerEvents`. +- Client events with `TriggerEvent`. The same API works in Minimal API handlers: diff --git a/docs/articles/responses.md b/docs/articles/responses.md index 5fa4d5e..90ac330 100644 --- a/docs/articles/responses.md +++ b/docs/articles/responses.md @@ -130,8 +130,6 @@ Response.Htmx(htmx => htmx.TriggerEvent( ``` -Use `TriggerEvents` to add several event/detail pairs. Multiple Toolkit calls for the same response accumulate pending events. - HTMX 1.x and 2.x distinguish receive, after-swap, and after-settle response headers. HTMX 4.x delivers these Toolkit events through `HX-Trigger` when the request completes, after the swap when one occurs. See [Version compatibility](version-compatibility.md). diff --git a/src/Ramstack.HtmxToolkit/HtmxResponse.cs b/src/Ramstack.HtmxToolkit/HtmxResponse.cs index ecccc23..657ecaf 100644 --- a/src/Ramstack.HtmxToolkit/HtmxResponse.cs +++ b/src/Ramstack.HtmxToolkit/HtmxResponse.cs @@ -228,25 +228,9 @@ public HtmxResponse TriggerEvent(string eventName, object detail, HtmxTriggerTim return TriggerEventImpl(this, eventName, detail, timing); static HtmxResponse TriggerEventImpl(HtmxResponse response, string eventName, object detail, HtmxTriggerTiming timing) => - AddEvents(response, new Dictionary { [eventName] = detail }, timing); + AddEvent(response, eventName, detail, timing); } - /// - /// Adds client-side events to the response header selected by . - /// - /// - /// In HTMX 4.x, every value is emitted through HX-Trigger - /// and runs when the request completes (after the swap whenever one is performed). - /// See PR #3900. - /// - /// The event names and their associated details. - /// The event timing. Defaults to . - /// - /// The current instance. - /// - public HtmxResponse TriggerEvents(IReadOnlyDictionary events, HtmxTriggerTiming timing = HtmxTriggerTiming.Receive) => - AddEvents(this, events, timing); - /// /// Sets a response header and returns the response wrapper for fluent chaining. /// @@ -263,17 +247,18 @@ private static HtmxResponse SetHeader(HtmxResponse response, string key, string } /// - /// Adds pending client-side events and returns the response wrapper for fluent chaining. + /// Adds a pending client-side event and returns the response wrapper for fluent chaining. /// /// The response wrapper to update. - /// The event names and their associated details. + /// The event name. + /// The event detail. /// The time at which to trigger the events. /// /// The updated response wrapper. /// - private static HtmxResponse AddEvents(HtmxResponse response, IReadOnlyDictionary events, HtmxTriggerTiming timing) + private static HtmxResponse AddEvent(HtmxResponse response, string eventName, object detail, HtmxTriggerTiming timing) { - PendingEvents.GetOrCreate(response._response).AddEvents(timing, events); + PendingEvents.GetOrCreate(response._response).AddEvent(timing, eventName, detail); return response; } diff --git a/src/Ramstack.HtmxToolkit/PendingEvents.cs b/src/Ramstack.HtmxToolkit/PendingEvents.cs index 210c064..68a8bb5 100644 --- a/src/Ramstack.HtmxToolkit/PendingEvents.cs +++ b/src/Ramstack.HtmxToolkit/PendingEvents.cs @@ -32,13 +32,14 @@ private PendingEvents(HttpResponse response) => (_response, _version) = (response, GetTargetVersion(response)); /// - /// Adds the specified events to the pending set for . + /// Adds the specified event to the pending set for . /// When an event name already exists, the duplicate is stored under the /// rs:events key for client-side replay. /// /// The time at which to trigger the events. - /// The event names and their associated details. - public void AddEvents(HtmxTriggerTiming timing, IReadOnlyDictionary events) + /// The event name. + /// The event detail. + public void AddEvent(HtmxTriggerTiming timing, string eventName, object detail) { timing = NormalizeTiming(timing); @@ -49,18 +50,15 @@ public void AddEvents(HtmxTriggerTiming timing, IReadOnlyDictionary _afterSettle ??= new SmallDictionary(StringComparer.Ordinal) }; - foreach (var (k, v) in events) + if (!current.TryAdd(eventName, detail)) { - if (current.TryAdd(k, v)) - continue; - if (!current.TryGetValue(ProxyEventName, out var value) || value is not List> collection) { collection = []; current[ProxyEventName] = collection; } - collection.Add(new KeyValuePair(k, v)); + collection.Add(new KeyValuePair(eventName, detail)); } } diff --git a/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs b/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs index 4cc1511..c158de3 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs +++ b/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs @@ -222,20 +222,6 @@ public void TriggerEvent_WithComplexDetail_SerializesJson() Assert.That(json["message"].GetProperty("text").GetString(), Is.EqualTo("hi")); } - [Test] - public void TriggerEvents_SetsMultipleEvents() - { - var context = TestHelper.CreateHtmxRequestContext(); - context.Response.Htmx(r => r.TriggerEvents( - new Dictionary { ["a"] = 1, ["b"] = 2 }, - HtmxTriggerTiming.AfterSwap)); - - var events = context.Response.GetHtmxHeaders().TriggerAfterSwap!; - Assert.That(events.Count, Is.EqualTo(2)); - Assert.That(events["a"], Is.EqualTo(1)); - Assert.That(events["b"], Is.EqualTo(2)); - } - [Test] public void TriggerEvent_Htmx4_AddsEveryTimingToReceiveTrigger() { diff --git a/tests/Ramstack.HtmxToolkit.Tests/PendingEventsTests.cs b/tests/Ramstack.HtmxToolkit.Tests/PendingEventsTests.cs index cc82494..1f9006b 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/PendingEventsTests.cs +++ b/tests/Ramstack.HtmxToolkit.Tests/PendingEventsTests.cs @@ -6,14 +6,13 @@ namespace Ramstack.HtmxToolkit.Tests; public class PendingEventsTests { [Test] - public void AddEvents_StoresDistinctKeys() + public void AddEvent_StoresDistinctKeys() { var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents( - HtmxTriggerTiming.Receive, - CreateDictionary(("a", 1), ("b", 2))); + pending.AddEvent(HtmxTriggerTiming.Receive, "a", 1); + pending.AddEvent(HtmxTriggerTiming.Receive, "b", 2); var events = pending.GetEvents(HtmxTriggerTiming.Receive)!; @@ -23,18 +22,13 @@ public void AddEvents_StoresDistinctKeys() } [Test] - public void AddEvents_AccumulatesDuplicateKeys_UnderProxy() + public void AddEvent_AccumulatesDuplicateKeys_UnderProxy() { var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents( - HtmxTriggerTiming.Receive, - CreateDictionary(("message", "first"))); - - pending.AddEvents( - HtmxTriggerTiming.Receive, - CreateDictionary(("message", "second"))); + pending.AddEvent(HtmxTriggerTiming.Receive, "message", "first"); + pending.AddEvent(HtmxTriggerTiming.Receive, "message", "second"); var events = pending.GetEvents(HtmxTriggerTiming.Receive)!; @@ -44,14 +38,14 @@ public void AddEvents_AccumulatesDuplicateKeys_UnderProxy() } [Test] - public void AddEvents_TracksTimingsIndependently() + public void AddEvent_TracksTimingsIndependently() { var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents(HtmxTriggerTiming.Receive, CreateDictionary(("r", 1))); - pending.AddEvents(HtmxTriggerTiming.AfterSettle, CreateDictionary(("t", 3))); - pending.AddEvents(HtmxTriggerTiming.AfterSwap, CreateDictionary(("s", 2))); + pending.AddEvent(HtmxTriggerTiming.Receive, "r", 1); + pending.AddEvent(HtmxTriggerTiming.AfterSettle, "t", 3); + pending.AddEvent(HtmxTriggerTiming.AfterSwap, "s", 2); Assert.That(pending.GetEvents(HtmxTriggerTiming.Receive), Is.EqualTo(CreateDictionary(("r", 1)))); Assert.That(pending.GetEvents(HtmxTriggerTiming.AfterSettle), Is.EqualTo(CreateDictionary(("t", 3)))); @@ -60,14 +54,14 @@ public void AddEvents_TracksTimingsIndependently() [TestCase(HtmxTargetVersion.V1)] [TestCase(HtmxTargetVersion.V2)] - public void AddEvents_PriorVersions_WriteEachTimingToItsOwnHeader(HtmxTargetVersion targetVersion) + public void AddEvent_PriorVersions_WriteEachTimingToItsOwnHeader(HtmxTargetVersion targetVersion) { var context = TestHelper.CreateHtmxRequestContext(targetVersion); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents(HtmxTriggerTiming.Receive, CreateDictionary(("received", 1))); - pending.AddEvents(HtmxTriggerTiming.AfterSwap, CreateDictionary(("swapped", 2))); - pending.AddEvents(HtmxTriggerTiming.AfterSettle, CreateDictionary(("settled", 3))); + pending.AddEvent(HtmxTriggerTiming.Receive, "received", 1); + pending.AddEvent(HtmxTriggerTiming.AfterSwap, "swapped", 2); + pending.AddEvent(HtmxTriggerTiming.AfterSettle, "settled", 3); pending.Flush(); Assert.Multiple(() => @@ -87,14 +81,14 @@ public void AddEvents_PriorVersions_WriteEachTimingToItsOwnHeader(HtmxTargetVers } [Test] - public void AddEvents_Htmx4_NormalizesAllTimingsToReceive() + public void AddEvent_Htmx4_NormalizesAllTimingsToReceive() { var context = TestHelper.CreateHtmxRequestContext(HtmxTargetVersion.V4); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents(HtmxTriggerTiming.Receive, CreateDictionary(("received", 1))); - pending.AddEvents(HtmxTriggerTiming.AfterSwap, CreateDictionary(("swapped", 2))); - pending.AddEvents(HtmxTriggerTiming.AfterSettle, CreateDictionary(("settled", 3))); + pending.AddEvent(HtmxTriggerTiming.Receive, "received", 1); + pending.AddEvent(HtmxTriggerTiming.AfterSwap, "swapped", 2); + pending.AddEvent(HtmxTriggerTiming.AfterSettle, "settled", 3); var events = pending.GetEvents(HtmxTriggerTiming.Receive)!; pending.Flush(); @@ -115,14 +109,14 @@ public void AddEvents_Htmx4_NormalizesAllTimingsToReceive() } [Test] - public void AddEvents_Htmx4_PreservesDuplicatesAcrossRequestedTimings() + public void AddEvent_Htmx4_PreservesDuplicatesAcrossRequestedTimings() { var context = TestHelper.CreateHtmxRequestContext(HtmxTargetVersion.V4); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents(HtmxTriggerTiming.Receive, CreateDictionary(("message", "first"))); - pending.AddEvents(HtmxTriggerTiming.AfterSwap, CreateDictionary(("message", "second"))); - pending.AddEvents(HtmxTriggerTiming.AfterSettle, CreateDictionary(("message", "third"))); + pending.AddEvent(HtmxTriggerTiming.Receive, "message", "first"); + pending.AddEvent(HtmxTriggerTiming.AfterSwap, "message", "second"); + pending.AddEvent(HtmxTriggerTiming.AfterSettle, "message", "third"); var events = pending.GetEvents(HtmxTriggerTiming.Receive)!; @@ -154,7 +148,7 @@ public void SetEvents_ReplacesExisting() var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents(HtmxTriggerTiming.Receive, CreateDictionary(("old", 1))); + pending.AddEvent(HtmxTriggerTiming.Receive, "old", 1); pending.SetEvents(HtmxTriggerTiming.Receive, CreateDictionary(("new", 2))); Assert.That( @@ -168,9 +162,7 @@ public void Flush_WritesCamelCaseJsonToHeaders() var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents( - HtmxTriggerTiming.Receive, - CreateDictionary(("message", "hello"))); + pending.AddEvent(HtmxTriggerTiming.Receive, "message", "hello"); pending.Flush(); @@ -185,9 +177,7 @@ public void Flush_WritesOnlyRegisteredTimings() var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents( - HtmxTriggerTiming.AfterSwap, - CreateDictionary(("swapped", true))); + pending.AddEvent(HtmxTriggerTiming.AfterSwap, "swapped", true); pending.Flush(); @@ -202,7 +192,7 @@ public void Flush_SerializesNullEventDetail_AsNull() var context = TestHelper.CreateHttpContext(); var pending = PendingEvents.GetOrCreate(context.Response); - pending.AddEvents(HtmxTriggerTiming.Receive, CreateDictionary(("e", null!))); + pending.AddEvent(HtmxTriggerTiming.Receive, "e", null!); pending.Flush(); var header = context.Response.Headers[HtmxResponseHeaderNames.Trigger].ToString();