19

Given a child and a parent component I am trying to execute a method within the child when a parameter has changed (from the parent).

Parent

<Child value=@IsChanged></Child>

<button onclick="Update"></button>

@functions(){
  public bool IsChanged{get;set;}
  public void Update()
  {
   this.IsChanged!=this.IsChanged;
  }

Child

@(value?"True":"False")
@functions()
{
  [Parameter]
  protected bool value {get;set;}
  
  public void SideEffect()
  {
    Console.WriteLine("has changed"); //i want this method executed when value changes from parent
  }

As you can see i need to execute the method inside the Child onchange of the parameter.The parameter is changed in the parent.

P.S
I have looked on the onchange eventhandler but i need to execute on a [Parameter].

2 Answers 2

42

You should override the OnParametersSet or OnParametersSetAsync lifecycle method.

Child

@(_value ? "True" : "False")

@code()
{
  private bool _value;

  [Parameter] public bool Value { get; set; }

  protected override void OnInitialized()
  {
      _value = Value;
  }

  protected override void OnParametersSet()
  {
      if (_value != Value)
      {
          _value = Value;
          Console.WriteLine("a parameter has changed");
      }
  }
}
7
  • 2
    But what if i want different side effects for different parameters? Is this method called on any parameter change? Can't i pattern match or something? Commented Jan 24, 2019 at 13:42
  • 1
    @ChrisSainty The dumb but obvious question: how do you check them to determine which one has changed? Do you have to keep a copy of all your parameters just so you can do a comparison? Commented Jan 13, 2021 at 22:16
  • 3
    @BrianMacKay It really depends on what you need to do. If you look at the Blazor source code this pattern is common. They keep local copies of parameters and then check them during the OnParametersSet lifecycle for changes. They do this for performance reasons so only parameters which have performance implications are copied. Commented Jan 14, 2021 at 11:56
  • 2
    @ChrisSainty So if I have e.g. a component containing an EditForm with 50 string/text fields, and I want to make sure that all those strings are formatted properly, i.e. all should be trimmed, some contain phone numbers, others contain dates etc, and one of the 50 fields are changed in the parent component, I have to keep 50 extra local variables and compare all of them in OnParametersSet to see if there is a differece so I can apply the format? Surely there's a better way? This is why I doubt Blazor will ever take off for advanced LOB apps. In some areas it still feels very amateurish. Commented Jul 2, 2021 at 15:26
  • 3
    no, because you could (and probably should) use form validation, or validate in the event handlers of the textboxes or when the form is submitted / some button is clicked. OnParameterSet is the wrong place to validate user input.
    – Michael
    Commented Sep 21, 2021 at 7:16
17

This is the best and simpler solution to your question

Child.razor

    @( _Value ? "True" : "False")


<div>@message</div>

@code
{
    [Parameter]
    public bool Value { get;set; }
  // [Parameter]
  //  public EventCallback<bool> ValueChanged { get;set; }

    private bool _value;
    private bool _Value
    {
        get => _value;
        set 
        {
            if ( _value != value)
            {
                _value = value;
                SideEffect();
              //  ValueChanged.InvokeAsync(_value);        
            }
        }
    }

    private string message;

    public void SideEffect()
    {
       message = $"Parameter has changed: {_Value}"; //i want this method executed when value changes from parent
     
    }

    protected override void OnParametersSet()
    {
            if (_Value != Value)
            {
                _Value = Value;
            }
    }
 }

Usage

@page "/"

<Child Value="IsChanged" />

<button type="button" @onclick="Update">Update</button>

<div>IsChanged: @IsChanged</div>

@code
{
   private bool IsChanged { get; set; } = true;

   public void Update()
   {
      IsChanged = !IsChanged;
   }
}
6
  • 3
    You're not supposed to have any logic in component parameter setters. See "Component parameters should be declared as auto-properties, meaning that they shouldn't contain custom logic in their get or set accessors ... Don't place custom logic in the get or set accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component." from learn.microsoft.com/en-us/aspnet/core/blazor/components/… Commented Sep 9, 2021 at 15:17
  • @Christopher Edwards, sorry, but this answer was provided 2.3 years before the linked docs had been created, even before Steve Sanderson writes that component parameter properties should be defined as auto-properties only, even before all the code samples in the docs had been altered to reflect that. But, alas, my code is exactly formed in the way it should be nowadays. Please, inspect the code to see that I DO NOT mutate or change the parameter property Value's value. I do define a local property named _Value whose initial value is taken from the Value parameter property from within the
    – enet
    Commented Sep 9, 2021 at 16:07
  • OnParametersSet method, and thus I can manipulate the copied value as much as I want. THIS IS HOW YOU SHOULD CODE NOWADAYS. Incidentally, the issue is not the logic placed in the get or set accessors. Don't tell others not to place logic there. Very often I do that with component parameter properties. Tell them not to change or mutate the value of the parameter property, and as long as you do not change the parameter value you can, and sometimes have to place logic in either accessors. Hope this helps...
    – enet
    Commented Sep 9, 2021 at 16:15
  • It's annoying that the official advice is to not do this when it is so convenient and OnParametersSet is so inconvenient. Maybe if you disagree with the advice, with more expertise than me, you could open a github issue on the docs page. It seems that the blazor team are leery of people using logic in the set accessors in case they cause issues accidentally. It seems to me that it should just be more deterministic and easier to fall into the pit of success. Commented Sep 10, 2021 at 8:27
  • 1
    I have removed code like this from almost anywhere in my solution. Partially, because the side effects caused unnecessary rerendering (like Steve Sanderson states) or because I needed to call async methods, which cannot be awaited properly in properties. Be very careful, if you have logic in setters. I guess parameter checking or just setting a changed property like here should be ok.
    – Michael
    Commented Sep 21, 2021 at 7:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.