Base solution for your next web application
Ends in:
01 DAYS
01 HRS
01 MIN
01 SEC

Activities of "winstat"

This is not about an issue. It is to help any users struggling with events in their components, specifically

  • how to unsubscribe successfully
  • how to access the component's variables within an event handler

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

  • What is your product version? 9.3
  • What is your product type (Angular or MVC)? Angular
  • What is product framework type (.net framework or .net core)? .net core

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.

Showing 1 to 4 of 4 entries