How to publish event not in component angular 2? - events

How to publish event not in component of angular 2?
I try:
let events = new Events(//Need agruments here);
events.publish('myevent');
But that need to pass in class arguments. How to do this?
I want global event catch in component. And event to publish anythere.
Explain:
I have component where I want to catch event.
#Component({
selector: 'category-blocks-listproductspage',
templateUrl: '/mobilesiteapp/template/?path=pages/category/blocks/listproductspage'
})
export class CatalogBlocksListProductsPage extends AbstractBlock{
constructor(
public events: Events
){
this.events.subscribe('category-blocks-filterspage.apply', (eventsData) => {
this.applyFilters(eventsData[0]);
});
}
}
And I have class where I want to publish Event.
export class ProductItem{
updateFavorite(response: Object, data: any)
{
this.product.toggleInFavorite(response, data);
let events = new Events(//Need agruments here);
events.publish('favorite-block-favoritepage.refresh', response['response']);
}
}
How to do this? Sorry for bad English.

Related

RxJs Observable duplicate values

So i have pretty straight forward scenario. One subject and observable. When client logs in i publish success, when user logs out i publish false.
Problem is in subscribe method in LoginComponent
First time everything works great. User logs in i get one event, but after that when user logs out second time and logs in again i get 2 same events, again if user logs out and then logs in i get 3 duplicate events and so on.
AuthService.ts
public _loggedIn: Subject<LoggedInOrResetPassword> = new Subject();
public loggedId: Observable<LoggedInOrResetPassword> = this._loggedIn.asObservable();
obtainAccessToken(){
// ommitted
this.httpClient.post(environment.baseUrl + url, null, requestOptions)
.subscribe(data => {
this.saveToken(data);
this._loggedIn.next(LoggedInOrResetPassword.createTrue());
});
// ommitted
}
private logout(navigateTo?: string){
this._loggedIn.next(LoggedInOrResetPassword.createFalse());
}
LoginComponent.ts
ngOnInit() {
this.authservice.loggedId.subscribe( ( loggedInOrResetPassword: LoggedInOrResetPassword ) => {
// HERE I GET DUPLICATE VALUES
});
The reason is that you are NOT unsubscribing when LoginComponent is destroyed.
Your code should be changed as follows
First add an instance property to LoginComponent to store the subscription, such as
export class LoginComponent implements OnInit, OnDestroy {
.....
loginSubscription: Subscription;
.....
}
Then change ngOnInit so that you store the subscription in the newly added property
ngOnInit() {
this.loginSubscription = this.authservice.loggedId.subscribe( ( loggedInOrResetPassword: LoggedInOrResetPassword ) => {
// HERE I GET DUPLICATE VALUES
});
Eventually add ngOnDestroy to make sure you unsubscribe when the component gets destroyed
ngOnDestroy {
if (this.loginSubscription) {
this.loginSubscription.unsubscribe();
}
}
Take a look at the async pipe of Angular as an alternative method to subscribe to Observables and automatically unsubscribe.

Is there a way to reach the subscription of a merged Observable (mergeMap/concatMap/exhaustMap)

I'm doing a paginator with RxJS, I use a subject to centralize any time a new page is called. And on each event, I use exhaustMap to retrieve the page. That's prevent the getPage http call to be fired several time for the same page.
this._nextPage$.pipe(
exhaustMap(nextPageNumber => this.getPage(nextPageNumber))
).subscribe();
But I'd like to also show a spinner on each http pending.
With this code, how to reach the subscription of the merged http Observable ?
(in order to bind a pending component to the subscription)
Thx in advance !
[EDIT]
I need/prefer using a Subscription that hold the pending state itself for 2 main reasons:
I already use several custom components/directives based on Subscription
As it's used in many different places I hopped to manage the pending state without too much boilerplate code ...
Here is a simple example of component displaying pending action
#Component({
selector: 'anie-busy',
templateUrl: './busy.component.html',
styleUrls: ['./busy.component.scss']
})
export class BusyComponent implements OnChanges {
#Input() subscription;
isPending = false;
constructor() { }
ngOnChanges() {
if (this.subscription) {
this.isPending = true;
this.subscription.add(() => this.isPending = false);
} else {
this.isPending = false;
}
}
}
You can pipe do operator in between to accomplish that.
this._nextPage$.pipe(
tap(()=>{//loading},
exhaustMap(nextPageNumber => this.getPage(nextPageNumber),
tap(()=>{// stop loading})
).subscribe();

Override registration decorator for Aurelia container in Jasmine test

We have a component that is registered as a transient service in the Aurelia DI container using a decorator, like this:
#transient()
export class EntityGraphObserver {
...
}
So that whenever it is injected, it is a new instance.
However when used in Jasmine specs, we want to override this default registration by passing a spy which is registered as an instance, so that we can mock the component, but it never works - the instance received by the test is always a new instance of the real component, not the spy, for example:
import { Container, Aurelia } from "aurelia-framework";
import { bootstrap } from "aurelia-bootstrapper";
import { StageComponent, ComponentTester } from "aurelia-testing";
import { Bootstrapper } from "...";
import { EntityGraphObserver } from "...";
describe("shareholder-step", () => {
let component: ComponentTester;
let observer: any;
let container: Container;
beforeEach(() => {
observer = jasmine.createSpyObj("EntityGraphObserver", ["attach"]);
component = StageComponent
.withResources("modules/business-details/shareholder-step")
.inView("<shareholder-step></shareholder-step>");
component.bootstrap((aurelia: Aurelia) => {
Bootstrapper.configure(aurelia);
container = aurelia.container;
container.registerInstance(EntityGraphObserver, observer);
});
});
afterEach(() => {
component.dispose();
});
it("initializes with shareholder data", async done => {
// arrange / act
await component.create(bootstrap);
const vm = component.viewModel as ShareholderStep;
await vm.activate(...); // some activation data
// assert
expect(vm.shareholders.length).toBe(1);
expect(observer.attach).toHaveBeenCalledTimes(1); // NEVER WORKS
done();
});
};
It looks like the transient decorator always overrides the registration we specify when bootstrapping the component for testing, which stops us from being able to isolate components with decorators.
The child container injected into the component is not available during the bootstrap phase, but I suspect that's where the transient registration is occurring, however I would expect it to only use that as the default registration where there is no existing registration in the container hierarchy, so maybe it's a bug in the Aurelia framework.
Is there a way to control the container setup so that registrations from decorators can be ignored or overridden, or is this a bug?

How can i override placeOrder() action in Magento 2

I'm newbie in Magento. My shop should work with a web service. I have to check availability of products from web service before magento creates a new order. And after creating order successful i have to send the orderId back to web service. All this actions should be execute when a customer confirm a button "place order".
In a picture you see an "Place Order". I not sure how Magento does create a new order. I assume that an action placeOrder() will be call. My aim is to put a method checkAvailability() before this action and and method sendOrderId() after this action. checkAvailability() and SendOrderId() are the methods from webservice.
Has somebody an idea, how and where can i do that?
Sorry about bad english. Thank you
If you need to overwrite a function instead a class method (I used to overwrite Magento_Checkout/js/action/place-order).
requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/place-order': {
'My_Module/js/action/place-order': true
}
}
}
};
place-order.js
define(['mage/utils/wrapper'], function (wrapper) {
'use strict';
return function (placeOrderAction) {
return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, redirectOnSuccess) {
// my own code here
return originalAction(paymentData, redirectOnSuccess);
});
};
});
For your requirement, you need to used this event.
Used this event observer to check checkAvailability()
checkout_onepage_controller_success_action
Used this event observer to used SendOrderId()
sales_order_place_after
I had a similar case. I needed to override placeOrder action that was announced in third part module (Amasty_Checkout).
So, my solution was to create mixin in my theme.
1) Announce the mixin in theme with myTheme/Amasty_Checkout/requirejs-config.js:
var config = {
config: {
mixins: {
'Amasty_Checkout/js/view/onepage': {
'Amasty_Checkout/js/view/onepage-extend': true
}
}
}
};
2) Add mixin myTheme/Amasty_Checkout/web/js/view/onepage-extend.js with code:
define(
[
'jquery',
'uiComponent',
'ko',
'uiRegistry',
'Magento_Checkout/js/model/quote',
'Amasty_Checkout/js/action/set-shipping-information',
'Amasty_Checkout/js/model/agreement-validator',
'Amasty_Checkout/js/model/agreement-validator-old',
'Magento_Checkout/js/model/payment/additional-validators',
'Amasty_Checkout/js/model/amalert',
'mage/translate'
],
function (
$,
Component,
ko,
registry,
quote,
setShippingInformationAction,
checkoutValidator,
checkoutValidatorOld,
additionalValidators,
alert,
$t
) {
'use strict';
var mixin = {
placeOrder: function () {
// Here you put your extended code
}
};
return function (target) { // target == Result that Magento_Ui/.../default returns.
return target.extend(mixin); // new result that all other modules receive
};
});
Note that in my case I copied all content in define[...] section from original module script ('Amasty_Checkout/js/view/onepage') that I needed to override.
Here is the resource that helped me with my solution https://github.com/magento/magento2/issues/1864#issuecomment-141112927
I hope this will help someone save time.

Angular Dart component events

I am trying to pass custom events from a component to its parent component/controller
confirm.html
<div class="comfirm-component">
<content></content>
Yes
No
</div>
confirm.dart
#Component(
selector: "confirm-component",
templateUrl: 'confirm.html',
useShadowDom: false,
publishAs: "ctrl"
)
class ConfirmComponent {
void yes(){
print('yes');
// Fire confirm-yes event
}
void no(){
print('no');
// Fire confirm-no event
}
}
is there something like this?:
<confirm-component on-confirm-yes="doSomething()" on-confirm-no="doSomethingElse()">
Do you want to delete
</confirm-component>
I could use a normal StreamController but then i'd had to connect my components with code.
confirmComponent.onConfirmYes.listen()
confirmComponent.onConfirmNo.listen()
I also found this:
How to communicate between Angular DART controllers
And this:
angulardart components - dispatch custom event
In both treads scope.emit is mentioned. But i didn't find a way to use it with a component instead of a controller. Is there a full example vor angular.dart v0.14.0?
Is scope.emit the thing i'm searching for?
This should be the same, just add a scope argument to the constructor so the component gets the scope injected.
There was a related change in Angular 0.14.0 https://github.com/angular/angular.dart/commit/181f01448555c475869505491159045904e5dc89
I haven't yet tried this.
From the description you need to implement ScopeAware
#Component(...)
class MyComponent implements ScopeAware {
Watch watch;
MyComponent(Dependency myDep) {
// It is an error to add a Scope / RootScope argument to the ctor and will result in a DI
// circular dependency error - the scope is never accessible in the class constructor
}
void set scope(Scope scope) {
// with this scope you should be able to use emit
// This setter gets called to initialize the scope
watch = scope.rootScope.watch("expression", (v, p) => ...);
}
}
Based on the answer from Günter i built this working example:
#Component(
selector: "confirm-component",
templateUrl: 'component/confirm.html',
useShadowDom: false,
publishAs: "ctrl"
)
class ConfirmComponent implements ScopeAware {
Scope scope;
void yes(){
scope.emit('confirm', 'yes');
}
void no(){
scope.emit('confirm', 'no');
}
}
#Component(
selector: "my-component",
templateUrl: 'component/my.html',
useShadowDom: false,
publishAs: "ctrl"
)
class MyComponent implements ScopeAware{
void set scope(Scope scope) {
Stream mystream = scope.on('confirm');
mystream.listen((event){
print('confirmed: ' + event.data);
});
}
}

Resources