question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[GithubBrowserSample] Recommended way to inject dynamic parameters to ViewModel using Dagger 2

See original GitHub issue

Hi everyone! Thanks for great examples. I’m just wondering what method you recommend to inject dynamic parameters into ViewModel using Dagger 2? Currently in the GithubBrowserSample such parameters are injected by hand using setters:

userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class);
userViewModel.setLogin(getArguments().getString(LOGIN_KEY));

I’ve tried to move login String to the UserViewModel constructor but unfortunately, it caused the problem:


Error:(35, 8) error: [dagger.android.AndroidInjector.inject(T)] @javax.inject.Named("login") java.lang.String cannot be provided without an @Provides-annotated method.
@javax.inject.Named("login") java.lang.String is injected at
com.android.example.github.ui.user.UserViewModel.<init>(…, loginString)
com.android.example.github.ui.user.UserViewModel is injected at
com.android.example.github.di.ViewModelModule.bindUserViewModel(userViewModel)
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.android.example.github.viewmodel.GithubViewModelFactory.<init>(creators)
com.android.example.github.viewmodel.GithubViewModelFactory is injected at
com.android.example.github.di.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.android.example.github.ui.repo.RepoFragment.viewModelFactory
com.android.example.github.ui.repo.RepoFragment is injected at
dagger.android.AndroidInjector.inject(arg0)

So… what is your recommended way to pass dynamic parameters to the constructor instead of setters? I see one solution - using separate ViewModelFactory implementation per each ViewModel. What do you think about it? Do you have any other recommendations?

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:29
  • Comments:26

github_iconTop GitHub Comments

37reactions
magneticflux-commented, Oct 26, 2017

@radzio I feel like the best way to transfer data from View to ViewModel is by using those setter methods. In order for MVVM architecture to work, the View must push commands to the ViewModel and receive data from a binding of some sort. By allowing the ViewModel to “pull” data from the View (through a Dagger injection), the architecture is broken and you run into problems.

mvvm-light

You also have to keep in mind the lifecycle of your components. ViewModels exist for a long time, so they can’t reference any views. I think Dagger is not the right tool for the job, and that the userViewModel.setLogin(getArguments().getString(LOGIN_KEY)); that you want to get rid of is actually helping to clearly divide the architecture into Model, ViewModel, and View.

31reactions
Maraguescommented, Oct 27, 2017

@radzio the idea is to inject the ViewModel.Factory that’ll be responsible for creating the ViewModel

A possible approach is to provide the ViewModelProvider.Factory in each Activity/Fragment module, which you can specify in the BindingsModule. The problem is the verbosity needed to create a new screen: Activity + ActivityModule + ViewModel + ViewModelProvider.Factory with duplicated code (as you’ll see).

As an example, this’d be the ActivityModule

@Module
public class MyActivityModule {
  @Provides
  @ActivityScope
  ViewModelProvider.Factory providesViewModelFactory(MyActivity activity, MyDependency dependency) {
    return new MyActivityViewModel.Factory(dependency, activity.getLogin());
  }
}

This is the ViewModel + ViewModelProvider.Factory

public class MyActivityViewModel extends ViewModel {

  private final MyDependency dependency;
  private final String login;

  MyActivityViewModel(MyDependency dependency, String login) {
    this.facade = facade;
    this.login = login;
  }

  static class Factory implements ViewModelProvider.Factory {
    private final MyDependency dependency;
    private final String login;

    public Factory(MyDependency dependency, String login) {
      this.facade = facade;
      this.login = login;
    }

    @SuppressWarnings("unchecked")
    @Override
    public MyActivityViewModel create(Class modelClass) {
      return new MyActivityViewModel(dependency, login);
    }
  }
}

And then, the Activity

class MyActivity ...{
  @Inject
  ViewModelProvider.Factory viewModelFactory;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);

    viewModel = ViewModelProviders.of(this, viewModelFactory).get(MyActivityViewModel.class);
}

 String getLogin(){
   return getIntent.getString(EXTRA_LOGIN);
  }
}

There must be a better way, but it’s what I’m using.

Like this, it’s easy to provide the dependencies when testing ViewModels in isolation.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How To Inject ViewModel With Dagger & What Might Go Wrong
There are several methods. The easiest way is to inject view model providers into a factory and map them manually: Add this factory...
Read more >
dagger 2 - How to use AssistedInject to pass dynamic value as ...
This is the simple ViewModel I used for testing ViewModel injection. WeatherViewModel class WeatherViewModel @Inject constructor(val repository: ...
Read more >
ViewModel with Dagger2 (Android Architecture Components)
In this story I want to share some light on how you can use ViewModel (Android ... For @Inject with dynamic parameters read...
Read more >
Understanding Dagger 2 Multibindings + ViewModel
Many of us, when started learning how to use the new ViewModel class had a look at the GithubBrowserSample example on GitHub (this...
Read more >
Injecting Android ViewModels With Dagger2 in a Clean ...
Today, we're going to learn how to use ViewModel in clean architecture and build a dependency injection framework with Dagger2 so that we ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found