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
?
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
}