This is not about an issue. It is to help any users struggling with events in their components, specifically
If you have a component that subscribes to abp events, you must unsubscribe when the component is destroyed. Otherwise, when the component subscribes again in its next instantiation, the handler will be called twice. However, getting this right is not easy. There have been some threads about this matter, but I haven’t seen any solutions that give an example of putting it all together correctly.
There are two main problems.
PROBLEM 1: abp.event.on and abp.event.off need to refer to the same instance of the handler. In the abp documentation, there is only this example of an event handler:
abp.event.on('itemAddedToBasket', function (item) {
console.log(item.name + ' was added to basket!');
});
Unfortunately: If you use this format for abp.event.on, there is no way to ever unsubscribe using abp.event.off. The reason is that the handler is embedded as a lambda and has no reference that can be used later for unsubscribing. The documentation does mention this problem:
You can use the abp.event.off method to unregister from an event. Note that the same function should be provided so it can be unregistered. So for the example above, you must set the callback function to a variable, then use both the on and off methods.
But there is no example given. And there is no mention of the fact that, if you do define a named method for the event handler, this method is called within the scope of the EVENT instance, not within the scope of the COMPONENT instance, no matter where the method is defined. Which brings us to:
PROBLEM 2: It is quite likely that, if you subscribe to an event within a component, you will want to interact with the component in your event handler. Maybe you’ll need to set a member variable depending on the event data. But if you put a breakpoint in your event handler, you will see that “this” refers to the event class (with the methods “on” and “off”) and not to the component. Any attempt to reference, say, “this.myVariable” will fail. So the first thing the event handler must do is change the scope to that of the component. The following example shows how to put it all together.
interface AbpSubscription {
name: string;
handler: (...args: any[]) => void;
}
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html'
})
export class MyComponent extends AppComponentBase implements OnInit, OnDestroy {
subscriptions: Array<AbpSubscription> = [];
eventContents: string;
constructor(
injector: Injector,
public _zone: NgZone
) {
super(injector);
}
ngOnInit() {
// register all subscriptions
this.registerSubscriptions();
}
ngOnDestroy() {
// unregister all registered subscriptions
this.unRegisterSubscriptions();
}
// register all subscriptions
registerSubscriptions() {
const self = this;
// register handler for 'EventA' which emits one argument
this.registerSubscription('EventA', eventAHandler);
// this is the registered handler
function eventAHandler(arg1: string)
{
// switch to the component scope
self._zone.run(() => {
// call the component's 'real' event handler
self.onEventA(arg1);
});
}
// register handler for 'EventB' which emits two arguments
this.registerSubscription('EventB', eventBHandler);
// this is the registered handler
function eventBHandler(arg1: string, arg2: string)
{
// switch to the component scope
self._zone.run(() => {
// call the component's 'real' event handler
self.onEventB(arg1, arg2);
});
}
}
// register a single subscription
registerSubscription(name: string, handler: (...args: any[]) => void) {
// activate the subscription
abp.event.on(name, handler);
// remember this subscription for the unRegister method
this.subscriptions.push({
name: name,
handler: handler
});
}
// unregister all registered subscriptions
unRegisterSubscriptions() {
// call abp.event.off on each subscription
this.subscriptions.forEach(s => abp.event.off(s.name, s.handler));
// clear the list of subscriptions
this.subscriptions = [];
}
// the component's event handler for 'EventA'
onEventA(arg1: string) {
// we have access to the component's variables
this.eventContents = arg1;
}
// the component's event handler for 'EventB'
onEventB(arg1: string, arg2: string) {
// we have access to the component's variables
this.eventContents = arg1 + arg2;
}
}
Hi, I have the same requirement and I understand the necessity to write a custom ITenantResolveContributor.
But how to I fetch the current URL from within my contributor? Is there a service or a class I need to know about?
Thanks
Solved!
By running the RAD Tool from the command line, I could see a message that .net core 2.2 is not installed (VS 2019 apparently doesn't install this version).
Go to this page: https://dotnet.microsoft.com/download/visual-studio-sdks
Select .NET Core 2.2 "x64 Runtime" (no need to install the SDK, since you only need to get the RAD Tool running).
Once installed (no reboot necessary) everything runs normally!
Regards, and thanks for the great product (developing my second asp.net ZERO project now), Bob
Are there issues with the Power Tools under VS 2019? After designing the Entity and selecting "Generate", there is a pause in which a CMD window appears very shortly, then nothing else happens. No files have been added to the solution.