Why is the #Output EventEmitter required in this code example? - javascript

I'm currently reading about two way data binding in Angular 2 and reading this article.
https://blog.thoughtram.io/angular/2016/10/13/two-way-data-binding-in-angular-2.html
In this article, there is a child component with an #Input and #Output which allows a value inside the component to be bonded to a variable on its parent.
export class CustomCounterComponent {
counterValue = 0;
#Output() counterChange = new EventEmitter();
#Input()
get counter() {
return this.counterValue;
}
set counter(val) {
this.counterValue = val;
this.counterChange.emit(this.counterValue);
}
decrement() {
this.counter--;
}
increment() {
this.counter++;
}
}
parent HTML
<custom-counter [(counter)]="counterValue"></custom-counter>
<p><code>counterValue = {{counterValue}}</code></p>
So for me, I understand why the #Input is needed - however I don't understand how the #Output counterChange works because it's not even being subscribed by anything on the parent. However, it is necessary to have it there and also have it called counterChange in order to work.
The author of the article says
The next thing we need to do, is to introduce an #Output() event with
the same name, plus the Change suffix. We want to emit that event,
whenever the value of the counter property changes. Let’s add an
#Output() property and emit the latest value in the setter
interceptor:
Why do we need to have the same name plus change suffix? Is this some sort of Angular convention that I'm unaware of? I'm just trying to figure out which fundamental concept I've missed so I can understand how this is working.
I have a plunker of the code here if it'll help.
https://plnkr.co/edit/BubXFDQ59ipxEdnEHWiG?p=preview

The #Output() decorator enables the counterChange EventEmitter to be used in the Angular event syntax - (event name)="function()".
What stumbles you in this case is the ability of Angular to desugar the [(counter)] syntax (called 'banana in a box') to [counter]="..." (counterChange)="...". In other words, Angular will append *Change suffix to the property binding value when sees [(property name)] syntax.
I hope this answers the first question.
As to Why do we need to have the same name plus change suffix?, this is an Angular convention that helps utilizing the 'banana in a box' syntax.
Highly recommend this blog post that explains in details the Angular template syntax:
https://vsavkin.com/angular-2-template-syntax-5f2ee9f13c6a#4930

Related

Angular Custom Component databinding

I have the following use case where i want to pass a part of a complex object to an angular component.
<app-component [set]="data.set"></app-component>
Now i want the object 'data.set' in the parent class to always be the same like the object 'set' inside the child class.
If I instead do it the following way, both objects are the same and changes are "synced".
<app-component [set]="set"></app-component>
How can i achieve this behaviour, when binding 'data.set' instead of 'set', without manually triggering an EventEmitter?
If you need changes done to set within app-component to be visible in parent component, then, you need to use two-way binding.
<app-component [(set)]="data.set"></app-component>
In the app-component.component.ts file, you need to declare two members:
#Input()
public set: any;
#Ouput()
public setChange:EventEmitter = new EventEmitter();
And whenever, there is change to the value of set, you need to emit the updated value.
this.setChange.emit(newVal);
You could refer to this article if you need more details.

Angular - recalculate a variable on every change

I have a variable that stores the available cars at any moment. Is there a way to automatically re-evaluate this function on every change?
Just using this.carFactory.available in this case is not a solution, because this example I'm showing is simplified - the real calculation in my project is alot more complex.
calculateAvailableCars(){
this.carFactory.available.forEach(function(item){
this.availableCars.push(car.id);
}.bind(this));
}
How could I do this in Angular 2? In Angular JS there was the possibility to $watch a function.
I could of course manually call this function everytime something changes, but it would be nice not to have to call this function in every part of the application that can change the data.
Using template function reference with auto change detection
You can use this function output on template:
carOutput(): cars[] {
this.calculateAvailableCars()
return this.availableCars;
}
and use output on template:
<p>My car ratio is {{ carOutput() }} </p>
However this will trigger very aggressive change detection strategy on this variable. This solution is the simpliest one, but from engineering perspective rather worst: consumes tons of unnecessary function calls. One note, that hosting element must not be set to detect changes onPush.
Separate data model to parent component and pass as property to child
You can store car list display in separate component, and pass new car array as input property to this component:
<car-display [cars]="availableCars"></car-display>
Then you can set changeDetetcion policy in this component to onPush, and each time input property bind to availableCars will change, <car-display> will re-render.
If update relays on some host binding
If some external host action is triggering new cars calculation, then hostBinding may help:
#hostListener(`hover`) recalculateCars() {
this.calculateAvailableCars()
}
And finally, (because you describe your use case quite cryptically, without many details, thus I'm scratching all possible scenarios) if some external component action shall trigger re-calculation, you can hook to ngLifecycle ngOnChanges() if for example external input property change shall re-trigger cars calculation.
In other words and summing all that up, it depends who and from where triggers changes, that shall re-trigger available cars recalculation.
And very important, see an answer from #chiril.sarajiu, because what we are trying to work around here can be handled automatically by single observable. This requires additional setup (service, provide observable to components, e.c.t.) but it's worth.
--- EDIT ---
If each variable change shall retrigger data
As OP clarified, that changes are related with model bound to component. So another option with mentioned by #marvstar is using set, where each model variable change will retrigger fetching function:
modelSchangeSubject: Subject<Model> = new Subject<Model>();
ngOnInitt() {
this.modelSchangeSubject
.subscribe((v: Model) => {
this.calculateAvailableCars()
})
}
/* Rest of controller code */
set modelBounded(v: Model) {
this.modelSchangeSubject.next(v);
}
You need RxJS. What you do is you create a data service, which will store an Observable (in my case a BehaviorSubject, which is mostly the same, but in my case I start with a value).
export class DataService {
private dataStorage$ = new BehaviorSubject(null); //here is the data you start with
get getDataStorage() {
return this.dataStorage$.asObservable(); // so you won't be able to change it outside the service
}
set setDataStorage(data: any) {
this.dataStorage$.next(data);
}
}
Then you subscribe to this data changes everywhere you need to:
constructor(private dataService: DataService){}
ngOnInit() {
this.dataService.getDataStorage.subscribe((data) => this.calculateAvailableCars(data));
}
calculateAvailableCars(){
this.carFactory.available.forEach(function(item){
this.availableCars.push(car.id);
}.bind(this));
}
Read more about best practices of using RxJS in Angular, as there can be quite a bit of pitfalls and problems.
Try using setter and getter.
private _YourVariable:any;
public set YourVariable(value:any){
this._YourVariable = value;
//do your logik stuff here like. calculateAvailableCars
}
public get YourVariable():any{
return this._YourVariable ;
}

Should rxjs subjects be public in the class?

Let's say I have two classes, where you can observe over some observables.
First example, with public subject:
class EventsPub {
public readonly onEnd = new Subject<void>();
}
Second example, with private subject and registering method:
class EventsPriv {
private readonly endEvent = new Subject<void>();
public onEnd(cb: () => void): Subscription {
return this.endEvent.subscribe(cb);
}
}
The first example is somehow unsafe because anyone can call eventsPub.endEvent.next() from outside the class and introduce side effects, however, comparing to example 2 It allows for pipes, which is a big plus since developers can for ex. register only for the first event with eventsPub.onEnd.pipe(first()).subscribe(cb).
The second example also allows for one-time subscription but requires more code and ugly unsubscribing.
const subscription = eventsPriv.onEnd(() => {
// logic..
subscription.unsubscribe()
});
From your point of view, which is the best way to go? Or maybe there is a better solution?
This is based a lot on my personal preference but I'd do it like this:
class EventsPriv {
private readonly endEvent = new Subject<void>();
get endEvent$(): Observable<void> {
return this.endEvent;
}
}
So inside the class I'll use endEvent while I can still use it eg. in a template with obj.endEvent$ | async and from the outside it behaves like an Observable.
Note, that in fact I'm returning the same instance of Subject. The only thing that restricts the outside world from misusing it with obj.endEvent$.next() are Typescript's type guards. If I was using just JavaScript or if I typecasted it to any I could call next.
This is actually the recommended way of exposing Subjects instead of using the asObservable() operator. You can notice that this is used everywhere internally in RxJS 5. For example if you look at repeatWhen synopsys:
public repeatWhen(notifier: function(notifications: Observable): Observable): Observable
You can see that the notifier function receives an Observable as a parameter (you can see it in the code here as well https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L29).
But if you look into the code where the function is called you'll see they are in fact passing a Subject and not an Observable: https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L114-L115.
This has been discussed on RxJS GitHub page and reasons for this are performance and that the Typescript type guards are sufficient. You can read more in these discussions:
https://github.com/ReactiveX/rxjs/pull/2408
https://github.com/ReactiveX/rxjs/issues/2391

How to pass multiple data back from child to parent component in angular?

Currently, I am using angular 4 for my school project. I have an array, each item is a child component which can be updated and deleted, which means I should know the index and the data.
parent.ts:
updOne(i:number,stc:string):void{
this.myarray[i]=stc
}
delete(edu:string):void{
this.myarray=this.myarray.filter(x=>x!==edu)
}
parent.html:
<child-com [edu]=x [num]=i (updstr)="updOne($event)" (delstr)="delete($event)"></child-com>
child-com.ts:
#Input() edu:string
#Input() num:number
#Output() updstr: EventEmitter<string> = new EventEmitter<string>()
#Output() delstr: EventEmitter<string> = new EventEmitter<string>()
//some other code here
save():void{
this.updstr.emit(this.edu)
this.updating=false
}
del():void{
this.delstr.emit(this.edu)
}
delete works well, without a doubt. The problem is updating. Actually, using *ngFor, trackBy, and printing it all manually, this problem can be solved. But I wanna try using child component, as in React. When I play around with react, I can just use javascript closure, i.e. myfunc.bind(this,i,stc).
I've tried using bind here, no results
code when using bind:
parent.ts:
#Output() updstr: EventEmitter<number,string> = new EventEmitter<number,string>()
parent.html:
//I've tried some order
//this,i,$event
//$event,this,i
<child-com [edu]=x (updstr)="updOne.bind(this,$event,i)" (delstr)="delete($event)"></child-com>
And generics in typescript doesn't allow multiple data, so I cant emit more than one data
So my question is, how can I pass some data at once from child to parent, using emit or bind?
Thanks to Alex, using an object can substitute multiple data passing. Just to make sure that the data is correct, an interface is used, kind of like this
export interface Interview{
num:number
payload:{
dt:string
seeker:string
}
}
and used it like
#Output() updstr: EventEmitter<Interview> = new EventEmitter<Interview>()

Get access to Aurelia's Dependency Injection system without constructor injection

Is there a way to get access to Aurelia's Dependency Injection system without constructor injection.
I have a class called Box. I need to know when one of its properties change so I can update my validation. I found that I can use bindingEngine.propertyObserver from this answer.
But my instances of Box are created by BreezeJs, not Aurelia. So using #inject (or #autoinject in my case) to get the instance of bindingEngine is not going to work.
I saw aurelia.container.get will let me resolve from Aurelia's DI framework. But that needs the current instance of the Aurelia object. The only way I can see to get that is... constructor injection!
So, to get around constructor injection, you need... constructor injection!
I hope I am missing something and there is another way to get an instance of bindingEngine without constructor injection.
NOTE: For now I will just convert my variable in to a javascript property and fire an changed event on my own. But I know that this is going to move me to dirty checking... :(
If you want to know when a breeze entity's properties change, use the entityAspect.propertyChanged event:
http://breeze.github.io/doc-js/api-docs/classes/EntityAspect.html#event_propertyChanged
order.entityAspect.propertyChanged.subscribe(
function (propertyChangedArgs) {
// this code will be executed anytime a property value changes on the 'order' entity.
var entity = propertyChangedArgs.entity; // Note: entity === order
var propertyNameChanged = propertyChangedArgs.propertyName;
var oldValue = propertyChangedArgs.oldValue;
var newValue = propertyChangedArgs.newValue;
});
Circumventing constructor injection is not recommended. It violates the dependency inversion principle, however there is a mechanism for doing so:
main.js
export function configure(aurelia) {
aurelia.container.makeGlobal();
...
}
box.js
import {Container} from 'aurelia-dependency-injection';
let bindingEngine = Container.instance.get(BindingEngine);

Categories