I thought DI was implemented to allow use the same services over the application, and change them as needed. However this snippet (Angular 2.0.0-beta.0) refuses to work:
# boot.ts
import {ProjectService} from './project.service'
bootstrap(AppComponent, [ProjectService]);
# my.component.ts
export class MyComponent {
constructor(project: ProjectService) {
}
}
and with explicit service requirement it works:
# my.component.ts
import {ProjectService} from './project.service';
export class MyComponent {
constructor(project: ProjectService) {
}
}
The official doc is somewhat inconsistent, but has the same in the plunkr example:
# boot.ts
import {HeroesListComponent} from './heroes-list.component';
import {HeroesService} from './heroes.service';
bootstrap(HeroesListComponent, [HeroesService])
# heroes-list.component.ts
import {HeroesService} from './heroes.service';
Is this the intended way of DI usage? Why we have to import service in every class requiring it, and where are the benefits if we can't just describe the service once on boot?
This isn't really related to dependency injection. You can't use a class in TS that is not imported.
This line references a class and DI derives from the type what instance to inject.
constructor(project: ProjectService) {
If the type isn't specified by a concrete import, DI can't know which of all possible ProjectService classes should be used.
What you can do for example, is to request a type (ProjectService) and get a different implementation (subclass like MockProjectService or EnhancedProjectService,...)
bootstrap(HeroesListComponent, [provide(ProjectService useClass: MockProjectService)]);
this way DI would inject a MockProjectService for the following constructor
constructor(project: ProjectService) {
Related
Happy Another Covid Day. When I use generate-all, Grails creates the Data Service for me. I begin to understand what a data service is.
I also have my own service for my Author and Book classes to use. I name my service ImportService. I have methods in the ImportService to clean up my book data read from a CSV file before the Data Service saves my books to the database. I also follow the instruction to make the Data Service an Abstract Class. So, I can put my own method in the Data Service.
Since the Author has its own AuthorService, and the Book has its own BookService, I want the different Data Service to access the method in my ImportService. So, I don't have to copy and paste the import CSV code multiple times. So, I put the line ImportService importService in the AuthorServie class and the BookService class. That does not go well. importService is always NULL inside the Data Service classes. I google the problem. They say I cannot inject another service to the grails.gorm.services.Service.
There is a post that says to make a bean. I am new to Grails. I have no idea what they are talking about even with the codes posted. Part of my background is Assembly Language, C, and Pascal. My head is filled with lingo like Top Down, Subroutine, library, Address, and Pointer. I have no idea what a Bean is.
This is what it is. I am wondering whether this is a bug or by design that you cannot inject a service to the gorm service.
Thanks for your "Pointer".
See the project at https://github.com/jeffbrown/tom6502servicedi. That project uses Grails 4.0.3 and GORM 7.0.7.
https://github.com/jeffbrown/tom6502servicedi/blob/main/grails-app/services/tom6502servicedi/ImportService.groovy
package tom6502servicedi
class ImportService {
int getSomeNumber() {
42
}
}
https://github.com/jeffbrown/tom6502servicedi/blob/917c51ee173e7bb6844ca7d40ced5afbb8d9063f/grails-app/services/tom6502servicedi/AuthorService.groovy
package tom6502servicedi
import grails.gorm.services.Service
import org.springframework.beans.factory.annotation.Autowired
#Service(Author)
abstract class AuthorService {
#Autowired
ImportService importService
// ...
int getSomeNumberFromImportService() {
importService.someNumber
}
}
https://github.com/jeffbrown/tom6502servicedi/blob/917c51ee173e7bb6844ca7d40ced5afbb8d9063f/grails-app/controllers/tom6502servicedi/AuthorController.groovy
package tom6502servicedi
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
class AuthorController {
AuthorService authorService
// ...
def someNumber() {
render "The Number Is ${authorService.someNumberFromImportService}"
}
}
Sending a request to that someNumber action will verify that the ImportService is injected into the AuthorService and the AuthorService is injected into the AuthorController.
$ curl http://localhost:8080/author/someNumber
The Number Is 42
I am newbie for Guice and seeking help for the following use case :
I have developed one package say (PCKG) where the entry class of that package depends on other class like:
A : Entry point class --> #Inject A(B b) {}
B in turn is dependent on C and D like --> #Inject B(C c, D d) {}
In my binding module I am doing :
bind(BInterface).to(Bimpl);
bind(CInterface).to(CImpl);
...
Note I am not providing binding information for A as i want to provide its binding by its consumer class. (this is how the design is so my request is to keep the discussion on main problem rather than design).
Now my consumer class is doing like:
AModule extends PrivateModule {
protected void configure() {
bind(AInterface.class).annotatedWith(AImpl.class);
}
}
Also in my consumer package:
.(new PCKGModule(), new AModule())
Q1. Am i doing the bindings correctly in consumer class. I am confused because when i am doing some internal testing as below in my consumer package:
class testModule {
bind(BInterface).to(Bimpl);
bind(CInterface).to(CImpl)...
}
class TestApp {
public static void main(..) {
Guice.createInstance(new testModule());
Injector inj = Guice.createInstance(new AModule());
A obj = inj.getInstance(A.class);
}
}
It is throwing Guice creation exception.Please help me get rid of this situation.
Also one of my friend who is also naive to Guice was suggesting that I need to create B's instance in AModule using Provides annotation. But i really didn't get his point.
Your main method should look like this:
class TestApp {
public static void main(..) {
Injector injector = Guice.createInjector(new TestModule(), new AModule());
A obj = injector.getInstance(A.class);
}
Note that the Java convention is for class names to have the first letter capitalised.
I'm pretty sure your implementation of AModule isn't doing what you think it's doing either, but it's hard to be certain based on the information you've provided. Most likely, you mean to do this:
bind(AInterface.class).to(AImpl.class)`
There's no need to do anything "special" with A's binding. Guice resolves all the recursion for you. That's part of its "magic".
annotatedWith() is used together with to() or toInstance(), like this:
bind(AInterface.class).to(AImpl.class).annotatedWIth(Foo.class);
bind(AInterface.class).to(ZImpl.class).annotatedWIth(Bar.class);
Then you can inject different implementations by annotating your injection points, e.g.:
#Inject
MyInjectionPoint(#Foo AInterface getsAImpl, #Bar AInterface getsZImpl) {
....
}
It's worth also pointing out that you can potentially save yourself some boilerplate by not bothering with the binding modules (depending how your code is arranged) and using JIT bindings:
#ImplementedBy(AImpl.class)
public interface AInterface {
....
}
These effectively act as "defaults" which are overridden by explicit bindings, if they exist.
I'm working with Guice and have one design question. My App consists of few module:
myapp-persistence (JPA Entities, DAO, other DB related stuff)
myapp-backend (Some background daemons, they use myapp-persistence )
myapp-rest (REST app that depends on myapp-persistence)
myapp-persistence must have singleton HibernateSessionFactory. It's by Hibernate design.
No problem I can solve it with Guice:
class MyAppPersistenceModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[SomeStuff])
bind(classOf[ClientDao])
bind(classOf[CustomerDao])
bind(classOf[SessionFactory]).toProvider(classOf[HibernateSessionFactoryProvider]).asEagerSingleton()
}
#Provides
def provideDatabaseConnectionConfiguration: DatabaseConnectionConfiguration = {
DatabaseConnectionConfiguration.fromSysEnv
}
}
The problem with passing DatabaseConnectionConfiguration to that singleton. myapp-persistence module doesn't really care how to get that config. Right now it's taken from sys variables.
myapp-rest is play-app and it wants to read conf from application.conf and inject it into other components using Guice.
myapp-backend does more or less the same.
Right now I'm locked myself with
#Provides
def provideDatabaseConnectionConfiguration: DatabaseConnectionConfiguration = {
DatabaseConnectionConfiguration.fromSysEnv
}
And I don't understand how to make it flexible and configurable for myapp-rest and myapp-backend.
UPD
According to answer, I did it this way:
Defined trait
trait DbConfProvider {
def dbConf: DbConf
}
Singleton factory now depends on provider:
class HibernateSessionFactoryProvider #Inject()(dbConfProvider: DbConfProvider) extends Provider[SessionFactory] {
}
myapp-persistence module exposes public guice module with all piblic persistence module DAO.
myapp-persistence has module used only for testing purposes. myapp-persistence Injector load module described below:
class MyAppPersistenceDbConfModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[DbConfProvider]).to(classOf[DbConfSysEnvProvider])
}
}
DbConfSysEnvProvider reads DB connection settings from sys env. Non production use case.
Play app has it's own conf mechanism. I've added my custom module to app conf:
# play-specific config
play.modules.enabled += "common.components.MyAppPersistenceDbConfModule"
# public components from myapp-persistence module.
play.modules.enabled += "com.myapp.persistence.connection.PersistenceModule"
And my configuration service:
#Singleton
class ConfigurationService #Inject()(configuration: Configuration) extends DbConfProvider {
...}
I am not an expert on Play-specific setup, but generally this kind of design problem is solved in one of the following ways:
No default. Remove the binding of DatabaseConnectionConfiguration from the upstream module (myapp-persistence), and define it in each downstream module (myapp-backend, myapp-rest) as appropriate.
Default with override. Keep the default binding of DatabaseConnectionConfiguration like you did, implementing the most common configuration strategy there. Override it in downstream modules using Guice Modules.override(..) API when needed.
Implement a unified configuration mechanism across the modules, that does not depend on particular frameworks used. (E.g. Bootique, which is built on Guice ... Haven't used it with Play though).
I personally prefer the approach #3, but in the absence of something like Bootique, #2 is a good substitute.
Unlike the other hooks, CanActivate is a function separated from the component.
import 'dart:async';
import 'package:angular2/core.dart';
import 'package:angular2/router.dart';
#Component(selector: 'my-app', template: '<h1>Hello World!</h1>')
#CanActivate(someGuard)
class HelloComponent {}
FutureOr<bool> someGuard(ComponentInstruction next, ComponentInstruction prev) {
return true;
}
AFAIK, it is possible to intercept the root injector at bootstrap time and assign it to a global variable, so that I can access it anywhere. IMO, that sounds like a hack and I don't like it. Is there a proper way to inject a service in CanActivate hook?
Angulars DI only supports constructor injection. #CanActivate() is an annotation and therefore doesn't support DI.
There is a workaround though.
Create a variable in a library and import it
Injector injector;
assign the injector when the application is created
bootstrap(App, [
Auth,
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
provide(LocationStrategy, useClass: HashLocationStrategy)
]).then((appRef: ComponentRef) => {
// store a reference to the application injector
injector = appRef.injector;
});
then you can import the library with the injector and access it from code someGuard()
I have read several articles on dependency injection and cannot seem to get it working the way I think it should. From what I have read, you can use the #Injectable decorator on a class and then the metadata is created for the DI like so:
import {Hero} from './hero.model';
import {Injectable} from 'angular2/angular2'
#Injectable()
export class HeroService {
constructor() {
console.log('Hero Service Created');
}
}
Then in your component, you can use it in your constructor (with the proper import) like this:
constructor(heroService: HeroService) {
console.log('App Component Created');
}
However, I get the following error: Cannot resolve all parameters for AppComponent(?). Make sure they all have valid type or annotations.
I am able to get it working properly if I remove the #Injectable syntax from the service and instead have my constructor like this:
constructor(#Inject(HeroService) heroService: HeroService) {
console.log('App Component Created');
}
With everything that I've read, these should do the same thing, but they aren't. Any ideas as to why? I am using Typescript 1.6.2 with VS 2013, angular 2 v2.0.0-alpha.46, and systemjs v0.19.5.
Make sure that you've specified "emitDecoratorMetadata" option in your TypeScript configuration.