Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Property - RadzenMultiDayView->AdvanceDays #1855

Merged
merged 1 commit into from
Dec 17, 2024

Conversation

paulo-rico
Copy link
Contributor

Rather than hardcoding how many days this view will Advance or Retract, currently set to equal the NumberOfDays displayed, this is a new property that allows you to set it explicitly.

Additional changes

  • There were some errors I made in the comments for DaySlotEvents. Now rectified.
  • When NumberOfDays slider is changed, it will call StateHasChanged() rather than reloading the Schedule (see notes below)

NOTES (let me know if you want me to raise issues for these)

Everything about the RadzenScheduler updates fine, with the exception of the title. This can be highlighted on the demo with the year views. Selecting a new StartMonth using the RadzenSelectBar updates all but the title. If January is selected, it should just show the current year. Any other month will show from and to. If you switch between, you will see that the title is one step behind, i.e.

  1. Select another month (this should now show a from and to. It doesn't)
  2. Select January (this should now just show the current year. It doesn't)
  3. Select another month (this should now show a from and to). It now shows just the current year)

Would address it but I've not been successful in my attempts. Been getting out of memory errors. Obviously, my attempts at inserting StateHasChanged in certain places has caused some sort of infinite loop!

For the NumberOfDays slider, I call StateHasChanged on the Change event to rectify this. Sure that this shouldn't be required and be part of the component.

Also, for the life in me, I couldn't get the Change event working on the RadzenSelectBar. May need looking into (the demo page has a section "Get and Set the value of SelectBar using Value and Change event" that doesn't seem to demonstrate this).

Regards
Paul

@akorchev
Copy link
Collaborator

akorchev commented Dec 16, 2024

Hi @paulo-rico,

I am not sure how to respond to this PR to be honest :) It is a combination of a PR and questions that seem unrelated.

In general if you want a child component to update its parent (which is where the title is rendered) one needs to invoke a method of the parent. Blazor in general won't update a parent if its child changes. This is why I think the title doesn't update when you check a property of a view.

The Change event of RadzenSelectBar seems to work just fine:

<div class="rz-p-12 rz-text-align-center">
    <RadzenSelectBar @bind-Value=@value TValue="bool" Change="@OnChange">
        <Items>
            <RadzenSelectBarItem Text="On" Value="true" />
            <RadzenSelectBarItem Text="Off" Value="false" />
        </Items>
    </RadzenSelectBar>
</div>
<EventConsole @ref="@console" />

@code {
    bool value;

    EventConsole console;

    void OnChange(bool value)
    {
        console.Log($"Value: {value}");
    }
}

This works as expected in our demo. We will update the demo to show that.

@paulo-rico
Copy link
Contributor Author

Hi @akorchev

Yeah, I did go off at various tangents with this one :)

Although not related to the component in the PR, the notes were issues with other components I came across while creating the demo. Ignore the notes for now. I'll raise them in a GitHub Issue.

BTW, I did get Change event of SelectBar working, as per your code above. The thing that was missing in the demo was TValue="Month". It wouldn't compile without that.

Thanks and regards
Paul

@akorchev
Copy link
Collaborator

To answer your question about why StateHasChanged() or scheduler.Reload() is needed:

You update a child component of the scheduler (the RadzenMultiDayView's AdvanceDays for example). This won't by itself make the scheduler render again as it doesn't update an attribute of the scheduler. You have to either call Reload() (which in effect does StateHasChanged() internally) or call the page's StateHasChanged() method (which also makes the scheduler to render again). The ideal solution is to listen for changes in the child and update the parent. A similar thing is done in RadzenChart - check the ChartComponentBase for example. If a property that affects the parent component is updated we have to refresh it manually.

@paulo-rico
Copy link
Contributor Author

Thanks Atanas, I'll have a look at that.

@inject DialogService DialogService

<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem" class="rz-p-4 rz-mb-6 rz-border-radius-1" Style="border: var(--rz-grid-cell-border);">
    <RadzenLabel Text="Schedule Start Month:" />
    <RadzenSelectBar TValue="Month" Change="@StartMonthChange" @bind-Value="@startMonth" TextProperty="Text" ValueProperty="Value" Data="@(Enum.GetValues(typeof(Month)).Cast<Month>().Select(t => new { Text = $"{t}", Value = t }))" Size="ButtonSize.Small" class="rz-display-none rz-display-xl-flex" />
    <RadzenDropDown @bind-Value="@startMonth" Change="StartMonthChange" TextProperty="Text" ValueProperty="Value" Data="@(Enum.GetValues(typeof(Month)).Cast<Month>().Select(t => new { Text = $"{t}", Value = t }))" class="rz-display-inline-flex" />
</RadzenStack>

<RadzenScheduler @ref=@scheduler SlotRender=@OnSlotRender style="height: 768px;" TItem="Appointment" Data=@appointments StartProperty="Start" EndProperty="End"
    TextProperty="Text" SelectedIndex="1"
    SlotSelect=@OnSlotSelect AppointmentSelect=@OnAppointmentSelect AppointmentRender=@OnAppointmentRender MonthSelect=@OnMonthSelect>
    <RadzenMonthView />
    <RadzenYearPlannerView StartMonth="@startMonth" />
    <RadzenYearTimelineView StartMonth="@startMonth" />
    <RadzenYearView StartMonth="@startMonth" />
</RadzenScheduler>

<EventConsole @ref=@console />

@code {
    RadzenScheduler<Appointment> scheduler;
    EventConsole console;
    Dictionary<DateTime, string> events = new Dictionary<DateTime, string>();
    Month startMonth = Month.January;

    IList<Appointment> appointments = new List<Appointment>
    {
        new Appointment { Start = DateTime.Today.AddDays(-2), End = DateTime.Today.AddDays(-2), Text = "Birthday" },
        new Appointment { Start = DateTime.Today.AddDays(-11), End = DateTime.Today.AddDays(-10), Text = "Day off" },
        new Appointment { Start = DateTime.Today.AddDays(-10), End = DateTime.Today.AddDays(-8), Text = "Work from home" },
        new Appointment { Start = DateTime.Today.AddHours(10), End = DateTime.Today.AddHours(12), Text = "Online meeting" },
        new Appointment { Start = DateTime.Today.AddHours(10), End = DateTime.Today.AddHours(13), Text = "Skype call" },
        new Appointment { Start = DateTime.Today.AddHours(14), End = DateTime.Today.AddHours(14).AddMinutes(30), Text = "Dentist appointment" },
        new Appointment { Start = DateTime.Today.AddDays(1), End = DateTime.Today.AddDays(12), Text = "Vacation" },
    };

    async Task StartMonthChange()
    {
        StateHasChanged();
    }

    void OnSlotRender(SchedulerSlotRenderEventArgs args)
    {
        // Highlight today in month view
        if (args.View.Text == "Month" && args.Start.Date == DateTime.Today)
        {
            args.Attributes["style"] = "background: var(--rz-scheduler-highlight-background-color, rgba(255,220,40,.2));";
        }

        // Draw a line for new year if start month is not January
        if ((args.View.Text == "Planner" || args.View.Text == "Timeline") && args.Start.Month == 12 && startMonth != Month.January)
        {
            args.Attributes["style"] = "border-bottom: thick double var(--rz-base-600);";
        }
    }

    async Task OnSlotSelect(SchedulerSlotSelectEventArgs args)
    {
        console.Log($"SlotSelect: Start={args.Start} End={args.End}");

        if(args.View.Text != "Year")
        {            
            Appointment data = await DialogService.OpenAsync<AddAppointmentPage>("Add Appointment",
                new Dictionary<string, object> { { "Start", args.Start }, { "End", args.End } });

            if (data != null)
            {
                appointments.Add(data);
                // Either call the Reload method or reassign the Data property of the Scheduler
                await scheduler.Reload();
            }
        }
    }

    async Task OnMonthSelect(SchedulerMonthSelectEventArgs args)
    {
        console.Log($"MonthSelect: MonthStart={args.MonthStart} AppointmentCount={args.Appointments.Count()}");
        await Task.CompletedTask;
    }

    async Task OnAppointmentSelect(SchedulerAppointmentSelectEventArgs<Appointment> args)
    {
        console.Log($"AppointmentSelect: Appointment={args.Data.Text}");

        await DialogService.OpenAsync<EditAppointmentPage>("Edit Appointment", new Dictionary<string, object> { { "Appointment", args.Data } });

        await scheduler.Reload();
    }

    void OnAppointmentRender(SchedulerAppointmentRenderEventArgs<Appointment> args)
    {
        // Never call StateHasChanged in AppointmentRender - would lead to infinite loop

        if (args.Data.Text == "Birthday")
        {
            args.Attributes["style"] = "background: red";
        }
    }
}

Although, again, not related to the component in the PR (sorry :) ), I tried using the Change event of the RadzenSelectBar to call StateHasChanged. In the above code, I have made the original RadzenDropDown visible. They both call the same function on Change event. The RadzenDropDown works as expected. The RadzenSelectBar doesn't. Not sure if I'm missing something.

@akorchev
Copy link
Collaborator

To me both work in identical way. To be honest I don't think the problem is in RadzenDropdown or RadzenSelectBar.
scheduler

@paulo-rico
Copy link
Contributor Author

It is easily missed, but it does show on your post.

This is just to do with the Title of the Scheduler. Everything else updates correctly.

The only time one year should be shown, is with January. Any other month will show a from and to, which could be last year to this year, or this year to next year, depending on the current date.

When you change to January (after changing to another month) using the RadzenDropDown it updates immediately.

When you change to January using the RadzenSelectBar (after changing to another month) it remains as a range. Change to another Month, and it goes to a single year, as you would expect from selecting January. It's as if the refresh of the Scheduler is one step behind.
👍

@akorchev
Copy link
Collaborator

No idea why this happens as the code which triggers the change event is quite similar. You can debug it though.

@paulo-rico
Copy link
Contributor Author

paulo-rico commented Dec 17, 2024

I'll leave this PR as is, as it has nothing to do with RadzenSelectBar

I'll try and debug what the difference between the RadzenDropDown and RadzenSelectBar Change event that is resulting in such behaviour. If I find anything, I'll raise a new PR.

Stay well and speak soon

@akorchev
Copy link
Collaborator

Thanks. Merging this now.

@akorchev akorchev merged commit 0c56f20 into radzenhq:master Dec 17, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants