azizbekian
azizbekian

Reputation: 62209

Inject activity's ViewModel into fragment's ViewModel

I've setup a project very similar to GithubBrowserSample. So, the dagger setup is the same.

Consider there are ActivityViewModel and FragmentViewModel, which have non-zero arg constructor, so they are being acquired from ViewModelProviders via custom ViewModelProvider.Factory.

What I want is to instruct dagger to inject already created instance of ActivityViewModel in following code:


    class FragmentViewModel @Inject constructor(
        private val activityViewModel: ActivityViewModel
        private val foo: Foo
    ) : ViewModel() {
        ...
    }

With current declaration Dagger will create a new instance of ActivityViewModel regardless that there already exists one.

This happens, because there exists an @Inject annotated constructor for ActivityViewModel.

So, dagger is free to assume, that it is the correct way of providing an instance of ActivityViewModel to FragmentViewModel.

I know how to make things for ordinary Dagger, but I do not know how to do this for Dagger-Android, and this questions is specifically for Dagger-Android setup.

As a dirty solution I'm currently manually setting that instance:


    class MyFragment : Fragment {
      ...
      override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(FragmentViewModel::class.java)
        viewModel.activityViewModel = ViewModelProviders.of(activity!!, viewModelFactory).get(ActivityViewModel::class.java)
      }
      ...
    }

What's the correct way of injecting parent's ViewModel into child's ViewModel?

Upvotes: 4

Views: 1759

Answers (1)

azizbekian
azizbekian

Reputation: 62209

This is not an answer to the question, rather this is an approach that I've come up with and currently use.

Having declared following extension functions:

inline fun <reified T : ViewModel> Fragment.getViewModel(
    factory: ViewModelProvider.Factory = ViewModelProvider.NewInstanceFactory()
) = ViewModelProviders.of(this, factory).get(T::class.java)

inline fun <reified T : ViewModel> Fragment.getParentViewModel(
    factory: ViewModelProvider.Factory = ViewModelProvider.NewInstanceFactory()
) = ViewModelProviders.of(activity!!, factory).get(T::class.java)

Then in a fragment class we can declare following:

private val parentViewModel by lazy { getParentViewModel<ParentViewModel>(viewModelFactory) }
private val childViewModel by lazy {
  val field = getViewModel<ChildViewModel>(viewModelFactory)
  field.parentViewModel = parentViewModel
  field
}

Upvotes: 1

Related Questions