Step #12: Making Another Motor

Now, let’s create another ViewModel implementation that uses the same pattern as RosterMotor, but for a single model based on its ID. We can use this both for DisplayFragment and the EditFragment that we will create in the next tutorial.

Right-click over the com.commonsware.todo package in the java/ directory and choose “New” > “Kotlin File/Class” from the context menu. For the name, fill in SingleModelMotor, then choose “Class” for the kind. Then press Enter or Return to create the empty SingleModelMotor class.

Then, replace its contents with:

package com.commonsware.todo

import androidx.lifecycle.ViewModel

class SingleModelMotor(
  private val repo: ToDoRepository,
  private val modelId: String
) : ViewModel() {
}

Like RosterMotor, this takes a ToDoRepository as a constructor parameter. Unlike RosterMotor, this also takes the ID of the ToDoModel that we want to use, as another constructor parameter.

To actually retrieve the ToDoModel for this ID, we could just rummage through repo.items here in RosterMotor. It will be cleaner to have ToDoRepository do this. So, add this find() function to ToDoRepository:

  fun find(modelId: String) = items.find { it.id == modelId }

Right now, all this does is scan through the items list and find the matching ToDoModel. Later on, we will do a database query to find the to-do item in the database.

Then, add a corresponding getModel() function to SingleModelMotor:

  fun getModel() = repo.find(modelId)

This just uses the repo and modelId to retrieve the ToDoModel and returns it.

We need to teach Koin about this viewmodel, the same way that we did with RosterMotor. So, in ToDoApp, add this line to the koinModule declaration:

    viewModel { (modelId: String) -> SingleModelMotor(get(), modelId) }

This is a bit different than the viewModel() call to set up RosterMotor:

    viewModel { RosterMotor(get()) }

This time, we want to provide a parameter when we retrieve the SingleModelMotor: the model ID of the ToDoModel that we want to display. Koin has no way of getting this value on its own, the way it can for the ToDoRepository singleton. So, we set up the lambda expression to return a function type, one that takes our modelId as a parameter and uses it to construct the SingleModelMotor instance.

Then, in DisplayFragment, add this property:

  private val motor: SingleModelMotor by viewModel { parametersOf(args.modelId) }

This time, instead of just using by viewModel(), we use a variant that employs parametersOf() to supply the parameters that get passed to our function type that we used in the Koin declaration. Here, we get the modelId out of our Navigation component arguments, wrap them using parametersOf(), and use that to set up the SingleModelMotor. The net result is that the model ID that we had in RosterListFragment — from when the user clicked the row — winds up in the hands of our viewmodel, and from there can be used to get the ToDoModel.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.