How to provide data when emiting via EventEmitter in Angular 2? - javascript

According to this answer, I can define an event emitter to my output.
export class NavComponent {
#Output() poof: EventEmitter<any> = new EventEmitter();
onClick():void { this.poof.emit(null); }
}
Then, I can emit the event to the markup according to the below.
<navbar (poof)="catcher.boom()"></navbar>
<compa #catcher></compa>
I tried to provide an object instead of null using this.poof.emit({}); and altering the markup by catcher.boom(input). That didn't work and the console tells me that the input is undefined.
How can I provide an object using the setup above? Is there a wiser approach?

You need to use the implicit $event variable that provides the emitted value
<navbar (poof)="catcher.boom($event)"></navbar>

Related

angular 2 equivalent of vuejs v-bind

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
}

Separate two-way data binding in Angular

I have a custom table component that expects a model for some row selection actions that can be two-way bound like so:
<my-table [(selected)]="selectedRows"></my-table>
Optionally, I can also simply pass an item via one-way data binding if I don't care about the changes that happen to that model:
<my-table [selected]="selectedRows"></my-table>
If I want to not have a two-way bound data item, and instead want to have a data item I pass down to the table component via one-way data binding, and a handler/event emitter so that the syntax ends up not to different than this:
<my-table [selected]="selectedRows" (selected)="handleSelectedChanged($event)"></my-table>
Is it possible with no, or minimal changes to the my-table component? Or do I have to create an #Output parameter on the my-table component to which I pass handleSelectedChanged($event)?
Thank you!
No you don't need to do any changes inside the my-table component table. Only when you want to use custom event to be emitted use (selectedChange) instead of (selected) that's it. I hope you already had Input binding selected and Output binding selectedChange in a place inside my-table component. Also selected change property binding is completely optional.
<my-table
[selected]="selectedRows"
(selectedChange)="handleSelectedChanged($event)">
</my-table>
If you're wondering how two way binding stuff needed to have Change suffix on event, because that's by design. For understanding it more clearly you can have a look at angular ngModel directive as well.
<input [ngModel]="myModel" (ngModelChange)="myModel = $event" />
// You can also do assignment by calling function
<input [ngModel]="myModel" (ngModelChange)="inpuChanged($event)" />
can be written as
<input [(ngModel)]="myModel" />
parent.component.html
<my-table [selected2]="selectedRows" (selected)="handleSelectedChanged($event)"></my-table>
parent.component.ts
handleSelectedChanged(event){
// console.log(event)
this.selectedRows = event
}
my-table.component.ts
#Input() selected2: any;
#Output() selected: EventEmitter<any> = new EventEmitter();
OnCLCICK(){
this.selected.emit({'key':'value'})
}
or :--
user.service.ts
#Output() selected: EventEmitter<any> = new EventEmitter();
setselected(data) {
this.selected.emit(data);
}
getselected() {
return this.selected;
}
my-table.component.ts
#Input() selected2: any;
constructor(
public user: Userservice
){
}
onclick(){
this.user.setselected({'key','val'})
}
parent.component.html
<my-table [selected2]="selectedRows"></my-table>
parent.componnet.ts
constructor(
public user: Userservice
){
}
ngOnInit(){
this.userService.getselected().subscribe((data) => {
this.getData(data);
});
}
getData(data){
console.log(data)
}
Your my-table.component.ts already has an #Input() and #Output() implemented i.e through selected and selectedChange() .
For a custom two way data binding , angular expects the event emitter and the variable to be something like this :
#Input() date : date ;
#Output() dateChange : EventEmitter<Date> = new EventEmitter<Date>();
So when you use [(date)] , you have a two way data binding created .
if you don't want to use two way data binding you can omit the () in the [(date)] and just use [date] , and it will still behave like a normal parent child component communication .
If you want to listen to the changes and do some action to the date variable value , then you can use (dateChange) event emitter and bind it with a function so that you can listen for the changes .
Now to answer your question whether you have to create a new #Output() in my-table.component.ts , well you don't have to create any thing or add any event emitters to bind your handleSelectedChanged($event) as an Event Emitter is implemented through selectedChange() . All you have to do now is :
<my-table [selected]="selectedRows" (selectedChange)='handleSelectedChanged($event)'></my-table>
So now you have selectedRows as an input and selectedChange as an output that emits an event if anything changes in selected and the event is passed through handleSelectedChanged() .

How to make a component listen and wait for data from another component without an event in Angular 5?

I have a problem working with two components (pop ups) in which i have to send data from a chlid component to another one (parent) who doesn't have an event to extract this data.
logically i have to find a sort of function that makes the parent listen to the changes made in the child.
The Changes have to appear in the same time in both components.
Could any one help ?
The answer is in your question. You need an Output property, which is the Angular generalization of a JS event.
In your child component:
class ChildComponent {
#Input() someProperty: string;
#Output() dataChanged = new EventEmitter<string>();
whenSomethingHappensInChild() {
this.dataChanged.emit('something');
}
}
In your parent template:
...
<app-child [someProperty]="'someValue'" (dataChanged)="doSomething($event)"></app-child>
...
And in you Parent code:
class ParentComponent {
...
doSomething(theValue: string) {
// TA-DAA! you have the value.
}
...
}
Please, do yourself a favor and READ THE DOCS, or, better, a book ;)
In particular:
https://angular.io/guide/architecture-components has a full overview of the basics of binding, which this problem falls into.
Have a nice day.
Yes, you can use a shared BehaviorSubject to push values and both components have to subscribe to get this changes
Problem Solved: I used the #Host() tag to get the current instance of the Parent component and access the methode that changes it's attributes.
Here is what you should do.
First:
You should declate your parent component in the child
parent:ParentComponent;
Second :
you should pass your current parent instance to your new declaration in the constructor
constructor(#Host() currentParent:ParentComponent){
this.parent=currentParent
}
Third:
Now try just to access the methods and attributes in the parent components
changeParentAttribute(){
this.parent.iAmInTheParent();
}
I hope you find this helpful

Refactoring Angular components from many inputs/outputs to a single config object

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;
}
}

Angular 2 how to pass function with params to a child component?

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/

Categories