Updating task list

All changes should happen in the view/task_list.rs

In relm4-store components there is pagination component ready for you to use. Let's import it

use relm4::Components;
use relm4::RelmComponent;

use components::pagination::PaginationMsg;
use components::pagination::PaginationConfiguration;
use components::pagination::PaginationViewModel;

use store::StoreViewInnerComponent;

Now we need to add a component for the tasks list

pub struct TasksListComponents<Config>
where Config: TasksListConfiguration + 'static {
    pagination: RelmComponent<PaginationViewModel<Self>, TasksListViewModel<Config>>
}

impl<Config> Components<TasksListViewModel<Config>> for TasksListComponents<Config> 
where Config: TasksListConfiguration,
{
    fn init_components(
        parent_model: &TasksListViewModel<Config>, 
        parent_sender: Sender<<TasksListViewModel<Config> as ViewModel>::Msg>
    ) -> Self {
        Self {
            pagination: RelmComponent::new(parent_model, parent_sender.clone()),
        }
    }

    fn connect_parent(&mut self, _parent_widgets: &TasksListViewWidgets) {}
}

impl<Config> PaginationConfiguration for TasksListComponents<Config>
where Config: TasksListConfiguration + 'static {
    type StoreViewPrototype = TasksListViewModel<Config>;

    fn get_view(parent_view_model: &<Self::StoreViewPrototype as StoreViewPrototype>::ViewModel) -> View<Self::StoreViewPrototype> {
        parent_view_model.store_view.clone()
    }
}

impl<Config> StoreViewInnerComponent<TasksListViewModel<Config>> for TasksListComponents<Config>
where Config: TasksListConfiguration + 'static {
    fn on_store_update(&mut self) {
        self.pagination.send(PaginationMsg::StoreUpdated).unwrap();
    }
}

TaskListComponent and pagination are normal relm4 components. For TaskListComponent we need to implement two extra traits. First is PaginationConfiguration. As name implies it provides configuration for pagination component. Second one and more interesting is StoreViewInnerComponent. This one provides a way to notify components when there is a change in the store. This allows to solve chicken and the egg problem of what's first store view or the pagination. Without view there is no point in pagination but pagination must own the view since it manages it.

Since we've created component for task list, now we need to add it to the relm4::Model

impl<Config> ViewModel for TasksListViewModel<Config> 
where Config: TasksListConfiguration + 'static,
{
    type Msg = TaskMsg;
    type Widgets = TasksListViewWidgets;
    type Components = TasksListComponents<Config>;
}

What's left is to add bunch of 'static lifetimes for Config generic attribute all around the file (compiler will tell you where). This is required because compiler can't infer the lifetime of some of the types.

Hopefully full list of the places to add 'static lifetime:

  • implementation of the ViewModel for TaskListViewModel
  • implementation of the StoreViewPrototype for TaskListViewModel
  • implementation of the FactoryContainerWidget for TasksListViewWidgets

Now let's put a cherry on top and add the pagination to the TaskListViewWidgets.

#[widget(visibility=pub, relm4=reexport::relm4)]
impl<Config: TasksListConfiguration> Widgets<TasksListViewModel<Config>, Config::ParentViewModel> for TasksListViewWidgets {
    view!{
        root = gtk::Box {
            set_margin_all: 12,
            set_orientation: gtk::Orientation::Vertical,
            append = &gtk::Entry::with_buffer(&model.new_task_description) {
                connect_activate(sender) => move |_| { 
                    send!(sender, TaskMsg::New); 
                } 
            },
            append = &gtk::ScrolledWindow {
                set_hexpand: true,
                set_vexpand: true,
                set_child: container = Some(&gtk::Box) {
                    set_orientation: gtk::Orientation::Vertical,
                    factory!(model.store_view)
                }
            },
            append: components.pagination.root_widget()
        }
    }
}

Last append adds pagination to the view.

This ends the story of adding pagination to the task list. Source code can be found in todo_2 example