I've started exploring Angular2 (I'm coming with Angular1 and a bit of React background) and I got stuck with a problem.
I want to bind certain keystrokes to actions in my component, so I've decided to use Angular2 lifecycle to bind/unbind actions.
However, if I do something from within a Mousetrap callback, it works, but it's not rendered and a change is not visible until a digest cycle is run.
Do I need to run something explicitly to update a view
Could somebody help me to figure out what is going on? Any help would be very appreciated.
import {Component} from 'angular2/core';
const Mousetrap = require('mousetrap');
@Component({
template: `<div>
Video template: Mode {{ mode }}
<input type="number" [(ngModel)]="mode"/>
</div>`
})
export class Video {
public mode: number;
constructor() {
this.mode = 0;
}
ngOnInit() {
console.log('hello Video component');
Mousetrap.bind('d', () => console.log('this.mode=', this.mode));
Mousetrap.bind('i', () => this.incrementMode()); // doesn't work
this.incrementMode(); // works
this.incrementMode(); // works
setTimeout(() => this.incrementMode(), 4000); // works
}
incrementMode() {
console.log('incMode', this.mode++);
};
ngOnDestroy() {
console.log('bye bye Video component');
Mousetrap.unbind(['d', 'i']);
}
}
Although @Günter's answer is absolutely correct I want to propose a different solution.
The problem with Mousetrap
library is that it creates its instance outside of the angular zone (see here). But to fire change detection after each async event the instance should be instantiated inside the angular zone. You have two options to achieve this:
bootstrap(App, [provide(Mousetrap, { useFactory: () => new Mousetrap() }) ]);
// ...
@Component({
selector: 'my-app',
// ...
})
export class App {
constructor(@Inject(Mousetrap) mousetrap) {
this.mousetrap = mousetrap;
// ...
}
//...
}
Mousetrap
inside of the constructor:@Component({
selector: 'my-app',
// ...
})
export class App {
constructor() {
this.mousetrap = new Mousetrap();
// ...
}
//...
}
In both cases you will have the ability to use the mousetrap instance like this:
ngOnInit() {
this.mousetrap.bind('i', () => this.incrementMode()); // It works now!!!
// ...
}
Now you don't need to use ngZone.run()
in every bind
call. In case of dependency injection you also can use this mousetrap
instance in any component/service of your application (not only in App
component).
See this plunk. I use dependency injection there.
If MouseTrap
is something outside Angular you might need to inject NgZone
and run your code like
Mousetrap.bind('i', () => this.ngZone.run(() => this.incrementMode()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With