0

In Dagger, how can I add a model to Module? For example I added the presenter in the following way:

@Module
class AboutModule(val appContext: Context) {

    @FragmentScope
    @Provides
    fun providePresenter(): AboutListContract.Presenter {
        return AboutListPresenter(appContext = appContext)
    }
}

Now i want want to add my View model, also with appContext.

class AboutViewModel(val appContext: Context): ViewModel() {

UPDATE: Can i add my view model smth like this?

@Module

    class AboutModule(val appContext: Context) {

        @FragmentScope
        @Provides
        fun provideModel(model: AboutViewModel): ViewModel {
            return AboutViewModel(appContext = appContext)
        }
    }
Morozov
  • 4,968
  • 6
  • 39
  • 70

3 Answers3

1

I solved my problem with next solution:

@Module
class AboutModule(val appContext: Context) {
    
    @FragmentScope
    @Provides
    fun provideFactory(): AboutViewModelFactory {
        return AboutViewModelFactory(appContext)
    }
}

And in fragment write smth like this:

class AboutFragment : BaseFragment(), OnItemClickListener {

    lateinit var viewModel: AboutViewModel
    @Inject lateinit var viewModelFactory: AboutViewModelFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        injectDependencies()

        viewModel = ViewModelProviders
            .of(this, viewModelFactory)
            .get(AboutViewModel::class.java)
    }

    private fun injectDependencies() {
        activity?.let {
            DaggerAboutComponent.builder().aboutModule(AboutModule(it)).build().inject(this)
        }
    }
}

Nice advise: https://stackoverflow.com/a/60884492/6387618

jasont20015
  • 87
  • 1
  • 2
  • 7
Morozov
  • 4,968
  • 6
  • 39
  • 70
1

To retrieve the ViewModel from the component without Map Multibinding, you can do:

@Singleton
@Component(modules=[...])
interface SingletonComponent {
    val aboutListViewModel: Provider<AboutListViewModel>
}

Which works when you can use @Inject constructor:

// unscoped
class AboutListViewModel @Inject constructor(): ViewModel() {
}

Because now you can do:

class AboutListFragment: Fragment(R.layout.about_list_fragment) {
    private val viewModel by viewModels<AboutListViewModel>() {
        object: ViewModelProvider.Factory {
            override fun create(clazz: Class<ViewModel>) {
                return (requireActivity().application as MyApplication).component.aboutListViewModel.get()
            }
        }
    }
}

Which might seem tacky, but you can hide all of that in an extension function

fun <T: ViewModel> Fragment.fragmentViewModels(viewModelCreator: SingletonComponent.() -> T) = viewModels<T> {
    object: ViewModelProvider.Factory {
        override fun create(clazz: Class<ViewModel>) {
            val component = requireActivity().application as MyApplication).component
            return viewModelCreator(component)
        }
    }
}        

Because now you can do

class AboutListFragment: Fragment(R.layout.about_list_fragment) {
    private val viewModel by fragmentViewModels<AboutListViewModel> { component ->
        component.aboutListViewModel().get()
    }
}

And this would work even without map multibinding. If you need a SavedStateHandle, you'd need AssistedInject, but if you don't want that, it'll be easier when Dagger-Hilt is stable (then it'll be as simple as @ViewModelInject and @Assisted).

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
0

ViewModels are injected using Dagger Multibindings

refer to this medium article: https://medium.com/chili-labs/android-viewmodel-injection-with-dagger-f0061d3402ff

context can be injected by providing in the AppModule class.

Nataraj KR
  • 1,001
  • 10
  • 22