# Dependency Injection

# Why Dependency Injection?

When developing your App you want to keep a maximum of business logic out of your Components. For example you can create Services that can be reused across Components.

But in Vue GWT you are not responsible for your Components instantiation. Apart from your application root, other Components are instantiated for you by Vue depending on your model.

So how do you pass the instance of those Services to your Components? Sure you could have a static instance of those Services as a Singleton, but this is awful for testing.

Luckily, Vue GWT supports Dependency Injection. An instance of your Services can be automatically provided when your Components are instantiated. You can also provide a mock yourself when you are testing your Components.

# Setting up Dagger 2

In GWT two solutions exist for Dependency Injection:

In this documentation we will explain Injection with Dagger 2 as GIN is not actively developed anymore and not recommended for new projects. However if you are using GIN already, Vue GWT also works with it.

To setup Dagger 2 on your project you can follow this guide: Dependency injection in GWT using Dagger 2.

The version of the Maven dependency in the guide is a little out of date. You can check for the latest version of Dagger GWT to use on the Dagger GWT Maven repo.

# Injecting a Vue GWT Component

# Declaring the Component

Let's say we have a GotQuotesService that is able to provide us with a random quote from a famous TV Show.

We want to use that service in a GotQuotesComponent that will display a random quote. We simply add GotQuotesService as an attribute of our Component with the @Inject annotation.

WARNING

It's not possible to use injected constructor parameters as Java constructors are not supported in Vue GWT Components.

@Component
public class GotQuotesComponent implements IsVueComponent, HasCreated {
    @Data GotQuote quote;
    @Inject GotQuotesService gotQuotesService;

    @Override
    public void created() {
        changeQuote();
    }

    @JsMethod
    void changeQuote() {
        quote = gotQuotesService.getRandomQuote();
    }
}

We can then simply use quote in our GotQuotesComponent template:

<div>
    <blockquote>
        {{ quote.getText() }}
        <cite>
            **{{ quote.getAuthor() }}**, Season **{{ quote.getSeason() }}**, Episode **{{ quote.getEpisode() }}**
        </cite>
    </blockquote>
    <button @click="changeQuote">Give me another!</button>
</div>

To make this Component work we now must find a way to have it Injected.

# Instantiating the GotQuotesComponent with Injection

Every Vue GWT Component get an associated Factory generated for them by the Vue GWT annotation processor. This means that GotQuotesComponent has a generated GotQuotesComponentFactory.

To bootstrap injection we need to inject GotQuotesComponentFactory. Every instance of our GotQuotesComponent created using the injected GotQuotesComponentFactory will then be correctly injected.

To inject this factory we declare a Dagger 2 Component.

WARNING

Dagger 2 has it's own Component annotation. So you must be careful to use the right one when declaring your Dagger 2 Component.

@Component // ⚠️ This is Dagger 2 @Component annotation, not the Vue GWT one.
@Singleton
public interface ExampleInjector {
    GotQuotesComponentFactory gotQuoteComponentFactory();
}

TIP

If GotQuotesComponentFactory doesn't exist, then check your annotation processor configuration in your IDE to make sure that it's running.

We can then create our Dagger Injector, get our GotQuotesComponentFactory and use it to create our injected GotQuotesComponent:

public class VueGwtExamplesApp implements EntryPoint { 
    public void onModuleLoad() {
        // Create Dagger Injector
        ExampleInjector exampleInjector = DaggerExampleInjector.builder().build();
        // Get our Factory from the Injector
        GotQuotesComponentFactory factory = exampleInjector.gotQuoteComponentFactory();
        // Create our Component, it will be correctly injected
        GotQuotesComponent component = factory.create();
        // Mount (attach) our Component to an existing div
        component.$mount("#gotQuotesComponent");
    }
}

And here is the resulting live Component:


# Injecting Component's Children

We injected our root Component, that's great, but how about it's children?

Here is the good news: if you inject your root Component, then the whole tree of locally declared Components will be injected 🎉!

For example if you have the following Components, you only need to inject RootComponentFactory and use it to create your RootComponent. All the Components used in the template and declared in components will be injected.

# RootComponent

@Component(components = {Child1Component.class, Child2Component.class})
public class RootComponent implements IsVueComponent {}
<div>
    <!-- Create 10 instances of Child1Component -->
    <child1 v-for="int i in 10"></child1>
    <child2></child2>
</div>

# Child1Component

@Component(components = GrandChild1Component.class)
public class Child1Component implements IsVueComponent {
    @Inject MyService myService;
}
<div>
    <grand-child1></grand-child1>
</div>

# Child2Component

@Component
public class Child2Component implements IsVueComponent {
    @Inject MyService myService;
    @Inject AnotherService anotherService;
}

# GrandChild1Component

@Component
public class GrandChild1Component implements IsVueComponent {
    @Inject AnotherService anotherService;
}

# Globally Registered Components

It's also possible to inject Components that are declared globally. For this you only need to inject their factory and use it for the global registration. When used in the templates, the instances will automatically be injected.

public class VueGwtExamplesApp implements EntryPoint { 
    public void onModuleLoad() {
        ExampleInjector exampleInjector = DaggerExampleInjector.builder().build();
        MyGlobalComponentFactory factory = exampleInjector.getMyGlobalComponentFactory();
        VueGWT.register("my-global-component", factory);
    }
}

# Injecting Routes in Vue GWT Router

If you are using Vue GWT Router, you can also inject your Routes.

public class RoutesConfig implements CustomizeOptions {
    @Inject LoginComponentFactory loginComponentFactory;
    @Inject HomeComponentFactory homeComponentFactory;
    @Inject SettingsComponentFactory settingsComponentFactory;

    @Override
    public void customizeOptions(VueComponentOptions vueComponentOptions) {
        RouterOptions routerOptions = new RouterOptions()
            .setMode(RouterMode.HISTORY)
            .addRoute("/login", loginComponentFactory)
            .addRoute("/home", homeComponentFactory)
            .addRoute("/settings", homeComponentFactory);
        
        vueComponentOptions.set("router", new VueRouter(routerOptions));
    }
}