I am struggling to find a way to inject a service into an class object in angular2.
* NOTE: This is not a component, just a class. *
export class Product {
id: number;
name: string;
manufacturer: string;
constructor(product: any) {
this.id = product.id;
this.name = product.name;
this.manufacturer = product.manufacturer;
}
The only solution I have come up with is to pass the service reference to the constructor whenever I create a new product... ie: instead of new Product(product) I would do new Product(product, productService) . This seems tedious and error prone. I would rather import the reference from the class and not messy up the constructor.
I have tried the ReflectiveInjector:
let injector = ReflectiveInjector.resolveAndCreate([ProductService]);
this.productService = injector.get(ProductService);
However, this creates an error No provider for Http! (ProductService -> Http) at NoProviderError.BaseError [as constructor] (Also I'm pretty sure this creates a new productService when I simple want to reference my singleton that is instantiated at the app level).
If anyone knows of a working solution I would be glad to hear it. For now i will pass the reference through the constructor.
Thanks
I was struggling with a similar issue, and what I ended up doing, was making the service a singleton as well as an Angular injectable.
This way you can inject via DI into Angular classes and call the static getInstance() method to get the singleton instance of the class.
Something like this:
import {Injectable} from "#angular/core";
#Injectable()
export class MyService {
static instance: MyService;
static getInstance() {
if (MyService.instance) {
return MyService.instance;
}
MyService.instance = new MyService();
return MyService.instance;
}
constructor() {
if (!MyService.instance) {
MyService.instance = this;
}
return MyService.instance;
}
}
There is no way to inject a service into a plain class. Angular DI only injects into components, directives, services, and pipes - only classes where DI creates the instance, because this is when injection happens.
To get Http from a custom injector, you need to add to it's providers like shown in Inject Http manually in angular 2
or you pass a parent injector that provides them
// constructor of a class instantiated by Angulars DI
constructor(parentInjector:Injector){
let injector = ReflectiveInjector.resolveAndCreate([ProductService]);
this.productService = injector.get(ProductService, parentInjector);
}
See also https://angular.io/docs/ts/latest/api/core/index/ReflectiveInjector-class.html
I'm using inversify-express-utils using the shortcut decorators (#GET, #POST...) within a node application.
Is it possible to inject middleware into the controller to use with these decorators?
Example of what I'm trying to achieve (doesn't work):
export class TestController implements Controller {
constructor(#inject(TYPES.SomeMiddleware) private someMiddleware: ISomeMiddleware) {}
#Get('/', this.someMiddleware.someMiddlewhereMethod())
public test() {
...
}
}
Like #OweR ReLoaDeD said, currently you can't do that with middleware injected through the controller constructor, due to the way decorators work in TypeScript.
However, you can achieve the same effect by wrapping the controller definition in a function that accepts a kernel, like so:
controller.ts
export function controllerFactory (kernel: Kernel) {
#injectable()
#Controller('/')
class TestController {
constructor() {}
#Get('/', kernel.get<express.RequestHandler>('Middleware'))
testGet(req: any, res: any) {
res.send('hello');
}
}
return TestController;
}
main.ts
let kernel = new Kernel();
let middleware: express.RequestHandler = function(req: any, res: any, next: any) {
console.log('in middleware');
next();
};
kernel.bind<express.RequestHandler>('Middleware').toConstantValue(middleware);
let controller = controllerFactory(kernel);
kernel.bind<interfaces.Controller>(TYPE.Controller).to(controller).whenTargetNamed('TestController');
let server = new InversifyExpressServer(kernel);
// ...
UPDATE
I added an example to the inversify-express-examples repo that showcases this approach using both custom and third-party middleware.
You should be able to use middleware please refer to the following unit tests as an example.
Update
I don't think that is possible because decorators are executed when the class is declared. The constructor injection takes place when the class instance is created (which is after it has been declared). This means that, when the decorator is executed, this.someMiddleware is null.
I'm afraid you won't be able to inject the middleware into the same class that uses it but you can do the following:
import { someMiddlewareMethod} from "middleware";
class TestController implements Controller {
#Get('/', someMiddlewareMethod())
public test() {
// ...
}
}
This is not a limitation of InversifyJS this is a limitation caused by the way decorators work.
I am new to Angular 2 and TypeScript and I'm trying to follow best practices.
Instead of using a simple JavaScript model ({ }), I'm attempting to create a TypeScript class.
However, Angular 2 doesn't seem to like it.
My code is:
import { Component, Input } from "#angular/core";
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>"
})
export class testWidget {
constructor(private model: Model) {}
}
class Model {
param1: string;
}
and I'm using it as:
import { testWidget} from "lib/testWidget";
#Component({
selector: "myComponent",
template: "<testWidget></testWidget>",
directives: [testWidget]
})
I'm getting an error from Angular:
EXCEPTION: Can't resolve all parameters for testWidget: (?).
So I thought, Model isn't defined yet... I'll move it to the top!
Except now I get the exception:
ORIGINAL EXCEPTION: No provider for Model!
How do I accomplish this??
Edit: Thanks to all for the answer. It led me to the right path.
In order to inject this into the constructor, I need to add it to the providers on the component.
This appears to work:
import { Component, Input } from "#angular/core";
class Model {
param1: string;
}
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>",
providers: [Model]
})
export class testWidget {
constructor(private model: Model) {}
}
I'd try this:
Split your Model into a separate file called model.ts:
export class Model {
param1: string;
}
Import it into your component. This will give you the added benefit of being able to use it in other components:
Import { Model } from './model';
Initialize in the component:
export class testWidget {
public model: Model;
constructor(){
this.model = new Model();
this.model.param1 = "your string value here";
}
}
Access it appropriately in the html:
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>"
})
I want to add to the answer a comment made by #PatMigliaccio because it's important to adapt to the latest tools and technologies:
If you are using angular-cli you can call ng g class model and it will generate it for you. model being replaced with whatever naming you desire.
The problem lies that you haven't added Model to either the bootstrap (which will make it a singleton), or to the providers array of your component definition:
#Component({
selector: "testWidget",
template: "<div>This is a test and {{param1}} is my param.</div>",
providers : [
Model
]
})
export class testWidget {
constructor(private model: Model) {}
}
And yes, you should define Model above the Component. But better would be to put it in his own file.
But if you want it to be just a class from which you can create multiple instances, you better just use new.
#Component({
selector: "testWidget",
template: "<div>This is a test and {{param1}} is my param.</div>"
})
export class testWidget {
private model: Model = new Model();
constructor() {}
}
export class Car {
id: number;
make: string;
model: string;
color: string;
year: Date;
constructor(car) {
{
this.id = car.id;
this.make = car.make || '';
this.model = car.model || '';
this.color = car.color || '';
this.year = new Date(car.year).getYear();
}
}
}
The || can become super useful for very complex data objects to default data that doesn't exist.
.
.
In your component.ts or service.ts file you can deserialize response data into the model:
// Import the car model
import { Car } from './car.model.ts';
// If single object
car = new Car(someObject);
// If array of cars
cars = someDataToDeserialize.map(c => new Car(c));
In your case you are having model on same page, but you have it declared after your Component class, so that's you need to use forwardRef to refer to Class. Don't prefer to do this, always have model object in separate file.
export class testWidget {
constructor(#Inject(forwardRef(() => Model)) private service: Model) {}
}
Additionally you have to change you view interpolation to refer to correct object
{{model?.param1}}
Better thing you should do is, you can have your Model Class define in different file & then import it as an when you require it by doing. Also have export before you class name, so that you can import it.
import { Model } from './model';
my code is
import { Component } from '#angular/core';
class model {
username : string;
password : string;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
username : string;
password : string;
usermodel = new model();
login(){
if(this.usermodel.username == "admin"){
alert("hi");
}else{
alert("bye");
this.usermodel.username = "";
}
}
}
and the html goes like this :
<div class="login">
Usernmae : <input type="text" [(ngModel)]="usermodel.username"/>
Password : <input type="text" [(ngModel)]="usermodel.password"/>
<input type="button" value="Click Me" (click)="login()" />
</div>
You can use the angular-cli as the comments in #brendon's answer suggest.
You might also want to try:
ng g class modelsDirectoy/modelName --type=model
/* will create
src/app/modelsDirectoy
├── modelName.model.ts
├── ...
...
*/
Bear in mind:
ng g class !== ng g c
However, you can use ng g cl as shortcut depending on your angular-cli version.
I realize this is a somewhat older question, but I just wanted to point out that you've add the model variable to your test widget class incorrectly. If you need a Model variable, you shouldn't be trying to pass it in through the component constructor. You are only intended to pass services or other types of injectables that way. If you are instantiating your test widget inside of another component and need to pass a model object as, I would recommend using the angular core OnInit and Input/Output design patterns.
As an example, your code should really look something like this:
import { Component, Input, OnInit } from "#angular/core";
import { YourModelLoadingService } from "../yourModuleRootFolderPath/index"
class Model {
param1: string;
}
#Component({
selector: "testWidget",
template: "<div>This is a test and {{model.param1}} is my param.</div>",
providers: [ YourModelLoadingService ]
})
export class testWidget implements OnInit {
#Input() model: Model; //Use this if you want the parent component instantiating this
//one to be able to directly set the model's value
private _model: Model; //Use this if you only want the model to be private within
//the component along with a service to load the model's value
constructor(
private _yourModelLoadingService: YourModelLoadingService //This service should
//usually be provided at the module level, not the component level
) {}
ngOnInit() {
this.load();
}
private load() {
//add some code to make your component read only,
//possibly add a busy spinner on top of your view
//This is to avoid bugs as well as communicate to the user what's
//actually going on
//If using the Input model so the parent scope can set the contents of model,
//add code an event call back for when model gets set via the parent
//On event: now that loading is done, disable read only mode and your spinner
//if you added one
//If using the service to set the contents of model, add code that calls your
//service's functions that return the value of model
//After setting the value of model, disable read only mode and your spinner
//if you added one. Depending on if you leverage Observables, or other methods
//this may also be done in a callback
}
}
A class which is essentially just a struct/model should not be injected, because it means you can only have a single shared instanced of that class within the scope it was provided. In this case, that means a single instance of Model is created by the dependency injector every time testWidget is instantiated. If it were provided at the module level, you would only have a single instance shared among all components and services within that module.
Instead, you should be following standard Object Oriented practices and creating a private model variable as part of the class, and if you need to pass information into that model when you instantiate the instance, that should be handled by a service (injectable) provided by the parent module. This is how both dependency injection and communication is intended to be performed in angular.
Also, as some of the other mentioned, you should be declaring your model classes in a separate file and importing the class.
I would strongly recommend going back to the angular documentation reference and reviewing the basics pages on the various annotations and class types:
https://angular.io/guide/architecture
You should pay particular attention to the sections on Modules, Components and Services/Dependency Injection as these are essential to understanding how to use Angular on an architectural level. Angular is a very architecture heavy language because it is so high level. Separation of concerns, dependency injection factories and javascript versioning for browser comparability are mainly handled for you, but you have to use their application architecture correctly or you'll find things don't work as you expect.
create model.ts in your component directory as below
export module DataModel {
export interface DataObjectName {
propertyName: type;
}
export interface DataObjectAnother {
propertyName: type;
}
}
then in your component import above as,
import {DataModel} from './model';
export class YourComponent {
public DataObject: DataModel.DataObjectName;
}
your DataObject should have all the properties from DataObjectName.
Here's my use case:
I have an interface with 4 different implementations WorkflowA, WorkflowB, WorkflowC and WorkflowD.
export interface IWorkflow {
getQuestions();
validateQuestions();
getWorkflowSteps();
}
I have a WorkflowManager class that returns the appropriate workflow based on an input variable (Factory pattern?)
export class WorkflowManager {
workflow:IWorkflow;
getWorkflow(workflow){
switch (workflow) {
case 'workflowA':
return new workflowA();
case 'workflowB':
return new workflowB();
case 'workflowC':
return new workflowC();
case 'workflowD':
return new workflowD();
default:
throw new Error(`Unrecognized workflow: ${workflow}`);
}
}
}
I also have a component WorkflowComponent that gets loaded
Example:
/workflow=WorkflowA loads WorkflowComponent. The component then extracts the routeparam workflow and based on the value, it then passes it to the WorkflowManager which returns the appropriate workflow.
Here's a snippet of my component
#Component({
...
})
export class Workflow implements OnInit {
workflowInstance: IWorkflow;
constructor(
private route: ActivatedRoute,
private workflowManager: WorkflowManager,
) {
}
ngOnInit() {
this.route.params.subscribe(params => {
let regType: string = params['workflow'];
this.workflowInstance = this.workflowManager.getWorkflow(workflow);
});
}
}
But I don't like the idea of manually instantiating Workflow* classes. How can I improve this so I wire it using Angular2 supplied API so that it can be managed/injected in to other components?
Also in my current implementation, I will have to make ajax calls outside of Angular framework which I don't think is the best thing to do.
Any ideas on how to take advantage of the Angular framework and improve this?
One way is to inject an injector and acquire the workflow instance imperatively like:
#Injectable()
export class WorkflowManager {
constructor(private injector:Injector) {}
workflow:IWorkflow;
getWorkflow(workflow){
switch (workflow) {
case 'workflowA':
return this.injector.get(workflowA);
case 'workflowB':
return this.injector.get(workflowB);
case 'workflowC':
return this.injector.get(workflowC);
case 'workflowD':
return this.injector.get(workflowD);
default:
throw new Error(`Unrecognized workflow: ${workflow}`);
}
}
}
Alternatively you can inject workflowA, workflowB, workflowC, workflowA and just return the right one depending on the workflow but I think using the injector is a better fit for this use case.
Ensure you provide workflowA, workflowB, workflowC.
In the StackExchange.Redis docs it is recommended to only create one and reuse the connection to Redis.
Azure Redis best practices recommends using the following pattern:
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
return ConnectionMultiplexer.Connect("cachename.redis.cache.windows.net,ssl=true,abortConnect=false,password=password");
});
public static ConnectionMultiplexer Connection
{
get
{
return lazyConnection.Value;
}
}
but how should I get this working with Autofac where I want the configuration to be set in the web/app config files?
I currently have a RedisCacheProvider:
private readonly ConnectionMultiplexer _connection;
public RedisCacheProvider(string config)
{
_connection = ConnectionMultiplexer.Connect(config);
}
and in my Autofac config:
builder.RegisterType<RedisCacheProvider>().As<ICacheProvider>().WithParameter("config", "localhost");
My thinking is, I should change my RedisCacheProvider to take in a ConnectionMultiplexer which is passed in via the static variable?
Update: My Solution so far:
My RedisCacheProvider (injecting an interface here allows me to mock the connection in unit tests):
private readonly IConnectionMultiplexer _connection;
public RedisCacheProvider(IConnectionMultiplexer connection)
{
_connection = connection;
}
RedisConnection class to hold the static property and read config from config file:
public class RedisConnection
{
private static readonly Lazy<ConnectionMultiplexer> LazyConnection =
new Lazy<ConnectionMultiplexer>(
() => ConnectionMultiplexer.Connect(ConfigurationManager.AppSettings["RedisCache"]));
public static ConnectionMultiplexer Connection
{
get
{
return LazyConnection.Value;
}
}
}
Registration in an Autofac Module:
builder.RegisterType<RedisCacheProvider>().As<ICacheProvider>()
.WithParameter(new TypedParameter(
typeof(IConnectionMultiplexer),
RedisConnection.Connection));
Autofac supports Implicit Relationship Types and Lazy<> evaluation is supported out of the box.
So after you register your RedisCacheProvider as in your example, that is
builder
.RegisterType<RedisCacheProvider>()
.As<ICacheProvider>()
.WithParameter("config", "localhost");
you can resolve it like below:
container.Resolve<Lazy<ICacheProvider>>();
But do not forget that default Autofac lifetime scope is InstancePerDependency(transient). That is, you will get new instance of RedisCacheProvider everytime you resolve it or whenever it is provided to other component as dependency. To fix this you need to specify its lifetime scope explicitly. For instance, to make it singleton you need to change registration as below:
builder
.RegisterType<RedisCacheProvider>()
.As<ICacheProvider>()
.WithParameter("config", "localhost")
.SingleInstance();
Another assumption here is that RedisCacheProvider is the only component where Redis connection is used. If it is not the case then you should better let Autofac manage Redis connection's life scope (which is a better idea anyway) and get the connection as a dependency in RedisCacheProvider. That is:
public RedisCacheProvider(IConnectionMultiplexer connection)
{
this.connection = connection;
}
....
builder
.Register(cx => ConnectionMultiplexer.Connect("localhost"))
.As<IConnectionMultiplexer>()
.SingleInstance();
builder
.RegisterType<RedisCacheProvider>()
.As<ICacheProvider>();