Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Blazor Web Development Cookbook

You're reading from   Blazor Web Development Cookbook Tested recipes for advanced single-page application scenarios in .NET 9

Arrow left icon
Product type Paperback
Published in Nov 2024
Publisher Packt
ISBN-13 9781835460788
Length 282 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Pawel Bazyluk Pawel Bazyluk
Author Profile Icon Pawel Bazyluk
Pawel Bazyluk
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Chapter 1: Working with Component-Based Architecture 2. Chapter 2: Synchronous and Asynchronous Data Binding FREE CHAPTER 3. Chapter 3: Taking Control of Event Handling 4. Chapter 4: Enhancing Data Display with Grids 5. Chapter 5: Managing Application State 6. Chapter 6: Building Interactive Forms 7. Chapter 7: Validating User Input Forms 8. Chapter 8: Keeping the Application Secure 9. Chapter 9: Exploring Navigation and Routing 10. Chapter 10: Integrating with OpenAI 11. Index 12. Other Books You May Enjoy

Passing values from the parent component with CascadingParameter

Sharing parameters across multiple components is a common scenario in web applications. It boosts performance as data can be shared rather than being requested from an external source by each component. It also simplifies the code, especially in parent-child scenarios. In Blazor, that’s where the concept of CascadingParameter comes into play. Its counterpart, CascadingValue, allows you to provide a value that cascades down the component tree. This pair enables child components to receive and use this shared data or state. This approach solves the challenge of passing information through component hierarchies without complex plumbing or tightly coupled communication.

Let’s implement a Cart service and pass it downward in a cascading fashion so that we can intercept it within the offer area represented by Ticket components. We’ll also render the Cart summary – fully decoupled from the Ticket behavior.

Getting ready

Before we start exploring how to pass the cascading value, do the following:

  • Create a Recipe06 directory – this will be your working directory
  • Copy the Ticket component from the Ensuring that a parameter is required recipe or copy its implementation from the Chapter01/Recipe05 directory of this book’s GitHub repository.

How to do it...

Follow these steps to implement CascadingParameter for value sharing:

  1. Add a Cart class and declare supporting Content and Value properties. Extend Cart so that you can communicate state changes by requiring a fallback Action property with a primary constructor and implement the basic Add() method that triggers this notification:
    public class Cart(Action onStateHasChanged)
    {
        public List<string> Content { get; init; } = [];
        public decimal Value { get; private set; }
        public int Volume => Content.Count;
        public void Add(string tariff, decimal price)
        {
            Content.Add(tariff);
            Value += price;
            onStateHasChanged.Invoke();
        }
    }
  2. Create a SellingTickets component so that our tickets can be sold:
Figure 1.8: Adding a new SellingTickets component

Figure 1.8: Adding a new SellingTickets component

  1. Use the @rendermode attribute to declare that SellingTickets operates in InteractiveWebAssembly mode and a @page directive to enable routing:
    @page "/ch01r06"
    @rendermode InteractiveWebAssembly
  2. In the @code block of SellingTickets, declare the Cart object and initialize it within the OnInitialized() life cycle method:
    @code {
        protected Cart Cart;
        protected override void OnInitialized()
        {
            Cart = new(() =>
                InvokeAsync(StateHasChanged));
        }
    }
  3. In the SellingTickets markup, add the CascadingValue wrapper with the Cart instance as its value. Declare two sellable tickets within the cart’s operational scope, leveraging the Ticket component:
    <CascadingValue Value="Cart">
        <Ticket Tariff="Adult" Price="10.00m" />
        <Ticket Tariff="Child" Price="5.00m" />
    </CascadingValue>
  4. Below the Cart area of the SellingTickets markup, append additional markup to display the Cart summary:
    <div class="cart-summary">
        <div class="cart-content">
            Items: @Cart.Volume
        </div>
        <div class="cart-value">Price: @Cart.Value</div>
    </div>
  5. Navigate to the Ticket component. In the @code block, declare CascadingParameter so that you can intercept the Cart instance and replace the OnAdded parameter with an Add() method:
    @code {
        [CascadingParameter]
        public Cart Cart { get; set; }
        public void Add() => Cart.Add(Tariff, Price);
    }
  6. In the Ticket markup, replace the @onclick button action so that you can execute the new Add() method:
    <div class="ticket-actions">
        <button @onclick="@Add">Add to cart</button>
    </div>

How it works...

In step 1, we implemented the Cart class. We declared a Value property to hold the current cart value and a Content collection to store added ticket tariffs. We also implemented a parameterless Volume method to calculate the amount of tickets currently in the cart. Then, we implemented an Add() method that, in addition to the normal logic for adding to the cart, is responsible for communicating those changes to external objects by invoking the onStateHasChanged delegate, which is passed using the primary constructor pattern. That way, we ensured Cart initialization requires us to provide an action to execute upon state changes.

In step 2, we created a SellingTickets component. In step 3, we declared it to render in InteractiveWebAssembly mode and leveraged the @page directive to enable routing. In step 4, in the @code block of SellingTickets, we declared a Cart instance. We initialized Cart as part of the overridden OnInitialized() life cycle method and, as an invokable Action delegate responsible for applying state changes, we passed in the StateHasChanged() life cycle method. With that in place, any change in the Cart object will prompt Blazor to recalculate DOM changes at the level of the SellingTicket component. To avoid any threading or race condition issues, we wrapped the StateHasChanged() method in the InvokeAsync() component base method. In step 5, we implemented the SellingTickets markup. We used a CascadingValue component and assigned Cart as its value. We also declared CascadingValue content by adding two Ticket instances, representing tickets available for sale. In step 6, we extended the SellingTickets markup further by adding a section that contained the summary of the cart, showing its current size and value.

In step 7, we navigated to the @code block of the Ticket component and declared CascadingParameter there. Blazor will intercept this parameter’s value as it cascades from a parent component. Notably, we didn’t use EditorRequired here – as Blazor resolves the cascading value just in time, it would have no impact on compilation. With Cart available in the scope of the Ticket component, we replaced the existing OnAdded parameter with an Add() method that invokes Cart.Add() directly. In step 8, we updated the Ticket markup by replacing the outdated @onclick assignment on the existing button with a reference to the newly implemented Add() method.

There’s more...

So, why does the Cart implementation require an Action delegate to work? Here, StateHasChanged() is a component life cycle method, so it triggers DOM re-rendering scoped to that component and its nested children. Since adding to the cart happens at the Ticket component level and invokes StateHasChanged(), there’s no impact on the parent SellingTickets component, and the Cart summary section remains unchanged! Having the Action delegate allows the Cart object to persist a reference to the origin component and thus trigger a DOM update at any level of the component tree.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image