Erhan Urun
Erhan Urun

Reputation: 279

How to change Button Text by calling another component method in Blazor

I have a custom Button component like below.

<button class="btn btn-outline-primary" @onclick="OnClick"> @ButtonText</button>

@code {

    [Parameter]
    public string ButtonText { get; set; } = "Edit";

    public virtual void OnClick()
    {
        ButtonText = ButtonText == "Edit" ? "Editing..." : "Edit";
    }
}

When I click button every thing works as excepted and Button text is changing. But when i click on another button i want to change the text of the button.To do this I call the button's onclick method. But when I click the button, the text does not change even though the method is called. Here is my page.

@page "/test"

<EditButton @ref="EditButton"></EditButton>

<button class="btn-primary" @onclick="ChangeButtonText">Change Button Text</button>

@code {

    EditButton EditButton;

    void ChangeButtonText()
    {
        EditButton.OnClick();
    }
}

Upvotes: 1

Views: 5857

Answers (3)

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30046

What about this simple solution. EditButton remains the same.

@page "/"

<EditButton ButtonText="@this.editButtonText" ></EditButton>

<button class="btn btn-primary ml-3" @onclick="ChangeButtonText">Change Button Text</button>

@code {
    string editButtonText = "Edit";

    void ChangeButtonText()
    {
        editButtonText = editButtonText.Equals("Edit")
        ? "Editing"
        : "Edit";
    }
}

You don't need to hook up callbacks and references. When you change the value of ButtonText on your component and trigger a Blazor Component event handler - ChangeButtonText - on the parent, the Renderer detects the change to ButtonText and informs EditButton by calling SetParametersSetAsync. This precipitates a render event on EditButton and the button text gets updated.

Upvotes: 1

enet
enet

Reputation: 45626

Your code does not work simply because it lacks a call to the StateHasChanged method:

EditButton.razor

<button class="btn btn-outline-primary" @onclick="OnClick"> @ButtonText</button>

@code {

    [Parameter]
    public string ButtonText { get; set; } = "Edit";

    public void OnClick()
    {
        ButtonText = ButtonText == "Edit" ? "Editing..." : "Edit";

        InvokeAsync(() => StateHasChanged());
    }
} 

Note That I added a call to the StateHasChanged method to re-render the component after its state has changed.

InvokeAsync(() => StateHasChanged());

Index.razor

@page "/"

<EditButton @ref="EditButton"></EditButton>

<button class="btn-primary" @onclick="ChangeButtonText">Change Button Text</button>

@code {

    EditButton EditButton;

    void ChangeButtonText()
    {
        EditButton.OnClick();
    }
}

To sum up: What you do is fine, but you must call the StateHasChaged method manually. It is not called automatically by the framework. Note that it is not necessary to add a call to the StateHasChaged method from event handler that handle UI event, such as the 'click' event. But in your case it is necessary, as the EditButton.OnClick event handler is not a UI event handler. That was the only issue with your code.

IMPORTANT: You should not modify or change the state of a Component parameter property. This should be avoided:

[Parameter]
public string ButtonText { get; set; } = "Edit";

As well as this:

ButtonText = ButtonText == "Edit" ? "Editing..." : "Edit";

Component parameter properties should be automatic properties:

[Parameter]
public string ButtonText { get; set; } 

And they must never be modified outside of the component. Read this Read this. Instead you should define local variable or properties that are assigned from the component parameter properties' values, on which you can apply whatever manipulations you want.

Not adhering to that rule may result in unfavorable side effects in large components, only Steve Sanderson can discern.

Upvotes: 1

Henk Holterman
Henk Holterman

Reputation: 273264

It's partly due to your architectural design: The ButtonComponent should not be managing the editing-status, it should just display it.

EditButton

<button class="btn btn-outline-primary" @onclick="Click"> @ButtonText</button>
@code {

    // don't change [Parameter] properties inside this component, 
    // they are set by the Owner
    [Parameter]
    public bool IsEditing { get; set; }

    [Parameter]
    public EventCallback Click { get; set;  }

    public string ButtonText => IsEditing ? "Editing..." : "Edit";
}

Test Page

@page "/test"

<EditButton IsEditing="isEditing" Click="EditClick"></EditButton>

<button class="btn-primary" @onclick="ChangeButtonText">Change Button Text</button>

@code {

    //EditButton EditButton;
    bool isEditing = false;

    void ChangeButtonText()
    {
        isEditing = false;  // or !isEditing
    }

    void EditClick()
    {
       isEditing = ! isEditing;   
    }

}

Upvotes: 1

Related Questions