locked
Blazor does not update interface after calling a method RRS feed

  • Question

  • User433211055 posted

    I start to learn Blazor

    I write this code that call a service to load the data from sql server;

    I set the parameter and call a method and this work fine, but it doesn't work the second time the interface does not update.

    Why?

    Every time I call the method shouldn't the interface be automatically updated ?

    In debug mode the service method is called and the data is updated.

    This is my code

    @page "/ChipsReport"
        
        @using Data;
        @inject ChipsReportService crService
        
        <h3>ChipsReport</h3>
        
        <input id="txtDate" type="date" required @bind-value="paramChipReport.DataIni" />
        
        <select  @bind="paramChipReport.ValueTypeId">
            <option value="42">opt1</option>
            <option value="1">opt2</option>
            <option value="36">opt3</option>
            <option value="100">opt4</option>
        </select>
        
        <select @bind="paramChipReport.Absolute">
            <option value="0">Inc</option>
            <option value="1">Tot</option>
        </select>
        
        <button class="btn btn-primary" @onclick="@(() => LoadData(paramChipReport))">Load data</button>
        
        <br />
        
        @if (chipReports != null)
        {
                <table class="table table-sm table-striped">
                    <thead>
                        <tr>
                            <th>Test</th>
                            <th>Test1</th>
                            <th>Test2</th>
                           
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (var cr in chipReports)
                        {
                           <tr>
                            <td>@cr.Tag</td>
                            <td>@cr.Time.ToShortDateString()</td>
                            <td>@cr.Pos</td>
                         
                           </tr>
                        }
                    </tbody>
                </table>
        }
    
    @code {
        public class ParamChipReport
        {
            public DateTime DateIni{ get; set; }
            public int ValueTypeId { get; set; }
            public int Absolute { get; set; }
        };
    
        ParamChipReport paramChipReport = new ParamChipReport();
    
        ChipReport[] chipReports;
    
        private async Task<ChipReport[]> LoadData(ParamChipReport paramChipReport)
        { 
    
            return chipReports = await crService.GetChipsReports(paramChipReport.GamingDate, paramChipReport.ValueTypeId, paramChipReport.Absolute);
        }
    }

    BR

    Monday, October 5, 2020 4:56 PM

All replies

  • User-474980206 posted

    Blazor uses a component tree and virtual dom. while an event will trigger a tree re-render,  a component will only re-render if one of its properties (parameter or binding values) has changed since last render.

    In you case, you are only updating a local variable on the event, so no render is required.

    Anyway, just change chipReport to class property, and make either public or add [Parameter] attribute.

    Tuesday, October 6, 2020 1:58 AM
  • User433211055 posted

    I try but doesn't work.

    Blazor uses a component tree and virtual dom. while an event will trigger a tree re-render,  a component will only re-render if one of its properties (parameter or binding values) has changed since last render.

    In you case, you are only updating a local variable on the event, so no render is required.

    Anyway, just change chipReport to class property, and make either public or add [Parameter] attribute.

    I update my code but not obtain any bood result

    This is my code

    ChipReport Page

    @page "/ChipsReport"
    
    @using Data;
    @inject ChipsReportService crService
    
    <h3>ChipsReport</h3>
    
    <input id="txtDate" type="date" required @bind-value="paramChipReport.DataIni" />
    
    <select @bind="paramChipReport.ValueTypeId">
        <option value="42">Par </option>
        <option value="1">Par 2</option>
        <option value="36">Par 3</option>
        <option value="100">Par 4</option>
    </select>
    
    <select @bind="paramChipReport.Absolute">
        <option value="0">Inc</option>
        <option value="1">Abs</option>
    </select>
    
    <button class="btn btn-primary" @onclick="@(() => LoadData(paramChipReport))">Load data</button>
    
    <br />
    <hr />
    
    @if (@chipReports == null)
    { 
        <h2>Noting</h2>
    }
    else
    {
        <table class="table table-sm table-striped">
            <thead>
                <tr>
                    <th>Tag</th>
                    <th>Date</th>
                    <th>Total</th>       
                </tr>
            </thead>
            <tbody>
                @foreach (var cr in @chipReports)
                {
                    <tr>
                        <td>@cr.Tag</td>
                        <td>@cr.DataIni.ToShortDateString()</td>
                        <td>@cr.Total</td>
                    </tr>
                }
            </tbody>
        </table>
    }
    
    
    
    @code {
        public class ParamChipReport
        {
            public DateTime DataIni { get; set; }
            public int ValueTypeId { get; set; }
            public int Absolute { get; set; }
        };
    
        public ParamChipReport paramChipReport = new ParamChipReport();
    
        public List<ChipReport> chipReports;
    
        private async Task<List<ChipReport>> LoadData(ParamChipReport paramChipReport)
        {
            return chipReports = await crService.GetChipsReportsAsync(paramChipReport.DataIni, paramChipReport.ValueTypeId, paramChipReport.Absolute);
        }
    }

    Method service for data

    public async Task<List<ChipReport>> GetChipsReportsAsync(DateTime DataIni, int valueTypeId, int absolute)
            {
                
                var param = new SqlParameter[] 
                {
                    new SqlParameter() {ParameterName = "@dataini", SqlDbType = System.Data.SqlDbType.DateTime, Direction = System.Data.ParameterDirection.Input, Value = gamingDate },
                    new SqlParameter() {ParameterName = "@valuetypeid", SqlDbType = System.Data.SqlDbType.Int, Direction = System.Data.ParameterDirection.Input, Value = valueTypeId },
                    new SqlParameter() {ParameterName = "@absolute", SqlDbType = System.Data.SqlDbType.Int, Direction = System.Data.ParameterDirection.Input, Value = absolute}
                };
        
                chipsReport = await _context
                               .chipsReports
                               .FromSqlRaw("EXECUTE [Accounting].[usp_ChipsReportEx] @dataini, @valuetypeid, @absolute ", param)
                               .ToListAsync();
        
                return chipsReport;
            }

    Anyone can help me to undestand... ??

    Exist other approach?

    BR

    Tuesday, October 6, 2020 11:28 AM
  • User-474980206 posted

    you really should read the lifecycle, especially events and binding. I believe you need the [Parameter] attribute, as you have no binding expression.

    if the [Parameter] doesn't work, the easiest way to force a re-render is to call StateHasChanged().

    Tuesday, October 6, 2020 6:35 PM
  • User433211055 posted

    Hi
    I modify my section code in this mode, but doesn't work

        @code {
            public class ParamChipReport
            {
                public DateTime GamingDate { get; set; }
                public int ValueTypeId { get; set; }
                public int Absolute { get; set; }
            };
        
            public ParamChipReport paramChipReport = new ParamChipReport();
        
            [Parameter]
            public List<ChipReport> chipReports { get; set; }
        
            [Parameter]
            public EventCallback<List<ChipReport>> ChipReportChanged { get; set; }
        
            private async Task LoadData()
            {
                chipReports = await crService.GetChipsReportsAsync(paramChipReport.GamingDate, paramChipReport.ValueTypeId, paramChipReport.Absolute);
                await ChipReportChanged.InvokeAsync(chipReports);
            }
        }

    BR

    And service method

    public async Task<List<ChipReport>> GetChipsReportsAsync(DateTime gamingDate, int valueTypeId, int absolute)
            {
                var param = new SqlParameter[]
                {
                   new SqlParameter() { ParameterName = "@gaming", SqlDbType = System.Data.SqlDbType.DateTime,  Direction = System.Data.ParameterDirection.Input, Value = gamingDate },
                   new SqlParameter() { ParameterName = "@valuetypeid", SqlDbType = System.Data.SqlDbType.Int, Direction = System.Data.ParameterDirection.Input, Value = valueTypeId},
                   new SqlParameter() { ParameterName = "@absolute", SqlDbType = System.Data.SqlDbType.Int, Direction = System.Data.ParameterDirection.Input, Value = absolute }
                };
    
                return await Task.FromResult(
                               _context
                              .chipsReports
                              .FromSqlRaw("EXEC [Accounting].[usp_ChipsReportEx] @gaming, @valuetypeid, @absolute", param)
                              .ToList()
                    ); 
            }

    Friday, October 9, 2020 3:09 PM
  • User-474980206 posted

    my only guess is that the event handler is confused by the lambda. try:

       onclick="@(async (e) => await LoadData())"

    or 

       onclick="LoadData" 

    and loaddata to:

      private async Task LoadData()
      {
         chipReports = await crService.GetChipsReportsAsync(paramChipReport.GamingDate, paramChipReport.ValueTypeId, paramChipReport.Absolute);
      }

    Saturday, October 10, 2020 7:55 PM