Factory Design Pattern with Dependency Injection in Angular - javascript

I'm trying to use the factory design pattern in Angular, but I think I'm doing something wrong, or at least wondering if there's a better way. I have a factory which returns a car class depending on the type the user specifies (e.g., "mazda" returns a Mazda class, and "ford" returns a Ford class). Each car class uses multiples services.
constructor(
private userService: UserService,
private logService: LogService
) {}
create(
info
):
| Mazda
| Tesla
| Ford
switch (info.type) {
case 'mazda':
return new Mazda(info, this.userService, this.logService);
case 'tesla':
return new Tesla(info, this.userService, this.logService);
case 'ford':
return new Ford(info, this.userService, this.logService);
}
}
}
My issue is when I'm creating the factory in a component, I need to inject the dependencies.
this.carFactory = new CarFactory(this.userService, this.logService);
It seems weird that my component would need to know about which dependencies my factory needs. Is there a way to create a factory in Angular where you don't need to inject the dependencies into the factory? Something like
this.carFactory = new CarFactory();

you can solve this as following.
Declare above class as provider (Mazda,Tesla,Ford)
In factory class inject 1 dependancy "Injector".
In create function return as following
return this.injector.get(Tesla)
by this way you can resolve your dependancy dynamically.

You could define the CarFactory as a dependency:
#Injectable(providedIn: 'root') // or provided wherever you need it
export class CarFactory {...}
and then just inject it, where you want to use it:
constructor(private carFactory: CarFactory) {
const car = carFactory.create({type: 'tesla'});
}
Since the CarFactory is a dependency in the dependency injection hierarchy, it can inject, whatever is provided in this part of the Angular app.

Related

Why does constructor of HttpClient in Angular not need param, when I instanstiate it via constructor of other class, but needs via new?

I need to make a static method for instanstiating an object of class, but faced an issue.
import { HttpClient } from '#angular/common/http';
export MyClass {
// Case 1
public static init(): MyClass {
return this(new HttpClient(***), ....); // Need to pass new HttpClient() instead ***
}
// Case 2
public constructor(private http: HttpClient, ....) {} // Need to pass nothing
}
Why I have to pass argument in case 1, but in case 2 not? And how to solve my problem?
Answer
This happens thanks to dependency injection. Angular says:
Dependency injection, or DI, is a design pattern in which a class
requests dependencies from external sources rather than creating them
In your case you don't need to "create" that resource but simply use it, and for that dependency injection comes to your aid
I recommend you take a look at the official documentation of Angular, always very clear and quite useful!
And how to solve my problem?
If you just need to use HttpClient, do as in // Case 2 and without the need to initialize anything; otherwise, if you can please explain better what your goal is and how you can't achieve it so we can help you, thanks!
Let me know if you have any doubts ;)

Angular CDK Layout - How to include BreakPointObserver globally across the project

I am setting up a new project with latest Angular.I am using Angular Material for this.I am using BreakpointObserver from #angular/cdk/layout.
I am able to add that succesfully to one of my component.But I want to add it globally to my project so that all the components/modules can use web/tablet/mobile breakpoints for different DOM manipulation.
I am able to add that to the app.component.ts , but I am expecting to write a directive or something.Not service because BreakpointObserver is already a service.
What would be the best approach to add BreakPointObserver observables globally in the project.Do not want to add isHandset$ observables everytime in each component's ts file
I think your idea of using some kind of custom directive would be a good solution. For example, in the case you want to add/remove components of the DOM, you could define only one structural directive that handle adding/removing its host, depending on the viewport dimension using the BreakpointObserver service.
To accomplish that you could define something like this:
#Directive({
selector: '[contentForSmallScreen]'
})
export class ContentForSmallScreenDirective implements OnDestroy {
constructor(
private templateReference: TemplateRef<any>,
private viewContainerRef: ViewContainerRef,
private breakpointObserver: BreakpointObserver
) {
this.breakpointObserver
.observe([tablet,mobile])
.pipe(pluck('matches'), distinctUntilChanged())
.subscribe(this.showHideHost);
}
private showHideHost = (matches: boolean) => {
matches
? this.viewContainerRef.createEmbeddedView(this.templateReference)
: this.viewContainerRef.clear();
}
ngOnDestroy(): void {
this.breakpointObserver.ngOnDestroy();
}
}
And then after declare it at your NgModule, it can be used with any component/html element you wanna add/remove from the DOM depending on if the viewport's dimension meet the tablet and mobile viewport specs.
<app-any-component *contentForSmallScreen></app-any-component>
👨‍💻 StackBlitz example

NativeScript: Difference between a class and a service?

I'm trying to get into Nativescript + Angular2, and I read the following in the tutorial:
We’ll build this functionality as an Angular service, which is Angular’s mechanism for reusable classes that operate on data.
What they then do is to create a simple class, like this:
import { Injectable } from "#angular/core";
import { User } from "./user";
#Injectable()
export class UserService {
register(user: User) {
alert("About to register: " + user.email);
}
}
Now, I can't really see the difference between a normal class and a service - this is a very normal class definition.
So, why is it called an "Angular service"?
This creates a basic Angular service with a single method that takes an instance of the User object you created in the previous section.
Also, when using this "service" in the tutorial, it isn't clear to me when this class is instantiated - when is the construction executed? Is the object saved in memory for later use? The only call to the "userservice" in the tutorial is like this:
import { Page } from "ui/page";
import { Component, ElementRef, OnInit, ViewChild } from "#angular/core";
import { User } from "../../shared/user/user";
import { UserService } from "../../shared/user/user.service";
import { Router } from "#angular/router";
import { Color } from "color";
import { View } from "ui/core/view";
#Component({
selector: "my-app",
providers: [UserService],
templateUrl: "./pages/login/login.html",
styleUrls: ["./pages/login/login-common.css", "./pages/login/login.css"]
})
export class LoginComponent implements OnInit {
user: User;
isLoggingIn = true;
#ViewChild("container") container: ElementRef;
constructor(private router: Router, private userService: UserService, private page: Page) {
this.user = new User();
this.user.email = "bla#bla.com";
this.user.password = "1234";
}
//.... methods and stuff...
}
A class, in that context, is a regular class as in any other OO language: a "prototype" of objects which you can create instances simply using:
let myInstance = new MyClass(<arguments...>);
So, actually, an Angular service is also a class.
But consider services a special kind of class. The main difference between regular classes and service classes is their lifecycle, specially who creates their instance (who calls new).
Instances of a service are created - and managed (disposed) - by the Angular "container" (angular injector, actually).
You can also "inject" instances of service classes in constructors of other service classes (or other managed components).
A good resource in the capabilites of services is Angular's Dependency Injection Guide.
When is the construction executed?
The injector executes the construction. When? See below.
Is the object saved in memory for later use?
It could be. Depends on where you registered the service.
Before anything, know that Angular DI is a hierarchical injection system.
If you register the service with an Angular Module, the service will be created by the application's root injector. So everyone below it (aka everyone in that module) will receive the same instance of the service. In other words, Angular (will call the injector only once and) will create only one instance of the service class and pass that same instance to whoever asks for that service. And that instance will live as long as that module lives.
Now, if you register the service with a component, then the service will be registered with that component's injector. So when such component requests an instance of the service, angular will call the injector and create an instance. If any child of that component asks for an instance of such service, angular will provide the same instance. No one else, only children of the component, will receive that same instance. When that component dies, the service instance dies as well.
How does a "regular class" differ? It lacks the Injector?
The difference is not only the lack of an injector.
Angular aside, just JavaScript: you create an instance of a "regular class" by calling let instance = new MyRegularClass() somewhere in your code, right?
This instance has no "magical effects", it does nothing more than any class would (just regular JavaScript methods and properties). Example: if you need instances of other classes as arguments in the constructor, no one will "magically" create you those instances and pass them. You will have to create them manually, when calling new (e.g. new MyClass(new SomeOtherClassIDependOn(), ...)). If you want to instantiate SomeOtherClassIDependOn only once and reuse the same instance everywhere it is needed, you will have to save that instance and pass it wherever it is neeed yourself.
As services, though, angular can take some of that burden off your shoulders.
Now, before anything: since every service, deep down, is a class, someone has to call new MyServiceClass(). The difference is that someone is not you anymore. There is no new UserService() in your code. So, who is it? This someone is the Injector.
When Angular notices someone asks for a service, it calls for the injector to instantiate that service. The injector then calls let serviceInstance = new MyServiceClass(<dependencies>) and adds some "magic" to it (e.g. it can pass - inject - instances of other services to the constructor of a service), and make it available (save it) for anyone that requests that service in the scope you registered it.
Note: You can call new UserService(...) yourself, as it UserService is a class. But this instance is a regular object, not managed by angular, there is no magic (no constructor arguments will be injected, no instance is saved and reused).

angular.injector not behaving as imagined

I am trying to use angular.injector as a service locator to retrieve a service in the base class from which many controllers are derived. I'm doing this because I don't want to have to modify the constructor signature of dozens of controllers in order to get hold of an instance of my service. I am more than happy to sacrifice having a rogue service like this in order to avoid lots of risky work on my controllers.
My base controller looks like this:
module Controllers {
"use strict";
export class MyBaseController {
static $inject = ["$q"];
protected qService: angular.IQService;
private myService: Services.MyService;
constructor($q: angular.IQService) {
this.qService = $q;
// falls over on this next line
this.myService = angular.injector(["myApp"]).get("MyService");
}
protected myBehaviour(details: any) {
this.myService.myServiceMethod(details);
}
}
}
My service looks like this:
module Services {
"use strict";
export class MyService {
static $inject = ["$modal"];
private modal;
private modalInstance;
constructor($modal) {
this.modal = $modal;
}
public myServiceMethod() {
// do something with $modal
}
}
}
The error on the line in my controller looks like this:
Error: [$injector:unpr] Unknown provider: $rootElementProvider <- $rootElement <- $animate <- $compile <- $modalStack <- $modal <- ApplicationErrorService
Am I misunderstanding the function of injector? Is the problem that I can't create an instance of the service because the service itself has dependencies that cannot be resolved?
Much appreciation of any help on this one.
Is the problem that I can't create an instance of the service because the service itself has dependencies that cannot be resolved
Yes. Specifically $rootElement is not available. This is a tell tale sign that the code is executing before angular has been bootstrapped : https://docs.angularjs.org/api/ng/function/angular.bootstrap

AngularJS/Typescript Integration Pattern - Scope Methods

I am attempting change the way I am writing AngularJS apps from a plain-javascript to using TypeScript as a pre-processor.
I am struggling to reconcile the two approaches when it comes to scoped method calls.
For illustrative purposes, let's consider the common menu use-case; I wish to highlight a specific menu item which is currently displayed. The HTML template looks like this:
<ul class="nav navbar-nav">
...
<li ng-class="{active: isSelected('/page1')}">Page 1</li>
...
</ul>
This anticipates a scoped function called isSelected. With old-school javascript coding, I' write this as:
$scope.isSelected = function(path) {
return $location.path().substr(0, path.length) == path;
}
This anonymous function declaration doesn't really seem to honor the more traditional class model of TypeScript. In typescript, I find myself tempted to write this:
export interface MenuScope extends ng.IScope {
isSelected(path: String): boolean;
}
export class MenuController {
location: ng.ILocationService;
scope: MenuScope;
constructor($scope: MenuScope, $location: ng.ILocationService) {
this.scope = $scope;
this.location = $location;
this.scope.isSelected = function(path) { return this.isSelected(path) }.bind(this);
}
isSelected(path: String): boolean {
return this.location.path().substr(0, path.length) == path;
}
}
In this case, isSelected belongs to the controller, rather than the scope. This seems sensible. However, the "link" between the scope and controller still relies on an anonymous method.
Even worse, I've had to explicitly bind the context of this to ensure I can write this.location to access the location service in the implementation of isSelected().
One of the benefits I am looking for from TypeScript is a clearer way of writing code. This indirection through a binded anonymous function seems to be the antithesis of this.
You shouldn't store your $scope as a variable in this but instead use this as the $scope, the way to do this is to do $scope.vm = this; in the beginning of the constructor, now every function and variable of the class will be part of the $scope.
You can't avoid this.location = $location because this is the syntax of TypeScript.
BTW you should use $inject for the dependencies.
Here is a simple app with a controller and a service. I use this style in my projects:
/// <reference path="typings/angularjs/angular.d.ts" />
module App {
var app = angular.module("app", []);
app.controller("MainController as vm", Controllers.MainController);
app.service("backend", Services.Backend);
}
module App.Controllers {
export class MainController {
public persons: Models.Person[];
static $inject = ["$location", "backend"];
constructor(private $location: ng.ILocationService, private backend: Services.Backend) {
this.getAllPersons();
}
public isSelected(path: string): boolean {
return this.$location.path().substr(0, path.length) == path;
}
public getAllPersons() {
this.backend.getAllPersons()
.then((persons) => {
this.persons = persons;
})
.catch((reason) => console.log(reason));
}
}
}
module App.Services {
export class Backend {
static $inject = ["$http"];
constructor(private $http: ng.IHttpService) { }
public getAllPersons(): ng.IPromise<Models.Person[]> {
return this.$http.get("api/person")
.then((response) => response.data);
}
}
}
module App.Models {
export interface Person {
id: number;
firstName: string;
lastName: string;
}
}
I have modules of app, controllers, services and models.
controller defined as a class but must be registered to app through controller as syntax. So everything you define in class is accessible through vm in the view (controller scope). Here we have persons, isSelected and getAllPersons.
You can inject every injectable through static $inject that is a string[], then add them as constructor parameters respectively. This role is also usable when defining services and it is minifiable.
You can also inject $scope to your controller class to access scope specific tools such as $apply, on etc.
Instead of defining factories you can define services to be able to define them as a class.
Injecting in services is the same as injecting in controllers.
You can define return type of you http calls as ng.IPromise<Model> then return response.data to ensure that type of your return method is just entities, not http related data.
We were considering a similar conversion (e.g. from Javascript to Typescript for Angular). There were certain things (like your example) that looked very odd as we started to implement. The quickest way to go from what you have is to use the controller as syntax. This way, you expose methods directly on the controller.
<!-- use controller as syntax -->
<div ng-controller="MenuController as menu">
<ul class="nav navbar-nav">
...
<li ng-class="{active: menu.isSelected('/page1')}">Page 1</li>
...
</ul>
</div>
This would allow you to get past the need to bind the scope's method back to that on the controller. Things I don't like about this approach:
Each injectable (e.g. $scope, $location) is now available directly through the controller. This might not be a big deal, but seems undesirable when you want to know exactly what the controller can do and keeping things properly scoped.
The generated code of classes seems overly cluttered and not optimized for minification... This is more of a pet peeve of mine where you trade the ease of using something familiar like class for code that still has room for optimization. See generated code for Inheritance at the Typescript Playground (extends function generated for each .js file where you want to extend a class unless you have your references are on point, the prototype of the function could be cached to add methods to it instead of ClassName.prototype.method for each and every method...), but I digress
The other option is to not use classes, but to stick to strongly typed functions:
export function MenuController($scope: MenuScope, $location: ng.ILocationService): any {
$scope.isSelected = function(path:string): boolean {
return $location.path().substr(0, path.length) == path;
}
}
Since angular is responsible for instantiating the controller, the return type any doesn't matter. But you could get caught if you had a typo on your $scope's method.
To get around this, you can go a step further by using controller as syntax. The example below won't let you actually new up your controller yourself (e.g. new MenuController($location) would fail with only void function can be called with new keyword), but this is negligible since angular handles the instantiation for you.
export interface IMenuController {
isSelected(path: string): boolean;
}
export function MenuController($location: ng.ILocationService): IMenuController {
var self:IMenuController = this;
self.isSelected = function(path:string): boolean {
return $location.path().substr(0, path.length) == path;
}
// explicitly return self / this to compile
return self;
}
TL DR: I'm a fan of the compile time checking of types, and would love to use the concept of class. However, I don't think it completely fits with the Angular 1.x model. It seems like this is designed to work for Angular2. Use strongly typed functions instead for Angular 1.x.

Categories

Resources