Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/articles/tag-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
</button>
Expand Down Expand Up @@ -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.
Expand All @@ -283,8 +288,8 @@ Use `HtmxRequestCache` and `HtmxRequestRedirect` for the HTMX 4.x Fetch options:

```razor
<button hx-get="/Reports/Preview"
hx-request-cache="@HtmxRequestCache.NoCache"
hx-request-redirect="@HtmxRequestRedirect.Error">
hx-request-cache="NoCache"
hx-request-redirect="Error">
Generate preview
</button>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
</button>
Expand Down
62 changes: 44 additions & 18 deletions src/Ramstack.HtmxToolkit/TagHelpers/HtmxRequestTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,15 @@ public int? Timeout
/// In HTMX 4.x, this property maps to the string <c>credentials</c> option
/// of <c>hx-config</c>.
/// </para>
/// <para>
/// If the property has not been set, the option is not rendered, HTMX uses its default value,
/// and the property returns <see cref="HtmxRequestCredentials.SameOrigin" />.
/// </para>
/// </remarks>
[HtmlAttributeName(RequestCredentialsAttributeName)]
public HtmxRequestCredentials? Credentials
public HtmxRequestCredentials Credentials
{
get => _request.Credentials;
get => _request.Credentials.GetValueOrDefault();
set => _request.Credentials = value;
}

Expand All @@ -127,33 +131,49 @@ public HtmxRequestCredentials? Credentials
/// <para>Supported only in HTMX 1.9.x and 2.x.</para>
/// <para>Headers added by the browser are unaffected.</para>
/// <para>The Toolkit script also omits the <c>HX-Request-Type</c> header.</para>
/// <para>
/// If the property has not been set, the option is not rendered, HTMX uses its default value,
/// and the property returns <see langword="false" />.
/// </para>
/// </remarks>
[HtmlAttributeName(RequestNoHeadersAttributeName)]
public bool? NoHeaders
public bool NoHeaders
{
get => _request.NoHeaders;
get => _request.NoHeaders.GetValueOrDefault();
set => _request.NoHeaders = value;
}

/// <summary>
/// Gets or sets the Fetch cache mode for the request.
/// </summary>
/// <remarks>Supported only in HTMX 4.x.</remarks>
/// <remarks>
/// <para>Supported only in HTMX 4.x.</para>
/// <para>
/// If the property has not been set, the option is not rendered, HTMX uses its default value,
/// and the property returns <see cref="HtmxRequestCache.Default" />.
/// </para>
/// </remarks>
[HtmlAttributeName(RequestCacheAttributeName)]
public HtmxRequestCache? Cache
public HtmxRequestCache Cache
{
get => _request.Cache;
get => _request.Cache.GetValueOrDefault();
set => _request.Cache = value;
}

/// <summary>
/// Gets or sets the Fetch redirect mode for the request.
/// </summary>
/// <remarks>Supported only in HTMX 4.x.</remarks>
/// <remarks>
/// <para>Supported only in HTMX 4.x.</para>
/// <para>
/// If the property has not been set, the option is not rendered, HTMX uses its default value,
/// and the property returns <see cref="HtmxRequestRedirect.Follow" />.
/// </para>
/// </remarks>
[HtmlAttributeName(RequestRedirectAttributeName)]
public HtmxRequestRedirect? Redirect
public HtmxRequestRedirect Redirect
{
get => _request.Redirect;
get => _request.Redirect.GetValueOrDefault();
set => _request.Redirect = value;
}

Expand Down Expand Up @@ -194,11 +214,17 @@ public string? Integrity
/// <summary>
/// Gets or sets a value indicating whether the form is validated before submission.
/// </summary>
/// <remarks>Supported only in HTMX 4.x.</remarks>
/// <remarks>
/// <para>Supported only in HTMX 4.x.</para>
/// <para>
/// If the property has not been set, the option is not rendered, HTMX uses its default value,
/// and the property returns <see langword="false" />.
/// </para>
/// </remarks>
[HtmlAttributeName(RequestValidateAttributeName)]
public bool? Validate
public bool Validate
{
get => _request.Validate;
get => _request.Validate.GetValueOrDefault();
set => _request.Validate = value;
}

Expand Down Expand Up @@ -240,16 +266,16 @@ internal sealed class HtmxRequestData
/// <inheritdoc cref="HtmxRequestTagHelper.Timeout" />
public int? Timeout { get; set; }

/// <inheritdoc cref="HtmxRequestTagHelper.Credentials" />
/// <inheritdoc cref="HtmxRequestTagHelper.Credentials" path="/summary" />
public HtmxRequestCredentials? Credentials { get; set; }

/// <inheritdoc cref="HtmxRequestTagHelper.NoHeaders" />
/// <inheritdoc cref="HtmxRequestTagHelper.NoHeaders" path="/summary" />
public bool? NoHeaders { get; set; }

/// <inheritdoc cref="HtmxRequestTagHelper.Cache" />
/// <inheritdoc cref="HtmxRequestTagHelper.Cache" path="/summary" />
public HtmxRequestCache? Cache { get; set; }

/// <inheritdoc cref="HtmxRequestTagHelper.Redirect" />
/// <inheritdoc cref="HtmxRequestTagHelper.Redirect" path="/summary" />
public HtmxRequestRedirect? Redirect { get; set; }

/// <inheritdoc cref="HtmxRequestTagHelper.Referrer" />
Expand All @@ -258,7 +284,7 @@ internal sealed class HtmxRequestData
/// <inheritdoc cref="HtmxRequestTagHelper.Integrity" />
public string? Integrity { get; set; }

/// <inheritdoc cref="HtmxRequestTagHelper.Validate" />
/// <inheritdoc cref="HtmxRequestTagHelper.Validate" path="/summary" />
public bool? Validate { get; set; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Loading