I have a simple service
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class CompToCompService {
private data = new Subject<any>();
data$ = this.data.asObservable();
constructor() { }
Send(value: any) {
this.data.next(value);
}
}
I use it to sling some light data back and forth between components.
I have however found myself needed to use it twice in the same component to send data and use different components to listen. As angular services are now by default singletons everything is being jumbled together.
Is it possible to create different instance of the server and if so how do I get the listening components to listen to the right instance?
If not what's the best way to accomplish what I am trying to do. I am trying to avoid creating a tiny service like that for every piece of communication I need to do. I know of events but my communications are across the routing-outlet and events don't seem to like that.
Create a parent component with two or more child components.
in each child component use
providers: [serviceName]
Then you will have tow instance of service .
but you must work with each instance in their component
Related
I have many endpoints in my app:
/Route1
/Route2
...
/Route99
In a number of these routes, there is some common functionality such as getting specific data from one source such as a local file, or another resource such as a No SQL database or external HTTP endpoint. My problem is that these services need to have a service dependency themselves, and I am not sure that how I have currently done it is the best way to do it in NestJS.
Route1Service - Read a file of data, and return it. This uses the FileSystemService() to wrap all the error handling, different data types, path checking etc., of the NodeJS fs module. The Route1Service then returns this to the Route1Controller
#Injectable()
export class Route1Service {
private FS_:FileSystemService; // defined here instead of constructor, as I do not know how to set it in the constructor via NestJS, or if this is even the best way.
// constructor(private FS_: FileSystemService) { }
// Since I do not set it in the constructor
public DataServiceDI(FsService:FileSystemService):void {
this.FS_ = FsService;
}
public GetData(): string {
const Data:string = this.FS_.ReadLocalFile('a.txt');
return Data;
}
}
Route99Service might do the same thing, but with a different file (b.txt)
#Injectable()
export class Route99Service {
private FS_:FileSystemService;
public DataServiceDI(FsService:FileSystemService):void {
this.FS_ = FsService;
}
public GetData(): string {
const Data:string = this.FS_.ReadLocalFile('b.txt');
return Data;
}
}
This is a contrived example to illustrate my issue. Obviously a basic RouteService could be used, and pass the file name, but I am trying to illustrate the dependent service. I do not know how to define the module(s) to use this dependent service or if I should be doing it this way.
What I have been doing for my definition:
#Module({
controllers: [Route1Controller],
providers: [Route1Service, FileSystemService],
})
export class Route1Module {}
The controller than has the constructor with both Services:
#Controller('route1')
export class Route1Controller
constructor(
private Route1_: Route1Service,
private FsSystem_: FileSystemService
) { }
Now that my controller has the FsSystem service as a separate entity, I need to add a method on my Route1Service, DataServiceDI(), to allow me to pass the FileSystemService as a reference. Then my service can use this service to access the file system.
My question comes down to, is this the best practice for this sort of thing? Ultimately, in my code, these services (FileSystemService, NoSqlService) extend a common service type, so that all my services can have this DataServiceDI() in then (they extend a base service with this definition).
Is this the best approach for longer term maintainability? Is there an easier way to simply inject the proper service into my Route1Service so it is injected by NestJS, and I do not have to do the DI each time?
The current method works for me to be able to simply test the service, since I can easily mock the FileSystemServie, NoSqlService, etc., and then inject the mock.
I'm trying to create a central notification service for the app, to report simple errors via the same "pipeline". It may be the wrong approach, but I need a material-popup stuck to the main HTML body, displaying on-demand as required by various components.
import 'package:angular/angular.dart';
import 'package:angular_components/angular_components.dart';
import 'package:angular_components/laminate/overlay/zindexer.dart';
#Component(
selector: 'hv-alerts',
templateUrl: 'alert_service.html',
directives: [
MaterialPopupComponent,
PopupSourceDirective
],
providers: [
ClassProvider(ZIndexer),
materialProviders,
popupBindings
]
)
class AlertService {
RelativePosition get popupPosition => RelativePosition.AdjacentTop;
bool popupVisible = false;
void setVisible(bool flag) {
popupVisible = flag;
}
PopupSizeProvider popupSize = FixedPopupSizeProvider(
minWidth: 400,
minHeight: 75,
maxWidth: 600,
maxHeight: 75
);
static final AlertService _instance = AlertService();
}
Is there a way to pass in the handler to this material-popup via a singleton or Factory or whatever, and allow other services to call an AlertService.show()?
This is what I would do:
Create an Alert Service (not a component, a regular class)
Inject this alert service into your AppComponent
Use this service to pass around the view that controls the popup and manipulate it accordingly
It is quite easy to pull-off.
The component's lifecycle is dependant on the element in the templates itself. Such if you had two elements with the same selector you would have two components. You can't have them by singletons themselves.
That said how I would do this is abstract the service part of it, and have it be a separate class that would be injected into the popup component and any usage. The popup would listen to a stream know when to show the popup, and the clients would send an event on a StreamController to tell the popup when to show. If you wanted to be a bit safer you could provide two different interfaces the stream, and the stream controller which were backed by the same entity. This would allow you more quickly see who was consuming alerts, and who was producing them.
In my controller, via service, I get from DB a list of the names of widgets (eg. chart, calendar, etc). Every widget implements WidgetInterface and may need other services as its own dependencies. The list of widgets can be different for each user, so I don't know which widgets / dependencies I will need in my controller. Generally, I put dependencies via DI, using factories, but in this case I don't know dependencies at the time of controller initialization.
I want to avoid using service locator directly in controller. How can I manage that issue? Should I get a list of the names of widgets in controller factory? And depending on widgets list get all dependencies and put them to controller?
Thanks, Tom
Solution
I solved my issue in a way that suggested Kwido and Sven Buis, it means, I built my own Plugin Manager.
Advantages: I do not need use service locator directly in controller and I have clear and extensible way to get different kinds of widgets.
Thank you.
Create your own Manager, like some sort of ServiceManager, for your widgets.
class WidgetManager extends AbstractPluginManager
Take a look at: Samsonik tutorial - pluginManager. So this way you can inject the WidgetManager and only retrieve the widgets from this manager as your function: validatePlugin, checks whether or not the fetched instance is using the WidgetInterface. Keep in mind that you can still call the parent ServiceManager.
Or keep it simple and build a plugin for your controller that maps your widget names to the service. This plugin can then use the serviceLocator/Manager to retrieve your widget(s), whether they're created by factories or invokableFactories. So you dont inject all the widget directly but only fetch them when they're requested. Something realy simplistic:
protected $map = [
// Widget name within the plugin => Name or class to call from the serviceManager
'Charts' => Widget\Charts::class,
];
public function load($name)
{
if (array_key_exists($name, $this->map)) {
return $this->getServiceManager()->get($this->map[$name]);
}
return null;
}
Injecting all the Widgets might be bad for your performance so you might consider something else, as when the list of your widgets grow so will the time to handle your request.
Hope this helped you and pushed you in some direction.
This indeed is a interesting question. You could consider using Plugins for the widgets, which can be loaded on the fly.
Depency injection is a good practise, but sometimes, with dynamic content, impossible to implement.
Another way to do this, is to make your own widget-manager. This manager then can load the specific widgets you need. The widget-manager can be injected into the controller.
Edit:
As you can see above, same idea from #kwido.
I would use a separate service and inject that into the controller.
interface UserWidgetServiceInterface
{
public function __construct(array $widgets);
public function getWidget($name);
}
The controller factory
class MyControllerFactory
{
public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
{
$serviceLocator = $controllerManager->getServiceLocator();
$userWidgetService = $serviceLocator->get('UserWidgetService');
return new MyController($userWidgetService);
}
}
Then the logic to load the widgets would be moved to the UserWidgetServiceFactory.
public function UserWidgetServiceFactory
{
public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
{
$userId = 123; // Load from somewhere e.g session, auth service.
$widgetNames = $this->getWidgetNames($serviceLocator, $userId);
$widgets = $this->loadWidgets($serviceManager, $widgetNames);
return new UserWidgetService($widgets);
}
public function getWidgetNames(ServiceManager $sm, $userId)
{
return ['foo','bar'];
}
public function loadWidgets(serviceManager $sm, array $widgets)
{
$w = [];
foreach($widgets as $widgetName) {
$w[$widgetName] = $sm->get($widgetName);
}
return $w;
}
}
The call to loadWidgets() would eager load all the widgets; should you wish to optimise this you could register your widgets as LazyServices
How can I set initial state of Angular 2 app from view?
I have a controller that must pass initial state throw the view to angular 2 component.
Currently you can't pass data to the root level component via a property, but you can however define a global variable outside the component and refer to it in your component. This might not be ideal, but perhaps not too bad either.
jquery is an example of this. In the below example the global jquery variable is referenced from my component. You could do the same with your own global variable. You could use server side rendering to dynamically create your global variable when the page renders.
import {Component, ElementRef, Inject, OnInit} from 'angular2/core';
declare var jQuery:any;
#Component({
selector: 'jquery-integration',
templateUrl: './components/jquery-integration/jquery-integration.html'
})
export class JqueryIntegration implements OnInit {
elementRef: ElementRef;
constructor(#Inject(ElementRef) elementRef: ElementRef) {
this.elementRef = elementRef;
}
ngOnInit() {
jQuery(this.elementRef.nativeElement).find('.moving-box').draggable({containment:'#draggable-parent'});
}
}
Why don't you hydrate the initial state of the application by calling a WebApi endpoint in ngOnInit()? That way your application is relying on a standard HTTP response rather than a piece of MVC infrastructure...
I'm following the tutorials display-data in angular.io.
I introduced a new class FriendsService to separate the controller logic and model concern. I Called FriendsService class in DisplayComponent class by using dependency injection, the dependency injection not working.
There's no errors in the console. The page doesn't display the component. This is the line causing the component not to display on the page.
constructor(friendsService: FriendsService)
The page loads and displays the components (display) if change the constructor to:
constructor()
I'm using angular2.alpha.34 , Typescript, ES6.
I solved it. Eclipse-Plugin was causing the issue. The plugin wasn't generating the ES5 complaint correct code.
I used "tsc --watch -m commonjs -t es5 --emitDecoratorMetadata app.ts" command described in angular.io website.
The eclipse-plugin generated code and the command "tsc" generated code is slightly different.
When using the "tsc" command I was achieving the expected behavior.
First you have to define the Service before that any component or directive.
If you have something like this
class Component {
constructor(svc: Service) {
}
}
class Service {
}
It will fail. In cases like this you should use fordwardRef or you just can declare it before your component.
class Service {
}
class Component {
constructor(svc: Service) {
}
}
Another thing is that you have to inject your service using viewBindings (see ComponentAnnotation documentation).
class Service {
}
#Component({
viewBindings: [Service]
})
class Component {
constructor(svc: Service) {
}
}
And you are good to go. I hope it helps.