From d31b508359a6034d7fb96196d7a9c43db7da4d7c Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 11 Jul 2026 21:33:07 -0400 Subject: [PATCH] WIP on htmx 4 integration (#60) --- src/MyWebLog/Handlers/Helpers.fs | 26 +++++++++++++++-------- src/MyWebLog/Views/Admin.fs | 18 +++++++++------- src/MyWebLog/Views/Helpers.fs | 22 +++++++++---------- src/MyWebLog/Views/Page.fs | 22 ++++++++----------- src/MyWebLog/Views/Post.fs | 36 +++++++++++++++----------------- src/MyWebLog/Views/User.fs | 11 +++++----- src/MyWebLog/Views/WebLog.fs | 5 +++-- 7 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/MyWebLog/Handlers/Helpers.fs b/src/MyWebLog/Handlers/Helpers.fs index 71f06a2..79c22ad 100644 --- a/src/MyWebLog/Handlers/Helpers.fs +++ b/src/MyWebLog/Handlers/Helpers.fs @@ -107,7 +107,7 @@ let isHtmx (ctx: HttpContext) = ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh /// Convert messages to headers (used for htmx responses) -let messagesToHeaders (messages: UserMessage array) : HttpHandler = +let messagesToHeaders (messages: UserMessage array) pushUrl : HttpHandler = seq { yield! messages @@ -116,7 +116,10 @@ let messagesToHeaders (messages: UserMessage array) : HttpHandler = | Some detail -> $"{m.Level}|||{m.Message}|||{detail}" | None -> $"{m.Level}|||{m.Message}" |> setHttpHeader "X-Message") - withHxNoPushUrl + match pushUrl with + | Some true -> withHxPushUrl "true" + | Some false -> withHxNoPushUrl + | None -> () } |> Seq.reduce (>=>) @@ -155,7 +158,7 @@ module Error = { UserMessage.Error with Message = $"You are not authorized to access the URL {ctx.Request.Path.Value}" } |] - (messagesToHeaders messages >=> setStatusCode 401) earlyReturn ctx + (messagesToHeaders messages (Some false) >=> setStatusCode 401) earlyReturn ctx else setStatusCode 401 earlyReturn ctx /// Handle 404s @@ -165,14 +168,14 @@ module Error = let messages = [| { UserMessage.Error with Message = $"The URL {ctx.Request.Path.Value} was not found" } |] - RequestErrors.notFound (messagesToHeaders messages) earlyReturn ctx + RequestErrors.notFound (messagesToHeaders messages (Some false)) earlyReturn ctx else RequestErrors.NOT_FOUND "Not found" earlyReturn ctx) let server message : HttpHandler = handleContext (fun ctx -> if isHtmx ctx then let messages = [| { UserMessage.Error with Message = message } |] - ServerErrors.internalError (messagesToHeaders messages) earlyReturn ctx + ServerErrors.internalError (messagesToHeaders messages (Some false)) earlyReturn ctx else ServerErrors.INTERNAL_ERROR message earlyReturn ctx) @@ -212,8 +215,8 @@ let bareForTheme themeId template next ctx viewCtx = task { match! Template.Cache.get themeId "layout-bare" ctx.Data with | Ok layoutTemplate -> return! - (messagesToHeaders completeCtx.Messages >=> htmlString (Template.render layoutTemplate completeCtx ctx.Data)) - next ctx + (messagesToHeaders completeCtx.Messages (Some false) + >=> htmlString (Template.render layoutTemplate completeCtx ctx.Data)) next ctx | Error message -> return! Error.server message next ctx | Error message -> return! Error.server message next ctx } @@ -228,7 +231,12 @@ let adminPage pageTitle next ctx (content: AppViewContext -> XmlNode list) = tas let! messages = getCurrentMessages ctx let appCtx = generateViewContext messages (viewCtxForPage pageTitle) ctx let layout = if isHtmx ctx then Layout.partial else Layout.full - return! htmlString (layout content appCtx |> RenderView.AsString.htmlDocument) next ctx + let returnFunc html = + if isHtmx ctx && messages.Length > 0 then + messagesToHeaders messages None >=> htmlString html + else + htmlString html + return! (layout content appCtx |> RenderView.AsString.htmlDocument |> returnFunc) next ctx } /// Display a bare page for an admin endpoint @@ -236,7 +244,7 @@ let adminBarePage pageTitle next ctx (content: AppViewContext -> XmlNode list) = let! messages = getCurrentMessages ctx let appCtx = generateViewContext messages (viewCtxForPage pageTitle) ctx return! - ( messagesToHeaders appCtx.Messages + ( messagesToHeaders appCtx.Messages (Some false) >=> htmlString (Layout.bare content appCtx |> RenderView.AsString.htmlDocument)) next ctx } diff --git a/src/MyWebLog/Views/Admin.fs b/src/MyWebLog/Views/Admin.fs index 7718cbe..87ef2f0 100644 --- a/src/MyWebLog/Views/Admin.fs +++ b/src/MyWebLog/Views/Admin.fs @@ -45,7 +45,8 @@ let dashboard (themes: Theme list) app = [ article [] [ fieldset [ _class "container mb-3 pb-0" ] [ legend [] [ raw "Themes" ] - span [ _hxGet (relUrl app "admin/theme/list"); _hxTrigger HxTrigger.Load; _hxSwap HxSwap.OuterHtml ] [] + span [ _hxGet (relUrl app "admin/theme/list"); _hxTrigger HxTrigger.Load; _hxSwap HxSwap.OuterHtml + _hxTarget "this" ] [] ] fieldset [ _class "container mb-3 pb-0" ] [ legend [] [ raw "Caches" ] @@ -65,8 +66,8 @@ let dashboard (themes: Theme list) app = [ raw "These caches include the page list and categories for each web log" ] let webLogUrl = $"{cacheBaseUrl}web-log/" - form [ _method "post"; _class "container g-0"; _hxNoBoost; _hxTarget "body" - _hxSwap $"{HxSwap.InnerHtml} show:window:top" ] [ + form [ _method "post"; _class "container g-0"; hxInherited (_hxTarget "body") + hxInherited (_hxSwap $"{HxSwap.InnerHtml} show:top showTarget:window") ] [ antiCsrf app button [ _type "submit"; _class "btn btn-sm btn-primary mb-2" _hxPost $"{webLogUrl}all/refresh" ] [ @@ -86,8 +87,8 @@ let dashboard (themes: Theme list) app = [ raw "The theme template cache is filled on demand as pages are displayed; " raw "refreshing a theme with no cached templates will still refresh its asset cache" ] - form [ _method "post"; _class "container g-0"; _hxNoBoost; _hxTarget "body" - _hxSwap $"{HxSwap.InnerHtml} show:window:top" ] [ + form [ _method "post"; _class "container g-0" + hxInherited (_hxSwap $"{HxSwap.InnerHtml} show:top showTarget:window") ] [ antiCsrf app button [ _type "submit"; _class "btn btn-sm btn-primary mb-2" _hxPost $"{themeCacheUrl}all/refresh" ] [ @@ -111,8 +112,9 @@ let themeList (model: DisplayTheme list) app = let themeCol = "col-12 col-md-6" let slugCol = "d-none d-md-block col-md-3" let tmplCol = "d-none d-md-block col-md-3" + let newUrl = relUrl app "admin/theme/new" div [ _id "theme_panel" ] [ - a [ _href (relUrl app "admin/theme/new"); _class "btn btn-primary btn-sm mb-3"; _hxTarget "#theme_new" ] [ + a [ _href newUrl; _hxGet newUrl; _class "btn btn-primary btn-sm mb-3"; _hxTarget "#theme_new" ] [ raw "Upload a New Theme" ] div [ _class "container g-0" ] [ @@ -124,7 +126,7 @@ let themeList (model: DisplayTheme list) app = ] div [ _class "row mwl-table-detail"; _id "theme_new" ] [] form [ _method "post"; _id "themeList"; _class "container g-0"; _hxTarget "#theme_panel" - _hxSwap $"{HxSwap.OuterHtml} show:window:top" ] [ + _hxSwap $"{HxSwap.OuterHtml} show:top showTarget:window" ] [ antiCsrf app for theme in model do let url = relUrl app $"admin/theme/{theme.Id}" @@ -161,7 +163,7 @@ let themeUpload app = div [ _class "col" ] [ h5 [ _class "mt-2" ] [ raw app.PageTitle ] form [ _action (relUrl app "admin/theme/new"); _method "post"; _class "container" - _enctype "multipart/form-data"; _hxNoBoost ] [ + _enctype "multipart/form-data" ] [ antiCsrf app div [ _class "row " ] [ div [ _class "col-12 col-sm-6 pb-3" ] [ diff --git a/src/MyWebLog/Views/Helpers.fs b/src/MyWebLog/Views/Helpers.fs index 6808c92..29a8c02 100644 --- a/src/MyWebLog/Views/Helpers.fs +++ b/src/MyWebLog/Views/Helpers.fs @@ -203,18 +203,18 @@ module Layout = /// Create a navigation link let private navLink app name url = let extraPath = app.WebLog.ExtraPath - let path = if extraPath = "" then "" else $"{extraPath[1..]}/" - let active = if app.CurrentPage.StartsWith $"{path}{url}" then " active" else "" - li [ _class "nav-item" ] [ - a [ _class $"nav-link{active}"; _href (relUrl app url) ] [ txt name ] - ] + let path = if extraPath = "" then "" else $"{extraPath[1..]}/" + let active = if app.CurrentPage.StartsWith $"{path}{url}" then " active" else "" + let linkUrl = relUrl app url + li [ _class "nav-item" ] [ a [ _class $"nav-link{active}"; _href linkUrl; _hxBoost ] [ txt name ] ] /// Create a page view for the given content let private pageView (content: AppViewContext -> XmlNode list) app = [ header [] [ - nav [ _class "navbar navbar-dark bg-dark navbar-expand-md justify-content-start px-2 position-fixed top-0 w-100" ] [ + nav [ _class "navbar navbar-dark bg-dark navbar-expand-md justify-content-start px-2 position-fixed top-0 w-100" + hxInherited (_hxTarget "body") ] [ div [ _class "container-fluid" ] [ - a [ _class "navbar-brand"; _href (relUrl app ""); _hxNoBoost ] [ txt app.WebLog.Name ] + a [ _class "navbar-brand"; _href (relUrl app "") ] [ txt app.WebLog.Name ] button [ _type "button"; _class "navbar-toggler"; _data "bs-toggle" "collapse" _data "bs-target" "#navbarText"; _ariaControls "navbarText"; _ariaExpanded "false" _ariaLabel "Toggle navigation" ] [ @@ -244,9 +244,7 @@ module Layout = ] if app.IsLoggedOn then li [ _class "nav-item" ] [ - a [ _class "nav-link"; _hxPost (relUrl app "user/log-off"); _hxNoBoost ] [ - raw "Log Off" - ] + a [ _class "nav-link"; _hxPost (relUrl app "user/log-off") ] [ raw "Log Off" ] ] else navLink app "Log On" "user/log-on" @@ -313,7 +311,7 @@ module Layout = _integrity "sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" _crossorigin "anonymous" ] link [ _rel "stylesheet"; _href (relUrl app "themes/admin/admin.css") ] - body [ _hxBoost; _hxIndicator "#loadOverlay" ] [ + body [ hxInherited (_hxIndicator "#loadOverlay"); hxInherited (_hxTarget "body") ] [ yield! pageView content app script [ _src "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" _integrity "sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" @@ -362,7 +360,7 @@ let commonEdit (model: EditCommonModel) app = [ ] ] div [ _class "mb-2" ] [ - label [ _for "text" ] [ raw "Text" ]; raw "     " + raw "Text"; raw "     " div [ _class "btn-group btn-group-sm"; _roleGroup; _ariaLabel "Text format button group" ] [ input [ _type "radio"; _name (nameof model.Source); _id "source_html"; _class "btn-check" _value "HTML"; if model.Source = "HTML" then _checked ] diff --git a/src/MyWebLog/Views/Page.fs b/src/MyWebLog/Views/Page.fs index a0cae23..df39882 100644 --- a/src/MyWebLog/Views/Page.fs +++ b/src/MyWebLog/Views/Page.fs @@ -7,9 +7,10 @@ open MyWebLog.ViewModels /// The form to edit pages let pageEdit (model: EditPageModel) templates app = [ + let url = relUrl app "admin/page/save" h2 [ _class "my-3" ] [ raw app.PageTitle ] article [] [ - form [ _action (relUrl app "admin/page/save"); _method "post"; _hxPushUrl "true"; _class "container" ] [ + form [ _action url; _method "post"; _hxPost url; _class "container" ] [ antiCsrf app input [ _type "hidden"; _name (nameof model.Id); _value model.Id ] div [ _class "row mb-3" ] [ @@ -30,7 +31,9 @@ let pageEdit (model: EditPageModel) templates app = [ let pageList (pages: DisplayPage list) pageNbr hasNext app = [ h2 [ _class "my-3" ] [ raw app.PageTitle ] article [ _class "container mb-3" ] [ - a [ _href (relUrl app "admin/page/new/edit"); _class "btn btn-primary btn-sm mb-3" ] [ raw "Create a New Page" ] + a [ _href (relUrl app "admin/page/new/edit"); _hxBoost; _class "btn btn-primary btn-sm mb-3" ] [ + raw "Create a New Page" + ] if pages.Length = 0 then p [ _class "text-muted fst-italic text-center" ] [ raw "This web log has no pages" ] else @@ -58,7 +61,7 @@ let pageList (pages: DisplayPage list) pageNbr hasNext app = [ a [ _href (relUrl app pageLink); _target "_blank" ] [ raw "View Page" ] if app.IsEditor || (app.IsAuthor && app.UserId.Value = WebLogUserId pg.AuthorId) then span [ _class "text-muted" ] [ raw " • " ] - a [ _href $"{adminUrl}/edit" ] [ raw "Edit" ] + a [ _href $"{adminUrl}/edit"; _hxBoost ] [ raw "Edit" ] if app.IsWebLogAdmin then span [ _class "text-muted" ] [ raw " • " ] a [ _href adminUrl; _hxDelete adminUrl; _hxTarget "body"; _class "text-danger" @@ -79,23 +82,16 @@ let pageList (pages: DisplayPage list) pageNbr hasNext app = [ ] ] if pageNbr > 1 || hasNext then + let aAttrs link = [ _href (relUrl app link); _hxBoost; _class "btn btn-secondary" ] div [ _class "d-flex justify-content-evenly mb-3" ] [ div [] [ if pageNbr > 1 then let prevPage = if pageNbr = 2 then "" else $"/page/{pageNbr - 1}" - p [] [ - a [ _class "btn btn-secondary"; _href (relUrl app $"admin/pages{prevPage}") ] [ - raw "« Previous" - ] - ] + p [] [ a (aAttrs $"admin/pages{prevPage}") [ raw "« Previous" ] ] ] div [ _class "text-right" ] [ if hasNext then - p [] [ - a [ _class "btn btn-secondary"; _href (relUrl app $"admin/pages/page/{pageNbr + 1}") ] [ - raw "Next »" - ] - ] + p [] [ a (aAttrs $"admin/pages/page/{pageNbr + 1}") [ raw "Next »" ] ] ] ] ] diff --git a/src/MyWebLog/Views/Post.fs b/src/MyWebLog/Views/Post.fs index 5337793..79e51a9 100644 --- a/src/MyWebLog/Views/Post.fs +++ b/src/MyWebLog/Views/Post.fs @@ -102,7 +102,8 @@ let chapterEdit (model: EditChapterModel) app = [ /// Display a list of chapters let chapterList withNew (model: ManageChaptersModel) app = - div [ _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap HxSwap.OuterHtml ] [ + div [ _id "chapter_list"; _class "container mb-3"; hxInherited (_hxTarget "this") + hxInherited (_hxSwap HxSwap.OuterHtml) ] [ div [ _class "row mwl-table-heading" ] [ div [ _class "col-3 col-md-2" ] [ raw "Start" ] div [ _class "col-3 col-md-6 col-lg-8" ] [ raw "Title" ] @@ -123,7 +124,7 @@ let chapterList withNew (model: ManageChaptersModel) app = else let chapterUrl = relUrl app $"admin/post/{model.Id}/chapter/{idx}" a [ _href chapterUrl; _hxGet chapterUrl; _hxTarget $"#chapter{idx}" - _hxSwap $"{HxSwap.InnerHtml} show:#chapter{idx}:top" ] [ + _hxSwap $"{HxSwap.InnerHtml} show:top showTarget:#chapter{idx}" ] [ raw "Edit" ] span [ _class "text-muted" ] [ raw " • " ] @@ -138,12 +139,13 @@ let chapterList withNew (model: ManageChaptersModel) app = div [ _class "row pb-3"; _id "chapter-1" ] [ let newLink = relUrl app $"admin/post/{model.Id}/chapter/-1" if withNew then - span [ _hxGet newLink; _hxTarget "#chapter-1"; _hxTrigger "load"; _hxSwap "show:#chapter-1:top" ] [] + span [ _hxGet newLink; _hxTarget "#chapter-1"; _hxTrigger "load" + _hxSwap "show:top showTarget:#chapter-1" ] [] else div [ _class "row pb-3 mwl-table-detail" ] [ div [ _class "col-12" ] [ a [ _class "btn btn-primary"; _href newLink; _hxGet newLink; _hxTarget "#chapter-1" - _hxSwap "show:#chapter-1:top" ] [ + _hxSwap "show:top showTarget:#chapter-1" ] [ raw "Add a New Chapter" ] ] @@ -154,12 +156,13 @@ let chapterList withNew (model: ManageChaptersModel) app = /// Manage Chapters page let chapters withNew (model: ManageChaptersModel) app = [ + let backUrl = relUrl app $"admin/post/{model.Id}/edit" h2 [ _class "my-3" ] [ txt app.PageTitle ] article [] [ p [ _style "line-height:1.2rem;" ] [ strong [] [ txt model.Title ]; br [] small [ _class "text-muted" ] [ - a [ _href (relUrl app $"admin/post/{model.Id}/edit") ] [ + a [ _href backUrl; _hxGet backUrl; _hxPushUrl "true" ] [ raw "« Back to Edit Post" ] ] @@ -176,9 +179,11 @@ let list (model: PostDisplay) app = [ let tagCol = "col-lg-3 col-xl-4 col-xxl-5 d-none d-lg-inline-block" h2 [ _class "my-3" ] [ txt app.PageTitle ] article [] [ - a [ _href (relUrl app "admin/post/new/edit"); _class "btn btn-primary btn-sm mb-3" ] [ raw "Write a New Post" ] + a [ _href (relUrl app "admin/post/new/edit"); _hxBoost; _class "btn btn-primary btn-sm mb-3" ] [ + raw "Write a New Post" + ] if model.Posts.Length > 0 then - form [ _method "post"; _class "container mb-3"; _hxTarget "body" ] [ + form [ _method "post"; _class "container mb-3" ] [ antiCsrf app div [ _class "row mwl-table-heading" ] [ div [ _class dateCol ] [ @@ -219,7 +224,7 @@ let list (model: PostDisplay) app = [ a [ _href (relUrl app post.Permalink); _target "_blank" ] [ raw "View Post" ] if app.IsEditor || (app.IsAuthor && app.UserId.Value = WebLogUserId post.AuthorId) then span [ _class "text-muted" ] [ raw " • " ] - a [ _href $"{postUrl}/edit" ] [ raw "Edit" ] + a [ _href $"{postUrl}/edit"; _hxBoost ] [ raw "Edit" ] if app.IsWebLogAdmin then span [ _class "text-muted" ] [ raw " • " ] a [ _href postUrl; _hxDelete postUrl; _class "text-danger" @@ -252,22 +257,15 @@ let list (model: PostDisplay) app = [ ] ] if Option.isSome model.NewerLink || Option.isSome model.OlderLink then + let aAttrs link = [ _href link; _hxBoost; _class "btn btn-secondary" ] div [ _class "d-flex justify-content-evenly mb-3" ] [ div [] [ if Option.isSome model.NewerLink then - p [] [ - a [ _href model.NewerLink.Value; _class "btn btn-secondary"; ] [ - raw "« Newer Posts" - ] - ] + p [] [ a (aAttrs model.NewerLink.Value) [ raw "« Newer Posts" ] ] ] div [ _class "text-right" ] [ if Option.isSome model.OlderLink then - p [] [ - a [ _href model.OlderLink.Value; _class "btn btn-secondary" ] [ - raw "Older Posts »" - ] - ] + p [] [ a (aAttrs model.OlderLink.Value) [ raw "Older Posts »" ] ] ] ] else @@ -278,7 +276,7 @@ let list (model: PostDisplay) app = [ let postEdit (model: EditPostModel) templates (ratings: MetaItem list) app = [ h2 [ _class "my-3" ] [ raw app.PageTitle ] article [] [ - form [ _action (relUrl app "admin/post/save"); _method "post"; _hxPushUrl "true"; _class "container" ] [ + form [ _action (relUrl app "admin/post/save"); _method "post"; _hxBoost; _class "container" ] [ antiCsrf app input [ _type "hidden"; _name (nameof model.Id); _value model.Id ] div [ _class "row mb-3" ] [ diff --git a/src/MyWebLog/Views/User.fs b/src/MyWebLog/Views/User.fs index 9d47e47..9166784 100644 --- a/src/MyWebLog/Views/User.fs +++ b/src/MyWebLog/Views/User.fs @@ -138,8 +138,8 @@ let userList (model: WebLogUser list) app = if app.IsAdministrator || (app.IsWebLogAdmin && not (user.AccessLevel = Administrator)) then let userUrl = relUrl app $"admin/settings/user/{user.Id}" small [] [ - a [ _href $"{userUrl}/edit"; _hxTarget $"#user_{user.Id}" - _hxSwap $"{HxSwap.InnerHtml} show:#user_{user.Id}:top" ] [ + a [ _href $"{userUrl}/edit"; _hxBoost; _hxTarget $"#user_{user.Id}" + _hxSwap $"{HxSwap.InnerHtml} show:top showTarget:#user_{user.Id}" ] [ raw "Edit" ] if app.UserId.Value <> user.Id then @@ -168,7 +168,7 @@ let userList (model: WebLogUser list) app = ] ] div [ _id "user_panel" ] [ - a [ _href (relUrl app "admin/settings/user/new/edit"); _class "btn btn-primary btn-sm mb-3" + a [ _href (relUrl app "admin/settings/user/new/edit"); _hxBoost; _class "btn btn-primary btn-sm mb-3" _hxTarget "#user_new" ] [ raw "Add a New User" ] @@ -187,7 +187,8 @@ let userList (model: WebLogUser list) app = div [ _class "row mwl-table-detail"; _id "user_new" ] [] ] List.map userDetail model - |> div [ _class "container g-0"; _hxTarget "#user_panel"; _hxSwap $"{HxSwap.OuterHtml} show:window:top" ] + |> div [ _class "container g-0"; hxInherited (_hxTarget "#user_panel") + hxInherited (_hxSwap $"{HxSwap.OuterHtml} show:top showTarget:window") ] ] ] |> List.singleton @@ -197,7 +198,7 @@ let userList (model: WebLogUser list) app = let myInfo (model: EditMyInfoModel) (user: WebLogUser) app = [ h2 [ _class "my-3" ] [ txt app.PageTitle ] article [] [ - form [ _action (relUrl app "admin/my-info"); _method "post" ] [ + form [ _action (relUrl app "admin/my-info"); _method "post"; _hxBoost ] [ antiCsrf app div [ _class "d-flex flex-row flex-wrap justify-content-around" ] [ div [ _class "text-center mb-3 lh-sm" ] [ diff --git a/src/MyWebLog/Views/WebLog.fs b/src/MyWebLog/Views/WebLog.fs index 58f8166..671314f 100644 --- a/src/MyWebLog/Views/WebLog.fs +++ b/src/MyWebLog/Views/WebLog.fs @@ -814,7 +814,8 @@ let webLogSettings ] fieldset [ _id "users"; _class "container mb-3 pb-0" ] [ legend [] [ raw "Users" ] - span [ _hxGet (relUrl app "admin/settings/users"); _hxTrigger HxTrigger.Load; _hxSwap HxSwap.OuterHtml ] [] + span [ _hxGet (relUrl app "admin/settings/users"); _hxTarget "this"; _hxTrigger HxTrigger.Load + _hxSwap HxSwap.OuterHtml ] [] ] fieldset [ _id "rss-settings"; _class "container mb-3 pb-0" ] [ legend [] [ raw "RSS Settings" ] @@ -891,7 +892,7 @@ let webLogSettings _hxTarget "#tag_new" ] [ raw "Add a New Tag Mapping" ] - span [ _hxGet (relUrl app "admin/settings/tag-mappings"); _hxTrigger HxTrigger.Load + span [ _hxGet (relUrl app "admin/settings/tag-mappings"); _hxTarget "this"; _hxTrigger HxTrigger.Load _hxSwap HxSwap.OuterHtml ] [] ] ]