Hydro Documentation

repository·main·Indexed 21 days ago

https://github.com/hydrostack/hydro

An extension for ASP.NET Core (version 6.0+) that adds reactivity and statefulness to View Components. Hydro enables the creation of interactive, SPA-like experiences using Razor views and C# with minimal JavaScript, leveraging AJAX and Alpine.js for client-server communication and DOM swapping. Key features include stateful HydroComponents, server-side actions triggered via the on: tag helper, request queuing to prevent race conditions, and custom authorization via IHydroAuthorizationFilter.

Tokens
19.3K
Snippets
71
Records
83
Agent score
75%

What's inside Hydro

  1. What is Hydro?

    main

    Hydro is an extension for ASP.NET Core MVC and Razor Pages that enables stateful and reactive components without requiring significant JavaScript. It extends standard View Components to allow them to communicate with each other and update the UI without full page reloads, providing a Single Page Application (SPA) feel within the standard .NET server-side rendering model.

    Hydro relies on:

    • Razor views (*.cshtml): For server-side UI generation.
    • AJAX: For client-server communication, state synchronization, and updates.
    • Alpine.js: Used for request execution, DOM swapping, and as an expansion point for client-side interactivity.
  2. What is Hydro and when should I use it?

    main

    Hydro is a library for ASP.NET Core MVC and Razor Pages designed to create stateful, interactive components using standard Razor views (.cshtml), view components, and tag helpers.

    Use Hydro when you want to:

    • Build applications with Server-Side Rendering (SSR).
    • Achieve a Single-Page Application (SPA) feel without writing custom JavaScript.
    • Keep your entire development workflow within the .NET ecosystem.
    • Avoid the complexity of managing large front-end package ecosystems and the communication overhead between separate front-end and back-end layers.
  3. How Hydro works

    main

    Hydro achieves reactivity and statefulness by combining server-side rendering with client-side orchestration using three core technologies:

    • Razor views and view components (*.cshtml): Used for the backbone of UI generation and server-side rendering, allowing a mix of HTML and C#.
    • AJAX: Facilitates communication between the client and server. It is used to send application state to the server, receive updates/responses, and persist state for subsequent requests to ensure context is maintained.
    • Alpine.js: Acts as the engine for request execution and DOM swapping. It also serves as an extension point, allowing developers to add custom client-side interactivity to the standard HTML generated by Hydro.
  4. Understand the JavaScript execution context in ExecuteJs

    main

    When you call Client.ExecuteJs, the JavaScript expression is executed within the context of the component's DOM element. This means you can use the this keyword in your JavaScript string to reference the specific element associated with that component.

    This is useful for immediate DOM manipulations (like removing an element) that don't need to wait for a full state update cycle.

    public void Close()
    {
        // Dispatches event and immediately removes the component's DOM element via JS
        DispatchGlobal(new CloseDialog(nameof(ProductDialog)));
        Client.ExecuteJs("this.remove()");
    }
  5. Dispatch synchronous vs asynchronous events

    main

    By default, Hydro events are synchronous. This means the event execution is part of the same internal operation as the action. For example, if a button triggers a synchronous action, the button will remain disabled until both the action and the event handlers are finished.

    To run an event independently of the action's pipeline, dispatch it asynchronously by setting the asynchronous parameter to true. This allows the action to complete and the UI to respond while the event runs on its own.

    // Synchronous (default)
    Dispatch(new CountChangedEvent(Count));
    
    // Asynchronous
    Dispatch(new CountChangedEvent(Count), asynchronous: true);
  6. How Hydro compares to HTMX

    main

    HTMX is a library used to handle client-server interactions by adding event handlers to a document that call the back-end and replace specific elements, providing a Single Page Application (SPA) feel without full-page reloads.

    The key distinction is state management:

    • HTMX lacks a built-in concept of components that maintain state.
    • Hydro allows you to define components with properties that are automatically persisted across requests without additional configuration.
  7. Naming conventions for Hydro views

    main

    There are two ways to name Hydro views for use in Razor templates:

    1. Automatic naming (dash-case): By default, a class like SubmitButton is used as <submit-button />.
    2. Manual naming (PascalCase): Use the [HtmlTargetElement] attribute with nameof to allow using the exact class name as the tag.
    Class NameAttributeTag Usage
    SubmitButton(Default)<submit-button />
    SubmitButton[HtmlTargetElement(nameof(SubmitButton))]<SubmitButton />
    // Automatic naming
    public class SubmitButton : HydroView;
    // Usage: <submit-button />
    
    // Manual naming
    [HtmlTargetElement(nameof(SubmitButton))]
    public class SubmitButton : HydroView;
    // Usage: <SubmitButton />
  8. Validate nested models and collections

    main

    Standard Data Annotations do not automatically validate nested objects or collections. Hydro provides two specific attributes to handle these scenarios:

    • [ValidateObject]: Apply this to a property of a complex type to trigger validation on the nested model.
    • [ValidateCollection]: Apply this to a collection (e.g., List<T>) to trigger validation on every item within that collection.

    Both attributes ensure that the validation logic traverses into the child objects or collection elements.

    // Nested Model Example
    public class ProductForm : HydroComponent
    {
        [ValidateObject]
        public ProductData Product { get; set; }
    }
    
    public class ProductData
    {
        [Required, MaxLength(50)]
        public string Name { get; set; }
        
        [Range(0, 100)]
        public decimal Price { get; set; }
    }
    
    // Collection Example
    public class InvoiceForm : HydroComponent
    {
        [ValidateCollection]
        public List<LineData> Lines { get; set; }
    }
    
    public class LineData
    {
        [Required, MaxLength(50)]
        public string Name { get; set; }
        
        [Range(0, 100)]
        public decimal Price { get; set; }
    }
  9. Define transient properties in Hydro components

    main

    Transient properties are properties whose values do not persist across requests. This is useful for request-scoped data like success messages or large datasets that should be re-fetched rather than stored in state.

    You can define a transient property in two ways:

    1. Use the [Transient] attribute on a property with a setter.
    2. Define a property without a setter (a read-only property).
    // Using [Transient] attribute
    public class ProductForm : HydroComponent
    {
        [Transient]
        public bool IsSuccess { get; set; }
    }
    
    // Using a property without a setter
    public class ProductForm : HydroComponent
    {
        public DateTime CurrentDate => DateTime.Now;
    }
  10. How Hydro compares to Blazor

    main

    Hydro and Blazor both use component models (Hydro vs. Blazor's Razor Components), but they differ fundamentally in their communication and state management:

    • Blazor Server uses WebSockets to manage state and exchange information between the server and client.
    • Blazor WebAssembly uses WebAssembly to run .NET code directly in the browser.
    • Hydro uses a standard HTTP request/response model. It performs rendering on the back-end and morphs client HTML when necessary. Crucially, Hydro keeps state on the page rather than within a connection scope, avoiding the need for WebSockets or WebAssembly.
  11. How polling pauses work

    main
    Hydro optimizes resource usage by automatically managing the polling lifecycle based on page visibility. When a page containing a polling component is hidden (for example, when the user switches to a different browser tab), polling will automatically stop. Polling will restart once the tab becomes visible again.
  12. How events work in Hydro

    main

    Events enable decoupled communication between components. A component can publish an event using Dispatch, and other components can subscribe to it using Subscribe. When a component's subscription is triggered, that component is automatically re-rendered.

    To use events, define a data structure (typically a record) to represent the event payload, then dispatch it from an action and subscribe to it in a target component.

    // 1. Define the event
    public record CountChangedEvent(int Count);
    
    // 2. Dispatch from a component
    public class Counter : HydroComponent
    {
        public int Count { get; set; }
        public void Add() 
        {
            Count++;
            Dispatch(new CountChangedEvent(Count));
        }
    }
    
    // 3. Subscribe in another component
    public class Summary : HydroComponent
    {
        public Summary() => Subscribe<CountChangedEvent>(Handle);
        public int CountSummary { get; set; }
        public void Handle(CountChangedEvent data) => CountSummary = data.Count;
    }