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
31 changes: 9 additions & 22 deletions flash-messages/flash.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,19 @@
<h1>{{.Title}}</h1>

<!-- Flash Messages Section -->
{{if .lvt.HasAnyFlash}}
<div id="flash-messages">
{{if .lvt.HasFlash "success"}}
<ins style="display:block;text-decoration:none" data-flash="success">{{.lvt.Flash "success"}}</ins>
{{end}}
{{if .lvt.HasFlash "error"}}
<del style="display:block;text-decoration:none" data-flash="error">{{.lvt.Flash "error"}}</del>
{{end}}
{{if .lvt.HasFlash "warning"}}
<ins style="display:block;text-decoration:none" data-flash="warning">{{.lvt.Flash "warning"}}</ins>
{{end}}
{{if .lvt.HasFlash "info"}}
<ins style="display:block;text-decoration:none" data-flash="info">{{.lvt.Flash "info"}}</ins>
{{end}}
</div>
{{end}}
{{.lvt.FlashTag "success"}}
{{.lvt.FlashTag "error"}}
{{.lvt.FlashTag "warning"}}
{{.lvt.FlashTag "info"}}
Comment on lines +14 to +17
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flash-messages example documentation is now out of sync with this template change: flash-messages/README.md (and the “About Flash Messages” blockquote in this template) still instructs using .lvt.HasAnyFlash/.lvt.HasFlash/.lvt.Flash, but the template now demonstrates .lvt.FlashTag. Please update the example docs/text to match the new recommended helper (or mention both patterns explicitly).

Copilot uses AI. Check for mistakes.

<!-- Add Item Form -->
<form method="POST" name="addItem">
<input type="text"
name="item"
placeholder="Enter item name"
{{if .lvt.HasError "item"}}aria-invalid="true"{{end}} />
<button type="submit" name="addItem">Add Item</button>
{{if .lvt.HasError "item"}}
<small>{{.lvt.Error "item"}}</small>
{{end}}
{{.lvt.AriaInvalid "item"}} />
<button type="submit" name="addItem" {{.lvt.AriaDisabled "item"}}>Add Item</button>
{{.lvt.ErrorTag "item"}}
</form>

<!-- Action Buttons -->
Expand Down Expand Up @@ -78,7 +64,8 @@
<li>They don't affect <code>ResponseMetadata.Success</code></li>
<li>Types: <code>success</code>, <code>error</code>, <code>warning</code>, <code>info</code></li>
<li>Set via: <code>ctx.SetFlash("success", "message")</code></li>
<li>Read via: <code>.lvt.Flash "success"</code>, <code>.lvt.HasFlash "success"</code></li>
<li>Render via: <code>.lvt.FlashTag "success"</code> (recommended)</li>
<li>Or manually: <code>.lvt.HasFlash "key"</code>, <code>.lvt.Flash "key"</code></li>
</ul>
</blockquote>
</main>
Expand Down
17 changes: 7 additions & 10 deletions login/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ type AuthController struct {
type AuthState struct {
Username string `lvt:"persist"`
IsLoggedIn bool `lvt:"persist"`
Error string
ServerMessage string
LoginTime time.Time `lvt:"persist"`
}
Expand All @@ -35,20 +34,19 @@ func (c *AuthController) Login(state AuthState, ctx *livetemplate.Context) (Auth
username := ctx.GetString("username")
password := ctx.GetString("password")

// Simple validation
if username == "" || password == "" {
state.Error = "Username and password are required"
return state, nil
// Field-level validation
if username == "" {
return state, livetemplate.NewFieldError("username", fmt.Errorf("username is required"))
}
if password == "" {
return state, livetemplate.NewFieldError("password", fmt.Errorf("password is required"))
}

// Demo: accept any username with password "secret"
if password != "secret" {
state.Error = "Invalid credentials"
ctx.SetFlash("error", "Invalid credentials")
return state, nil
}

// Clear error and set logged in state
state.Error = ""
state.Username = username
state.IsLoggedIn = true
state.LoginTime = time.Now()
Expand Down Expand Up @@ -76,7 +74,6 @@ func (c *AuthController) Login(state AuthState, ctx *livetemplate.Context) (Auth
func (c *AuthController) Logout(state AuthState, ctx *livetemplate.Context) (AuthState, error) {
state.Username = ""
state.IsLoggedIn = false
state.Error = ""
state.ServerMessage = ""

// Delete session cookie
Expand Down
12 changes: 6 additions & 6 deletions login/templates/auth.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ <h1>Dashboard</h1>
<!-- Login View -->
<article>
<h1>Login</h1>
{{if .Error}}
<del style="display:block;text-decoration:none">{{.Error}}</del>
{{end}}
{{.lvt.FlashTag "error"}}
<!-- HTTP login — lvt-no-intercept forces real HTTP POST for cookie + redirect -->
<form method="POST" lvt-no-intercept>
<label for="username">Username
<input type="text" id="username" name="username" placeholder="Enter username" required>
<input type="text" id="username" name="username" placeholder="Enter username" required {{.lvt.AriaInvalid "username"}}>
{{.lvt.ErrorTag "username"}}
</label>
<label for="password">Password
<input type="password" id="password" name="password" placeholder="Enter password" required>
<input type="password" id="password" name="password" placeholder="Enter password" required {{.lvt.AriaInvalid "password"}}>
{{.lvt.ErrorTag "password"}}
</label>
<button type="submit" name="login">Login</button>
<button type="submit" name="login" {{.lvt.AriaDisabled "username" "password"}}>Login</button>
</form>
<small>
Demo: Use any username with password <strong>secret</strong><br>
Expand Down
16 changes: 5 additions & 11 deletions progressive-enhancement/progressive-enhancement.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,8 @@
</mark>

<!-- Flash messages -->
{{if .lvt.Flash "success"}}
<ins style="display:block; text-decoration:none;">{{.lvt.Flash "success"}}</ins>
{{end}}
{{if .lvt.Flash "error"}}
<del style="display:block; text-decoration:none;">{{.lvt.Flash "error"}}</del>
{{end}}
{{.lvt.FlashTag "success"}}
{{.lvt.FlashTag "error"}}

<!-- Add todo form -->
<!-- Works with JS: client intercepts form and routes via WebSocket/fetch -->
Expand All @@ -56,13 +52,11 @@
name="title"
value="{{.InputTitle}}"
placeholder="What needs to be done?"
{{if .lvt.HasError "title"}}aria-invalid="true"{{end}}
{{.lvt.AriaInvalid "title"}}
autofocus
>
{{if .lvt.HasError "title"}}
<small>{{.lvt.Error "title"}}</small>
{{end}}
<button type="submit" name="add">Add Todo</button>
{{.lvt.ErrorTag "title"}}
<button type="submit" name="add" {{.lvt.AriaDisabled "title"}}>Add Todo</button>
</form>
</article>

Expand Down
8 changes: 3 additions & 5 deletions todos/todos.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@
placeholder="What needs to be done?"
required
aria-required="true"
{{ if .lvt.HasError "text" }}aria-invalid="true"{{ end }}
{{.lvt.AriaInvalid "text"}}
/>
<button type="submit" name="add">Add</button>
<button type="submit" name="add" {{.lvt.AriaDisabled "text"}}>Add</button>
</fieldset>
{{ if .lvt.HasError "text" }}
<del style="display:block;text-decoration:none">{{ .lvt.Error "text" }}</del>
{{ end }}
{{.lvt.ErrorTag "text"}}
</form>
{{ end }}

Expand Down
22 changes: 7 additions & 15 deletions ws-disabled/ws-disabled.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,26 @@
<p><small>WebSocket is disabled. The client library uses HTTP fetch for all actions
and applies tree-based DOM updates without page reloads.</small></p>

{{if .lvt.Flash "success"}}
<ins style="display:block; text-decoration:none;">{{.lvt.Flash "success"}}</ins>
{{end}}
{{if .lvt.Flash "error"}}
<del style="display:block; text-decoration:none;">{{.lvt.Flash "error"}}</del>
{{end}}
{{.lvt.FlashTag "success"}}
{{.lvt.FlashTag "error"}}

<article>
<form method="POST" name="add">
<div class="grid">
<label>
Label
<input type="text" name="label" placeholder="My favorite site"
{{if .lvt.HasError "label"}}aria-invalid="true"{{end}}>
{{if .lvt.HasError "label"}}
<small>{{.lvt.Error "label"}}</small>
{{end}}
{{.lvt.AriaInvalid "label"}}>
{{.lvt.ErrorTag "label"}}
</label>
<label>
URL
<input type="url" name="url" placeholder="https://example.com"
{{if .lvt.HasError "url"}}aria-invalid="true"{{end}}>
{{if .lvt.HasError "url"}}
<small>{{.lvt.Error "url"}}</small>
{{end}}
{{.lvt.AriaInvalid "url"}}>
{{.lvt.ErrorTag "url"}}
</label>
</div>
<button type="submit" name="add">Add Bookmark</button>
<button type="submit" name="add" {{.lvt.AriaDisabled "label" "url"}}>Add Bookmark</button>
</form>
</article>

Expand Down
Loading