How to use combineLatest and takeUntil rxjs operators in Angular 5 - angular5

I see that in Angular 5 one should be using rxjs operators differently and importing from 'rxjs/operators' but I'm a little unclear on how it is supposed to work. I have something like:
import { Observable } from 'rxjs/Observable';
import { combineLatest, takeUntil } from 'rxjs/operators';
#Component({ ... })
export class FooComponent implements OnInit, OnDestroy {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route_data = Observable.combineLatest(this.route.params, this.route.data,
(params, data) => ({params,data}));
this.route_data_sub = this.route_data.takeUntil(this.destroyed$).subscribe(
(params_and_data) => {
...
}
}
...
}
but I'm getting Observable.combineLatest is not a function errors. If I add the combineLatest operator the old way it works for combineLatest, but then takeUntil is now not found. How is this supposed to be done with Angular 5?
I have quite a bit of rxjs code all over the app and don't know how it is supposed to be rewritten or how to change the imports. Does everything have to be rewritten with .pipe() now?

You should import combileLatest use
import { combineLatest } from 'rxjs/observable/combineLatest';
For takeUntil
import { takeUntil } 'rxjs/operators';
I found that information:
combineLatest
takeUntil

#Mad Dandelion has the right answer but I figured it's worth showing what it looks like putting it together for anyone running across the same thing. You do have to pipe things like takeUntil. It's a bit of a pain to go through a large app and find all these spots but doesn't take that long. Doesn't look that bad either and has all the benefits in https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md under "why".
import { Observable } from 'rxjs/Observable';
import { combineLatest } from 'rxjs/observable/combineLatest';
import { takeUntil } from 'rxjs/operators';
#Component({ ... })
export class FooComponent implements OnInit, OnDestroy {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route_data = combineLatest(this.route.params,
this.route.data,
(params, data) => ({params,data})
);
this.route_data_sub = this.route_data
.pipe(takeUntil(this.destroyed$)) //<-- pipe()
.subscribe((params_and_data) => {
...
})
}
...
}
Also in my case I had some stale dlls serving the older rxjs (https://webpack.js.org/plugins/dll-plugin/) so if you run into something that looks like your Observables don't have the pipe property, you might want to make sure the dlls are building properly if you use that.

Related

SmartEditService.isLaunchedInSmartEdit() is always returns null

I have Spartacus storefront app.
Spartacus version is 3.2.2.
I have the requirement to check if the page is loaded in smart edit or not.
For that I tried to use this.smartEditService.isLaunchedInSmartEdit() but it always returns null value.
Please help me to find solution.
Below is my sample service code.
import { Product, ProductService, RoutingService, CmsService, SmartEditService } from '#spartacus/core';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root',
})
export class CurrentProductService {
constructor(
private smartEditService: SmartEditService
) {
}
getProduct(): Observable<Product> {
if (this.smartEditService && this.smartEditService.isLaunchedInSmartEdit()) {
return false
}
return true;
}
}
From 3.2, the SmartEditModule is deprecated. You can either import the deprecated SmartEditModule (from core) in your app, or use the SmartEditService from #spartacus/smartedit lib.

Nuxtjs using Vuex-module-decorator doesn't wordk

I want to use my vuex modules as classes to make my code more clean and readable. I used the section (Accessing modules with NuxtJS) at the bottom of this document: https://github.com/championswimmer/vuex-module-decorators/blob/master/README.md
I've searched for the solution for almost 3 days and tried out this link:
vuex not loading module decorated with vuex-module-decorators
but, it didn't work.
Also, I used getModule directly in the component like the solution in this issue page: https://github.com/championswimmer/vuex-module-decorators/issues/80
import CounterModule from '../store/modules/test_module';
import { getModule } from 'vuex-module-decorators';
let counterModule: CounterModule;
Then
created() {
counterModule = getModule(CounterModule, this.$store);
}
Then, accessing method elsewhere
computed: {
counter() {
return counterModule.getCount
}
}
it didn't work for me!
This is my Module in store folder in Nuxtjs project:
import { ICurrentUser } from '~/models/ICurrentUser'
import { Module, VuexModule, Mutation, MutationAction } from 'vuex-module-decorators'
#Module({ stateFactory: true, namespaced: true, name: 'CurrentUserStore' })
export default class CurrentUser extends VuexModule {
user: ICurrentUser = {
DisplayName: null,
UserId: null,
};
#Mutation
setUser(userInfo: ICurrentUser) {
this.user = userInfo;
}
get userInfo() {
return this.user;
}
}
In index.ts file in sore folder:
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import CurrentUser from './currentUser'
let currentUserStore: CurrentUser
const initializer = (store: Store<any>): void => {
debugger
currentUserStore = getModule(CurrentUser, store)
}
export const plugins = [initializer]
export {
currentUserStore,
}
I think the problem stems from this line:
currentUserStore = getModule(CurrentUser, store)
currentUserStore is created as object but properties and methods are not recognizable.
when I want to use getters or mutation I get error. For instance, "unknown mutation type" for using mutation
Probably several months late but I struggled with a similar issue, and eventually found the solution in https://github.com/championswimmer/vuex-module-decorators/issues/179
It talks about multiple requirements (which are summarised elsewhere)
The one that relates to this issue is that the file name of the module has to match the name you specify in the #Module definition.
In your case, if you rename your file from currentUser to CurrentUserStore' or change the name of the module toCurrentUser`, it should fix the issue.

RXJS operators are not functions

I am getting this error when I try to use rxjs in vue using vue-rx with rxjs.
[Vue warn]: Error in created hook: "TypeError: messageObservable.fromEvent(...).map(...).debounceTime is not a function"
I do not see any wrong imports from the documentation that I looked at and I am not getting any build errors when building the JS on my dev enviroment.
THese are the imports that I have
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
This is the fucntions calling these mehtods.
const messageObservable = Observable;
subscriptions(){
message$: messageObservable
},
created(){
message$.
fromEvent(document.querySelector('textarea'), 'input').
map(event => event.target.value).
debounceTime(500).
distinctUntilChanged().
subscribe({
next: function(value) {
console.log(value);
}
});
},
It seems that the tutorial you are following along is out to date. This imports are not longer working. (see changelog)
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
The latest and stable version is rxjs6. This is the correct way of using it:
import { fromEvent, map, debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'
import { Observable } from 'rxjs';
...
created() {
message$.
pipe(
map(event => event.target.value),
debounceTime(500),
distinctUntilChanged()
).subscribe(console.log);
}
I am guessing this is how you want to use fromEvent.
created() {
message$.
pipe(
switchMap(val => fromEvent(document.querySelector('textarea'), 'input'))
map(event => event.target.value),
debounceTime(500),
distinctUntilChanged()
).subscribe(console.log);
}

Using withMessageKey for custom validation messages in Aurelia

I have an Aurelia app and I am using the Aurelia validation tool for client-side validation. I want to use the validationMessages dictionary to define a list of custom validation messages to use throughout my app using withMessageKey like so:
import {validationMessages} from 'aurelia-validation';
validationMessages['customMessage1'] = `My first custom message`;
validationMessages['customMessage2'] = `My second custom message`;
And then when I set the validation rules on the class:
import { ValidationRules } from "aurelia-validation";
export class SampleObject {
text1;
text2;
constructor() {
ValidationRules
.ensure(a => a.text1)
.required()
.then().satisfies(x => x.trim() === x)
.withMessageKey('customMessage1')
.ensure(a => a.text2)
.satisfies( x => x.length > 5)
.withMessageKey('customMessage2')
.on(this);
}
};
The validation works, but the custom messages do not show up, the standard ones do. If I use withMessage('My first custom message') for example instead, then it does work, but I want to keep all of my custom messages in one place for use throughout the app.
What am I doing wrong?
Here is my solution:
I created a class which contains my custom messages in the constructor:
import { validationMessages } from 'aurelia-validation';
export class CustomValidationMessages {
constructor() {
validationMessages['customMessage1'] = `My first custom message`;
validationMessages['customMessage2'] = `My second custom message`;
}
}
Then, I inject it into my app.js:
import { inject } from 'aurelia-framework';
import { CustomValidationMessages } from "resources/utils/validation-messages";
#inject( CustomValidationMessages )
export class App {
constructor() {
}
configureRouter(config, router) {
.....
}
}
And I am able to use customMessage1 and customMessage2 everywhere throughout my app. I'm not sure this is the best way to do this, but it works.

With Aurelia, is there a way to store data clientside for reuse between view classes?

I have some data that is accessed between multiple views. If there any want to share that? In Angular, I was able to store stuff on a rootscope or parent controller, then it became available to all sub-views/controllers.
I see no reason to continually fetch them.
Services/classes are created as singletons (unless you tell the DI otherwise, I believe) so use a service class/module and inject it into your view controllers.
Then the service class can use internal, cached data or it can depend on, say, the http module and fetch data as needed.
EDIT: Added a bit of a sample:
I doubt this will work directly but it should give the basic idea.
Global service "someGlobalStuff.js":
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
#inject(HttpClient)
export class SomeGlobalStuff {
constructor(http) {
this.http = http;
}
getSomethingVital() {
if (this.somethingVital) {
return Promise.resolve(this.somethingVital)
} else {
// Do something with the HTTP client that will get the
// required stuff and return a promise
return this.http.get(blah blah blah)
.then(r => {
this.somethingVital = r;
return r; //
});
}
}
}
And something that uses it:
import {inject} from 'aurelia-framework';
import {SomeGlobalStuff} from 'someGlobalStuff';
#inject(SomeGlobalStuff)
export class DataManager {
constructor(someGlobalStuff) {
this.globalStuff = someGlobalStuff;
}
doSomething() {
this.globalStuff.getSomethingVital()
.then(v => {
// Do something with it
})
}
}