diff --git a/docs/articles/tag-helpers.md b/docs/articles/tag-helpers.md
index 8ec994d..04fc13d 100644
--- a/docs/articles/tag-helpers.md
+++ b/docs/articles/tag-helpers.md
@@ -245,7 +245,7 @@ Use `hx-request-*` attributes rather than hand-writing version-specific JSON:
asp-page="/Reports"
asp-page-handler="Preview"
hx-request-timeout="2000"
- hx-request-credentials="@HtmxRequestCredentials.SameOrigin"
+ hx-request-credentials="SameOrigin"
hx-target="#preview">
Generate preview
@@ -275,6 +275,11 @@ The output depends on the configured HTMX target:
Unsupported properties are omitted from the generated JSON. In particular, `HtmxRequestCredentials.Omit`
cannot be represented by HTMX 1.9.x or 2.x and is omitted for those targets.
+Enum attributes take a member name, such as `hx-request-credentials="Include"`. A C# expression starts with `@`,
+for example `hx-request-credentials="@credentials"`; `HtmxRequestCredentials.Include` without `@` does not compile.
+`hx-request-no-headers` and `hx-request-validate` can be written without a value, which means `true`.
+To leave an option unset, omit its attribute; these attributes do not accept `null`.
+
An element with `hx-request-*` attributes cannot also have a hand-written `hx-request` for HTMX 1.9.x and 2.x
or `hx-config` for HTMX 4.x, with or without modifiers or the `data-hx-` prefix.
The Tag Helper throws `InvalidOperationException` rather than lose one of the values.
@@ -283,8 +288,8 @@ Use `HtmxRequestCache` and `HtmxRequestRedirect` for the HTMX 4.x Fetch options:
```razor
```
diff --git a/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RequestOptions.cshtml b/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RequestOptions.cshtml
index 0617195..8d019af 100644
--- a/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RequestOptions.cshtml
+++ b/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RequestOptions.cshtml
@@ -22,8 +22,8 @@
asp-page="/Examples/RequestOptions"
asp-page-handler="Preview"
hx-request-timeout="2000"
- hx-request-cache="@HtmxRequestCache.NoCache"
- hx-request-redirect="@HtmxRequestRedirect.Error"
+ hx-request-cache="NoCache"
+ hx-request-redirect="Error"
hx-target="#preview-result">
Generate report preview
diff --git a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs
index d6c46bf..5d24703 100644
--- a/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs
+++ b/src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs
@@ -111,11 +111,15 @@ public int? Timeout
/// In HTMX 4.x, this property maps to the string credentials option
/// of hx-config.
///
+ ///
+ /// If the property has not been set, the option is not rendered, HTMX uses its default value,
+ /// and the property returns .
+ ///
///
[HtmlAttributeName(RequestCredentialsAttributeName)]
- public HtmxRequestCredentials? Credentials
+ public HtmxRequestCredentials Credentials
{
- get => _request.Credentials;
+ get => _request.Credentials.GetValueOrDefault();
set => _request.Credentials = value;
}
@@ -127,33 +131,49 @@ public HtmxRequestCredentials? Credentials
/// Supported only in HTMX 1.9.x and 2.x.
/// Headers added by the browser are unaffected.
/// The Toolkit script also omits the HX-Request-Type header.
+ ///
+ /// If the property has not been set, the option is not rendered, HTMX uses its default value,
+ /// and the property returns .
+ ///
///
[HtmlAttributeName(RequestNoHeadersAttributeName)]
- public bool? NoHeaders
+ public bool NoHeaders
{
- get => _request.NoHeaders;
+ get => _request.NoHeaders.GetValueOrDefault();
set => _request.NoHeaders = value;
}
///
/// Gets or sets the Fetch cache mode for the request.
///
- /// Supported only in HTMX 4.x.
+ ///
+ /// Supported only in HTMX 4.x.
+ ///
+ /// If the property has not been set, the option is not rendered, HTMX uses its default value,
+ /// and the property returns .
+ ///
+ ///
[HtmlAttributeName(RequestCacheAttributeName)]
- public HtmxRequestCache? Cache
+ public HtmxRequestCache Cache
{
- get => _request.Cache;
+ get => _request.Cache.GetValueOrDefault();
set => _request.Cache = value;
}
///
/// Gets or sets the Fetch redirect mode for the request.
///
- /// Supported only in HTMX 4.x.
+ ///
+ /// Supported only in HTMX 4.x.
+ ///
+ /// If the property has not been set, the option is not rendered, HTMX uses its default value,
+ /// and the property returns .
+ ///
+ ///
[HtmlAttributeName(RequestRedirectAttributeName)]
- public HtmxRequestRedirect? Redirect
+ public HtmxRequestRedirect Redirect
{
- get => _request.Redirect;
+ get => _request.Redirect.GetValueOrDefault();
set => _request.Redirect = value;
}
@@ -194,11 +214,17 @@ public string? Integrity
///
/// Gets or sets a value indicating whether the form is validated before submission.
///
- /// Supported only in HTMX 4.x.
+ ///
+ /// Supported only in HTMX 4.x.
+ ///
+ /// If the property has not been set, the option is not rendered, HTMX uses its default value,
+ /// and the property returns .
+ ///
+ ///
[HtmlAttributeName(RequestValidateAttributeName)]
- public bool? Validate
+ public bool Validate
{
- get => _request.Validate;
+ get => _request.Validate.GetValueOrDefault();
set => _request.Validate = value;
}
@@ -240,16 +266,16 @@ internal sealed class HtmxRequestData
///
public int? Timeout { get; set; }
- ///
+ ///
public HtmxRequestCredentials? Credentials { get; set; }
- ///
+ ///
public bool? NoHeaders { get; set; }
- ///
+ ///
public HtmxRequestCache? Cache { get; set; }
- ///
+ ///
public HtmxRequestRedirect? Redirect { get; set; }
///
@@ -258,7 +284,7 @@ internal sealed class HtmxRequestData
///
public string? Integrity { get; set; }
- ///
+ ///
public bool? Validate { get; set; }
}
diff --git a/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxRequestTagHelperTests.cs b/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxRequestTagHelperTests.cs
index 1c2b8dd..f25921f 100644
--- a/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxRequestTagHelperTests.cs
+++ b/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxRequestTagHelperTests.cs
@@ -169,20 +169,30 @@ public async Task ProcessAsync_SerializesHtmx4RedirectMode(HtmxRequestRedirect r
Assert.That(output.Attributes["hx-request"], Is.Null);
}
+ [TestCase(HtmxTargetVersion.V1, "hx-request")]
+ [TestCase(HtmxTargetVersion.V2, "hx-request")]
+ [TestCase(HtmxTargetVersion.V4, "hx-config")]
+ public async Task ProcessAsync_OmitsUnsetOptions(HtmxTargetVersion version, string attributeName)
+ {
+ var output = TestHelper.CreateTagHelperOutput();
+ var helper = CreateHelper(version);
+ helper.Timeout = 500;
+
+ await helper.ProcessAsync(TestHelper.CreateTagHelperContext(), output);
+
+ Assert.That(output.Attributes[attributeName]!.Value.ToString(), Is.EqualTo("{\"timeout\":500}"));
+ }
+
[Test]
- public async Task ProcessAsync_Htmx4_OmitsClearedCacheAndRedirectModes()
+ public async Task ProcessAsync_Htmx4_SerializesValidateWhenFalse()
{
var output = TestHelper.CreateTagHelperOutput();
var helper = CreateHelper(HtmxTargetVersion.V4);
- helper.Timeout = 500;
- helper.Cache = HtmxRequestCache.Default;
- helper.Redirect = HtmxRequestRedirect.Follow;
- helper.Cache = null;
- helper.Redirect = null;
+ helper.Validate = false;
await helper.ProcessAsync(TestHelper.CreateTagHelperContext(), output);
- Assert.That(output.Attributes["hx-config"]!.Value.ToString(), Is.EqualTo("{\"timeout\":500}"));
+ Assert.That(output.Attributes["hx-config"]!.Value.ToString(), Is.EqualTo("{\"validate\":false}"));
}
[Test]