From 9fe5a21f10311540c452b5efa93f87fd088ecbb3 Mon Sep 17 00:00:00 2001 From: rameel Date: Thu, 29 Oct 2026 00:06:01 +0500 Subject: [PATCH] config: expose collection options as read-only lists ResponseHandling was an IList because a removed Tag Helper appended rules to it, while the other collection options were arrays. All five are now IReadOnlyList. Configuration JSON is cached until a property is assigned, so adding or replacing items in place was silently ignored after the first render. A read-only list rules that out at compile time, and arrays, lists, and collection expressions can still be assigned. --- docs/articles/migrate-to-v2.md | 2 +- src/Ramstack.HtmxToolkit/Configuration/HtmxConfig.cs | 3 ++- src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs | 6 +++--- src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs | 8 ++++---- src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs | 4 ++-- ...ArrayJsonConverter.cs => HttpVerbListJsonConverter.cs} | 8 ++++---- 6 files changed, 16 insertions(+), 15 deletions(-) rename src/Ramstack.HtmxToolkit/Serialization/{HttpVerbArrayJsonConverter.cs => HttpVerbListJsonConverter.cs} (60%) diff --git a/docs/articles/migrate-to-v2.md b/docs/articles/migrate-to-v2.md index 931a336..41efa6b 100644 --- a/docs/articles/migrate-to-v2.md +++ b/docs/articles/migrate-to-v2.md @@ -116,7 +116,7 @@ on `HtmxV1Config` or `HtmxV2Config`. The exceptions are: | `include-antiforgery-token` | `HtmxToolkitOptions.IncludeAntiforgeryToken`, now `true` by default; see the next section | | `use-template-fragments` | `HtmxV1Config.UseTemplateFragments` only; HTMX 2.x always parses responses with templates | | `trigger-specs-cache` | `TriggerSpecsCacheEnabled` (`bool?`) | -| `methods-that-use-url-params` | `MethodsThatUseUrlParams` (`HttpVerb[]` instead of `string[]`) | +| `methods-that-use-url-params` | `MethodsThatUseUrlParams` (`IReadOnlyList` instead of `string[]`) | | `ws-reconnect-delay` | Removed: HTMX accepts only a function or the default `"full-jitter"`, and a function cannot be passed through JSON | For every property, see [HTMX 1.9.x](configuration-v1.md), [HTMX 2.x](configuration-v2.md), diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxConfig.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxConfig.cs index a028a24..b4ea99c 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxConfig.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxConfig.cs @@ -9,7 +9,8 @@ namespace Ramstack.HtmxToolkit.Configuration; /// /// /// Configuration JSON is cached until a configuration property is assigned. -/// Changes made in place to arrays, collections, or their items do not invalidate the cache; +/// Collection properties are read-only lists, so replace a collection by assigning the property. +/// Changes made in place to an assigned collection or its items do not invalidate the cache; /// reassign the containing property after making such changes. /// public abstract class HtmxConfig diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs index f398d84..f856591 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxV1Config.cs @@ -174,7 +174,7 @@ public string? InlineScriptNonce /// Gets or sets the attributes to settle during the settling phase. /// The HTMX default is ["class", "style", "width", "height"]. /// - public string[]? AttributesToSettle + public IReadOnlyList? AttributesToSettle { get; set => SetField(ref field, value); @@ -288,8 +288,8 @@ public bool? GlobalViewTransitions /// Gets or sets the HTTP methods that use URL parameters. /// The HTMX default is ["get"]. /// - [JsonConverter(typeof(HttpVerbArrayJsonConverter))] - public HttpVerb[]? MethodsThatUseUrlParams + [JsonConverter(typeof(HttpVerbListJsonConverter))] + public IReadOnlyList? MethodsThatUseUrlParams { get; set => SetField(ref field, value); diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs index 67c4f24..c52308d 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxV2Config.cs @@ -184,7 +184,7 @@ public string? InlineStyleNonce /// Gets or sets the attributes to settle during the settling phase. /// The HTMX default is ["class", "style", "width", "height"]. /// - public string[]? AttributesToSettle + public IReadOnlyList? AttributesToSettle { get; set => SetField(ref field, value); @@ -300,8 +300,8 @@ public bool? GlobalViewTransitions /// Gets or sets the HTTP methods that use URL parameters. /// The HTMX default is ["get", "delete"]. /// - [JsonConverter(typeof(HttpVerbArrayJsonConverter))] - public HttpVerb[]? MethodsThatUseUrlParams + [JsonConverter(typeof(HttpVerbListJsonConverter))] + public IReadOnlyList? MethodsThatUseUrlParams { get; set => SetField(ref field, value); @@ -344,7 +344,7 @@ public bool? TriggerSpecsCacheEnabled /// The HTMX default is [{ code: "204", swap: false }, { code: "[23]..", swap: true }, /// { code: "[45]..", swap: false, error: true }]. /// - public IList? ResponseHandling + public IReadOnlyList? ResponseHandling { get; set => SetField(ref field, value); diff --git a/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs b/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs index faf085e..578dd2b 100644 --- a/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs +++ b/src/Ramstack.HtmxToolkit/Configuration/HtmxV4Config.cs @@ -197,7 +197,7 @@ public bool? Transitions /// Gets or sets the attribute name prefixes to preserve during morphing. /// The HTMX default is ["data-htmx-powered"]. /// - public string[]? MorphIgnore + public IReadOnlyList? MorphIgnore { get; set => SetField(ref field, value); @@ -241,7 +241,7 @@ public int? MorphScanLimit /// Although HTMX declares this option as a number array, it converts each entry to a string /// at runtime and supports wildcard patterns such as "4xx" and "44x". /// - public string[]? NoSwap + public IReadOnlyList? NoSwap { get; set => SetField(ref field, value); diff --git a/src/Ramstack.HtmxToolkit/Serialization/HttpVerbArrayJsonConverter.cs b/src/Ramstack.HtmxToolkit/Serialization/HttpVerbListJsonConverter.cs similarity index 60% rename from src/Ramstack.HtmxToolkit/Serialization/HttpVerbArrayJsonConverter.cs rename to src/Ramstack.HtmxToolkit/Serialization/HttpVerbListJsonConverter.cs index 51efde3..a0b5535 100644 --- a/src/Ramstack.HtmxToolkit/Serialization/HttpVerbArrayJsonConverter.cs +++ b/src/Ramstack.HtmxToolkit/Serialization/HttpVerbListJsonConverter.cs @@ -7,16 +7,16 @@ namespace Ramstack.HtmxToolkit.Serialization; /// -/// Represents a for arrays of values. +/// Represents a for lists of values. /// -internal sealed class HttpVerbArrayJsonConverter : JsonConverter +internal sealed class HttpVerbListJsonConverter : JsonConverter> { /// - public override HttpVerb[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + public override IReadOnlyList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotSupportedException(); /// - public override void Write(Utf8JsonWriter writer, HttpVerb[] value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, IReadOnlyList value, JsonSerializerOptions options) { // NOTE: value is never null here: null-valued properties are omitted // by JsonIgnoreCondition.WhenWritingNull before this converter is invoked.