Im building an angular 2 app, and i need a way to get a reference to a service/class that is injected/instantiated by angular framework.
I have a generic class...lets call it Custom class, that i use throughout the site. This class, however is instantiated by me, not angular 2.
Now, i need to use some of the services instantiated by angular 2 inside that class.
i.e
// this is not angular component/service
export default class Custom {
constructor(properties) {
}
doSomething() {
// UserService is a service that is injected into other componetns constructors, but this class is not a component.
// Here i need a ref to the singleton 'UserService' made by angular
let userService = someAngularFunction.getInstance('UserService');
userService.doIt();
}
}
// in some component.
export class MyComponent implements OnInit {
doAnotherThing() {
let c = new Custom('some generic params');
c.doSomething();
}
}
// in some n number of other components, repeat the above.
Note, i know that i could inject the 'UserService' into
MyComponent, and from MyComponent, pass it down to new Custom() constructor. But, since MyComponent itself doesn't use that service, and
Custom class is instantiated in many other places, id like to move that dependency into Custom class.
Is there a way to do that ?
If not, whats the second best option.
As far as I know you have two choices:
1) Manually pass the service into the Custom class constructor.
let x = new Custom(this.userService);
2) Make the Custom class a service as well. Then it will participate in Angular's DI.
See this article for more information: https://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html
Related
I'm trying to call a function defined within an angular component.ts from another javascript library
The library supports a snapin, where I can define HTML Elements, which will be rendered (they are not part of angular).
Here is my html (not in a component's template)
<button onclick='ping()'>PING Button</button>
<button onclick='pong()'>PONG Button</button>
How can I, from the html above, call the pong component method defined my component.ts
import { Component, OnInit, AfterViewInit, Inject, ChangeDetectionStrategy, ChangeDetectorRef } from '#angular/core';
function ping() {
alert('PING PING');
}
#Component({ ...
})
export class Component implements OnInit, AfterViewInit { ...
pong() { //This is the method I want to call from outside of the component's template
alert('pong pong');
}
}
I tried, but it will not work
<button (click)="pong()">PONG Button</button>
But I have no idea to call the "pong()" function normally
Thanks!
If you really need this, you can make the method available in the window object
component.ts
constructor()
{
window['pong'] = () => this.pong();
}
pong()
{
alert ('pong inside component')
}
Then in your html, you can use old style event handler
<button onclick="pong()">Pong</button>
Here is a stackblitz demo
Note: If you have several instance of the same angular component implementing this solution, you'll only have once instance of the method. You could save them all to an array if needed, but you'll need to know which one to call
I guess you want to use a non-angular library from angular, which has a global callback. Be warned that this can lead to problems, because you have to manage the lifecycle of the non-angular thing from angular.
From the angular template, you can only call methods on the component class, and you can't call a global callback. You can however create a method on the component class, and call the global callback from there.
There's one more thing before that's possible: typescript doesn't know about your global callback, so you have to explictily declare it, see the example. This tells typescript that there's something that is created outside of typescript, so it will let you call it.
import { Component } from '#angular/core';
declare const libMethod: (any) => any;
#Component({
selector: 'my-app',
template: `
<button (click)="myMethod($event)"></button>
`,
styleUrls: []
})
export class AppComponent {
public myMethod(param) {
libMethod(param);
}
}
If you plan to use that library from multiple of your angular components, then you might want to create a service, declare the global callback only in that, and create a method on the service. That way, this somewhat hacky declaration will not be littered all over your code, but contained to a single place. It also makes your life easier, if you upgrade/replace this library.
Answers to questions in the comments:
TBH I don't completely understand the situation. (Who calls the backend, when it returns the HTML? You from Angular, or the lib? Does the lib process the HTML? Or what does it do?)
Some suggestions: create a global singleton service, which puts up one of its methods to the window (dont' forget to bind it if you use this inside the method) to serve as a callback for the lib. When the lib calls it with the data, regardless of who/when actually triggered the lib to do its thing, the service stores the data in a subject, and the service also provides an observable of the data (maybe with a shareReplay(1) so that the consumers always get something).
With that, actually displaying the data is fairly easy, you can just use the async pipe, and not care about how/when the data got there in the first place, and don't have to sync the component's lifecycle with the service.
Also, you probably need to use https://angular.io/api/platform-browser/DomSanitizer#bypasssecuritytrusthtml but I am not sure about that, since I never had to inject HTML. Speaking about which...
Important security notice: if you inject HTML from outside of angular, and that is hijacked, you just opened up your page to all kind of nasty cross site scripting things.
I'm using two components, SelectTag and the main component, SettingsComponent. The select component is a simple directive with a selector, but the issue I'm having is updating a parent variable from the child component.
For example: I have an ngModel binding to a variable (name) in the selector component, but how can I access this from the parent component (settings)?
What I've tried so far is using the Angular EventEmitter:
#Output() onNameChange: EventEmitter<string>;
...and accessing it through
<select-tag (onNameChange)="name = $event">
Is there a better practice to doing this?
You have a few options to share data between Child and Parent Components.
The best way is probably using an EventEmitter like you already tried, just make sure to trigger the event when you want to update.
For example:
export class ChildComponent {
#Output() nameUpdate: EventEmitter<string> = new EventEmitter<string>();
updateParent(name){
this.nameUpdate.emit(name)
}
}
child.component.html:
<input (input)="updateParent($event.target.value)">
Now the parent who's using this child selector can listen to the nameUpdate event:
<app-child (nameUpdate)="nameUpdate($event)">
You will get the new value within the $event.
You can also consider using services and DI to share data across components, or any other state management tools. But, if your components are in a sibling relationship, Use the Input and Output decorators as the best practice.
You can create a centralized centralized.service.ts component to distribute the data to your components, this way you get it centralized and easy to control the data flow when your app grow:
export class CentralizedService {
private sharedData = {};
getSharedData(){
return this.sharedData
}
updateSharedData(data){
//Statements
}
}
And inject the service to your module and get access to it in both your component
constructor(private centralizedService: CentralizedService){}
ngOnInit(){
this.componentData = this.centralizedService.getSharedData();
}
//Create a method to update shared data
updateSharedData(data) {
this.centralizedService.updateSharedData(data);
}
As title implies i'm looking for a way to bind an object with multiple properties to component #Inputs without having to explicitly write all of them
Let's say I have this object
let params = {
delta: 0.2,
theta: 2.3,
sigma: 'foo',
}
Instead of having to bind all of them individually like this
<my-component
[delta]="params.delta"
[theta]="params.theta"
[sigma]="params.sigma"/>
I would like bind all of them at once.
<my-component [someDirectiveIDontKnow]="params"/>
How can i do this?
Found a link to a previously asked question but couldn't get that to work properly.
Edit:
I'm not asking how to bind #Inputs. Imagine that the component I'm rendering has 40 #Inputs and I'm NOT allowed to rewrite it to just accept one #Input that could contain all the params.
So writing a template that uses this component gets really ugly and big.
....
<my-component
[param1]="params.param1"
[param2]="params.param2"
[param3]="params.param3"
[param4]="params.param4"
[param5]="params.param5"
[param6]="params.param6"
[param7]="params.param7"
[param8]="params.param8"
[param9]="params.param9"
[param10]="params.param10"
[param11]="params.param11"
[param12]="params.param12"
[param13]="params.param13"
[param14]="params.param14"
... and so on ....
/>
....
In my opinion, It would be best to define them all in a model
You would start with the following model
params.model.ts
import {SomeOtherModel} from './some-other.model'
export interface ParamsModel {
paramName1: string;
paramName2?: string;
paramName3?: number;
paramName4: SomeOtherModel;
}
Then in your component, you can force your input to take a specific model argument
my.component.ts
import {ParamsModel} from './params.model';
#Component({..})
class MyComponent {
#Input() params: ParamsModel;
}
app.component.html
<my-component params="paramsModel"></my-component>
app.component.ts
import {ParamsModel} from './params.model';
#Component({..})
class AppComponent implements OnInit {
paramsModel: ParamsModel;
ngOnInit(): void {
this.paramsModel = <ParamsModel>{someValue: someValue};
}
}
this way you have full code completion.
do note though! Angular does not deepwatch the contents, so changing the contents inside the Params object, will still have the same object ID in javascript, causing angular to not see the changes.
There are a few work-around for this
1: Bind every param (this is exactly what you do not want)
2: When changing contents of the model, destroy the instance and create a new instance everytime, you could do this by adding a constructor in the model and convert it olike this code
export class ParamsModel {
paramName1: string;
paramName2?: string;
paramName3?: number;
paramName4: SomeOtherModel;
constructor(config?: ParamsModel) {
Object.assign(this, config);
}
}
// first init
this.paramsModel = new ParamsModel(<ParamsModel>{someValue: someValue});
// updated init
this.paramsModel = new ParamsModel(this.paramsModel);
this.paramsModel.changedValue = changedValue; // (could also use an extend function on original params model)
3: Create an observer with events and trigger update events on the other side
4: use ngDoCheck to perform your own check if the contents changed
There is no generic directive to pass input properties in Angular. However, Angular supports binding any valid JavaScript type be it objects, arrays or primitives.
In the template
<my-component [params]="params"/>
In the class you have to use the #Input decorator to mark an object as an input. You can access it's value in any of the lifecycle hooks, some shown below. Note that params will not be set inside the constructor as view binding is performed after the class is instantiated.
class MyComponent {
#Input()
params: any
constructor() { } // <-- params not set
ngOnChanges() { } // <-- anytime params changes
ngOnInit() { } // <-- once when the component is mounted
}
My components often start out by having multiple #Input and #Output properties. As I add properties, it seems cleaner to switch to a single config object as input.
For example, here's a component with multiple inputs and outputs:
export class UsingEventEmitter implements OnInit {
#Input() prop1: number;
#Output() prop1Change = new EventEmitter<number>();
#Input() prop2: number;
#Output() prop2Change = new EventEmitter<number>();
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() => this.prop1Change.emit(this.prop1 + 1));
}
}
And its usage:
export class AppComponent {
prop1 = 1;
onProp1Changed = () => {
// prop1 has already been reassigned by using the [(prop1)]='prop1' syntax
}
prop2 = 2;
onProp2Changed = () => {
// prop2 has already been reassigned by using the [(prop2)]='prop2' syntax
}
}
Template:
<using-event-emitter
[(prop1)]='prop1'
(prop1Change)='onProp1Changed()'
[(prop2)]='prop2'
(prop2Change)='onProp2Changed()'>
</using-event-emitter>
As the number of properties grows, it seems that switching to a single configuration object might be cleaner. For example, here's a component that takes a single config object:
export class UsingConfig implements OnInit {
#Input() config;
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() => this.config.onProp1Changed(this.config.prop1 + 1));
}
}
And its usage:
export class AppComponent {
config = {
prop1: 1,
onProp1Changed(val: number) {
this.prop1 = val;
},
prop2: 2,
onProp2Changed(val: number) {
this.prop2 = val;
}
};
}
Template:
<using-config [config]='config'></using-config>
Now I can just pass the config object reference through multiple layers of nested components. The component using the config would invoke callbacks like config.onProp1Changed(...), which causes the config object to do the reassignment of the new value. So it seems we still have one-way data flow. Plus adding and removing properties doesn't require changes in intermediate layers.
Are there any downsides to having a single config object as an input to a component, instead of having multiple input and outputs? Will avoiding #Output and EventEmitter like this cause any issues that might catch up to me later?
personally if I see I need more than 4 inputs+outputs, I will check my approach to create my component again , maybe it should be more than one component and I'm doing something wrong.
Anyway even if I need that much of input&outputs I won't make it in one config, for this reasons :
1- It's harder to know what should be inside inputs and outputs,like this:
(consider a component with to html inputs element and labels)
imagine if you got only 3 of this component and you should comeback to work on this project after 1 or 2 month, or someone else gonna collaborate with you or use your code!.
it's really hard to understand your code.
2- lack of performance. it's way cheaper for angular to watch a single variable rather than watching an array or object. beside consider the example i gave you at first one, why you should force to keep track of labels in which may never change alongside with values which always are changing.
3- harder to track variables and debug. angular itself comes with confusing errors which is hard to debug, why should I make it harder. tracking and fixing any wrong input or output one by one is easier for me rather than doing it in one config variable which bunch of data.
personally I prefer to break my components to as small as possible and test each one. then make bigger components out of small ones rather than having just a big component.
Update :
I use this method for once input and no change data ( like label )
#Component({
selector: 'icon-component',
templateUrl: './icon.component.html',
styleUrls: ['./icon.component.scss'],
inputs: ['name', 'color']
});
export class IconComponent implements OnInit {
name: any;
color: any;
ngOnInit() {
}
}
Html:
<icon-component name="fa fa-trash " color="white"></icon-component>
with this method angular wont track any changes inside your component or outside.
but with #input method if your variable changes in parent component, you will get change inside component too.
I would say it could be OK to use single config objects for Inputs but you should stick to Outputs at all the time. Input defines what your component requires from outside and some of those may be optional. However, Outputs are totally component's business and should be defined within. If you rely on users to pass those functions in, you either have to check for undefined functions or you just go ahead and call the functions as if they are ALWAYS passed within config which may be cumbersome to use your component if there are too many events to define even if the user does not need them. So, always have your Outputs defined within your component and emit whatever you need to emit. If users don't bind a function those event, that's fine.
Also, I think having single config for Inputs is not the best practice. It hides the real inputs and users may have to look inside of your code or the docs to find out what they should pass in. However, if your Inputs are defined separately, users can get some intellisense with tools like Language Service
Also, I think it may break change detection strategy as well.
Let's take a look at the following example
#Component({
selector: 'my-comp',
template: `
<div *ngIf="config.a">
{{config.b + config.c}}
</div>
`
})
export class MyComponent {
#Input() config;
}
Let's use it
#Component({
selector: 'your-comp',
template: `
<my-comp [config]="config"></my-comp>
`
})
export class YourComponent {
config = {
a: 1, b: 2, c: 3
};
}
And for separate inputs
#Component({
selector: 'my-comp',
template: `
<div *ngIf="a">
{{b + c}}
</div>
`
})
export class MyComponent {
#Input() a;
#Input() b;
#Input() c;
}
And let's use this one
#Component({
selector: 'your-comp',
template: `
<my-comp
[a]="1"
[b]="2"
[c]="3">
</my-comp>
`
})
export class YourComponent {}
As I stated above, you have to look at the code of YourComponent to see what values you are being passed in. Also, you have to type config everywhere to use those Inputs. On the other hand, you can clearly see what values are being passed in on the second example better. You can even get some intellisense if you are using Language Service
Another thing is, second example would be better to scale. If you need to add more Inputs, you have to edit config all the time which may break your component. However, on the second example, it is easy to add another Input and you won't need to touch the working code.
Last but not least, you cannot really provide two-way bindings with your way. You probably know that if you have in Input called data and Output called dataChange, consumers of your component can use two-way binding sugar syntax and simple type
<your-comp [(data)]="value">
This will update value on the parent component when you emit an event using
this.dataChange.emit(someValue)
Hope this clarifies my opinions about single Input
Edit
I think there is a valid case for a single Input which also has some functions defined inside. If you are developing something like a chart component which often requires complex options/configs, it is actually better to have single Input. It is because, that input is set once and never changes and it is better to have options of your chart in a single place. Also, the user may pass some functions to help you draw legends, tooltips, x-axis labels, y-axis labels etc.
Like having an input like following would be better for this case
export interface ChartConfig {
width: number;
height: number;
legend: {
position: string,
label: (x, y) => string
};
tooltip: (x, y) => string;
}
...
#Input() config: ChartConfig;
The point of having the Input besides its obvious functionality, is to make your component declarative and easy to understand.
Putting all the configs in one massive object, which will grow definitely (trust me) is a bad idea, for all the above reasons and also for testing.
It's much easier to test a component's behaviour with a simple input property, rather than supplying a giant confusing object.
You're going backwards and thinking like the way jQuery plugins used to work, where you'd call a function called init and then you provide a whole bunch of configuration which you don't even remember if you should provide or not, and then you keep copy pasting this unknown and ever-growing object across your components where they probably don't even need them
Creating defaults is extremley easy and clear with simple Inputs whereas it becomes a little bit messy with objects to created defaults.
If you have too many similar Input, Outputs, you can consider below :
1- You can create a Base class and put all your Input/Outputs that are similar and then extend all your components from it.
export class Base{
#Input() prop1: number;
#Output() prop1Change = new EventEmitter<number>();
#Input() prop2: number;
#Output() prop2Change = new EventEmitter<number>();
}
#Component({})
export class MyComponent extends from Base{
constructor(){super()}
}
2- If you don't like this, you can use composition and create a reusable mixin and apply all your Input/Outputs like that.
Below is an example of a function that can be used to apply mixins, NOTE may not necessarily be exactly what you want, and you need to adjust it to your needs.
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
And then create your mixins :
export class MyMixin{
#Input() prop1: number;
#Output() prop1Change = new EventEmitter<number>();
#Input() prop2: number;
#Output() prop2Change = new EventEmitter<number>();
}
applyMixins(MyComponent, [MyMixin]);
3- You can have default properties for inputs so you only override them if you need:
export class MyComponent{
#Input() prop1: number = 10; // default
}
Are there any downsides to having a single config object as an input to a component, instead of having multiple input and outputs?
Yes, when you want to switch to the onpush change detection strategy, which is often needed in bigger projects to mitigate performance issues caused by too many render-cycles, angular will not detect changes that happened inside your config object.
Will avoiding #Output and EventEmitter like this cause any issues that might catch up to me later?
Yes, if you start to move away from #Output and in your template directly operate on the config object itself, then you are causing side-effects in your view, which will be the root of hard-to-find bugs in the future. Your view should never modify the data it get's injected. It should stay "pure" in that sense and only inform the controlling component via events (or other callbacks) that something happened.
Update: After having a look at the example in your post again, it looks like you did not mean that you want to directly operate on the input model but pass event emitters directly via the config object. Passing callbacks via #input (which is what you are implicitly doing) also has it's drawbacks, such as:
your component gets harder to understand and reason about (what are its inputs vs its outputs?)
cannot use banana box syntax anymore
If you want to bundle input parameters as an object, i'd suggest to do it like this:
export class UsingConfig implements OnInit {
#Input() config: any;
#Output() configChange = new EventEmitter<any>();
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() =>
this.configChange.emit({
...this.config,
prop1: this.config.prop1 + 1
});
);
}
}
You are creating a new config object when changing a property.
You are using an Output-Event to emit the changed config object.
Both points ensure that ChangeDetection will work properly (assuming you use the more efficient OnPush strategy). Plus it's easier to follow the logic in case of debugging.
Edit:
Here's the obvious part within the parent component.
Template:
<using-config [config]="config" (configChange)="onConfigChange($event)"></using-config>
Code:
export class AppComponent {
config = {prop1: 1};
onConfigChange(newConfig: any){
// if for some reason you need to handle specific changes
// you could check for those here, e.g.:
// if (this.config.prop1 !== newConfig.prop1){...
this.config = newConfig;
}
}
How do you properly pass a function from a parent to a child component when the function takes in parameters?
In the ngOnInit, how to scope a function like:
addToList(id) {
this.store.dispatch(this.listActions.addToList(id));
}
ngOnInit, which is wrong right now.
ngOnInit() {
this.addToList = this.addToList.bind(this, id);
}
In my parent component, I have the addToCart(id) function.
I want to pass that function to my child component, which has a list of items, and on clicking the ADD button on an item, I want to callback addToCart(item_id) to the parent.
#Maarek's answer is a good one, and is the 'right' way to do it, probably. What I am presenting here is a simpler means of communicating specifically from the Child to the Parent.
What you proposed in the original post was to have the Parent send a callback method to the Child, so the Child can call it with data when appropriate. To accomplish this specific task (data from Child to Parent on some action in the Child) using Events is appropriate, using the EventEmitter from inside the Child. See this API reference which has an example: https://angular.io/docs/ts/latest/api/core/index/EventEmitter-class.html and this Plunker I made as a demo: https://embed.plnkr.co/T1wFqVOhMXgX6NRfTuiC/
In the child, you have code like this:
import { Component, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'item',
template: `
<div class="item">
<button type="button" (click)="addItem()">Add</button>
<p>{{id}}
</div>
`
})
export class ItemComponent {
#Input() id: string;
//key line here: this emitter can be bound to by parent to get notifications
#Output() add: EventEmitter<string> = new EventEmitter<string>();
constructor() { }
addItem() {
//then when the button is clicked, emit events to the parent.
this.add.emit(this.id);
}
}
The Parent would call create the component like this:
<item id="1" (add)="addToList($event)"></item>
Where addToList() is a function on the Parent that does the work your callback was intended to do. The $event is the data passed from the child (the id).
There's not a lot of detail here, but from what I'm gathering I think what you will want is an injectable service (demonstrated here: https://angular.io/docs/ts/latest/tutorial/toh-pt4.html) to handle the data objects being shared between the components. Rather than type a bunch of code in here (which is better shown at that page in the tutorial) I'll describe what I think you're trying to do and how I'd go about doing it.
The entire store data model can be handled via a service (store.service.ts maybe). Which will have your CRUD functions exposed for the different properties of the store model. The list you are adding to here should have a public getter that returns an observable of the list in the service as well as a public function for adding and deleting from the list. Something like this:
#Injectable
export class StoreService {
private _storeList:BehaviorSubject<Array<any>> = new BehaviorSubject<Array<any>>([]);
/*I'm sure the store has other properties, set them up here. I'd suggest
breaking any arrays out of the general object (unless you want to use
pipes which are awesome but keeping it simple here) but if the store has
a lot of general properties (name, address, whatever) they can be stored
in a single BehaviorSubject of type any.
*/
constructor(){}
get StoreList() { return this._storeList.asObservable() }
public addToList(id) {
let curVal = this._storeList.getValue();
curVal.push(id);
this._storeList.next(curVal);
}
}
You would then inject this service into the constructor of both the parent and the child constructor(private _storeService:StoreService){} (and any other components that need it). The child could then subscribe to the list: get List() { return this._storeService.StoreList } and the parent can call the add function to add to the list. One thing to note, when you add this to your template as an *ngFor, make sure to pass the value through the async pipe. *ngFor="List | async" or your may tear your hair out trying to figure out why you're getting errors.
This article helped me a lot with this as well (although I might suggest avoiding immutable at first until you're comfortable with Angular 2 completely): http://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/