Multiple stores within single component using NGXS - ngxs

Using ngxs I have below setup,
user.state.ts
export class UserState { ... }
product.state.ts
export class ProductState { ... }
App.component.ts
import {UserState} from './user.state'
import {ProductState} from './product.state'
export class App{
/***
I am not sure but like ngrx we can't say,
private userStore:Store<UserState> <============ can we do it in constructor like it
***/
#Select(UserState.getUsers) Users$: Observable<IUsers[]>; // This works
#Select(ProductState.getProducts) Products$: Observable<IProducts[]>; // This works
constructor(private store : Store) {} //<==== Can we have Store<UserState> or Store<ProductState>
ngOnInit(){
/***
Below In 1 Case : when I type this.store.select(state => state.
after .(dot) it doesn't resolve to .Users(Case 1) or .Products(Case 2)
Question: So how can I use two different states for below cases????????????
***/
// 1 Case)
this.store.select(state => state.Users.Users).subscribe((users: IUsers[]) => {
this.users = users;
})
// 2 Case)
this.store.select(state => state.Products.Products).subscribe((products: IProducts[]) => {
this.products = products;
})
}
}
Is there any elegant way to do using NGXS????

I think this might help you
// Keep this as it is
#Select(UserState.getUsers) Users$: Observable<IUsers[]>;
#Select(ProductState.getProducts) Products$: Observable<IProducts[]>;
// And then use it like this
this.Users$.subscribe((users: IUsers[]) => {
this.users = users;
})
this.Products$.subscribe.((products: IProducts[]) => {
this.products = products;
})

Related

Why my object instance's variable were not be able to be accessed correctly in cypress/ it block?

In my class object, I have a variable named category, which would be initialed in constructor.
export class Page {
constructor(category) {
this.category = category;
}
categorySession = `[data-cy="${this.category}-form"]`
enterValue(Value){
cy.get(this.categorySession).find('Value').type(`${Value}`).should('have.value',`${Value}`)
}
}
When I run the test,
In cypress, it throws me a error [data-cy="undefined-form"], but never found it.
import {Page} from "./pages/valuePage"
const LiquidityPage = new Page('Liquidity')
console.log(LiquidityPage.category) <--- it show Liquidity
describe('E2E_case', () => {
describe('Portfolio Value', () => {
it('Input Portfolio Value', () => {
cy.visit('http://localhost:30087/')
LiquidityPage.enterValue(123) // this gives me this error - Timed out retrying after 4000ms: Expected to find element: [data-cy="undefined-form"], but never found it.
})
})
Why is that [data-cy="undefined-form"] but not as my expected value [data-cy="Liquidity-form"]
You would also need to set sessionCategory in the constructor.
That way you will avoid the undefined value in the selector string.
export class Page {
constructor(category) {
this.category = category;
this.categorySession = `[data-cy="${this.category}-form"]`
}
...
}
But that seems quite obvious, you must have tried it already?

ngxs: Store not initialized when injected

I am using ngxs 3.7.5 with Angular 14
// single slice
#State<EnvironmentStateModel>({
name: 'environment',
defaults: {
productionBuild: environment.production,
internalPlatform: detectInternalPlatform(window.location.hostname, window.location.port),
platform: detectPlattform(),
appVersion: environment.appVersion
}
})
#Injectable()
export class EnvironmentState {
}
I am injecting the store into a HttpInterceptor
export class MessageHeaderInterceptor implements HttpInterceptor {
constructor(private userService: UserService, private store: Store) {
console.log('constructor', this.store.selectSnapshot((appState: AppState) => appState));
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('intercept', this.store.selectSnapshot((appState: AppState) => appState));
//...
}
}
The console statement in the constructor shows {}, also the console statement in the first call to intercept. However consecutive calls to intercept show { environment: ... } that is with the environment slice properly initialized. I expect the store to be properly initialized when using it the first time. What am I doing wrong?
When you include the NgxsModule in the import, the EnvironmentState should be passed to the NgxsModule's .forRoot() function (if it's a root store), or .forFeature() function (if it's a lazy-loaded store).
You can find more details here:
https://www.ngxs.io/getting-started/installation
https://www.ngxs.io/advanced/lazy

Cypress: The 'task' event has not been registered in the plugins file. You must register it before using cy.task()

I am writing the end-to-end tests using Cypress for my web application. In my tests, I am trying to create a task, https://docs.cypress.io/api/commands/task. But it is throwing an error. Here is what I did.
I declared a task in the plugins/index.js file as follow.
module.exports = (on) => {
on("task", {
setTestId(id) {
testId = id;
return null;
},
getTestId() {
return testId;
}
});
};
Then I use the task in the test as follow.
cy.task('setTestId', 7654321);
When I run the tests, I am getting the following error.
The 'task' event has not been registered in the plugins file. You must register it before using cy.task()
As you can see, I tried this solution as well, Cypress task fails and complains that task event has not been registered in the plugins file. It did not work either. What is wrong with my code and how can I fix it?
You're missing some formatting (":" and "=>" and such). Try this:
module.exports = (on) => {
on("task", {
setTestId: (id) => {
testId = id;
return null;
},
getTestId: () => {
return testId;
}
});
};
Set the testId as you did:
cy.task('setTestId', 7654321);
And when you want to retrieve the testId, use:
cy.task('getTestId').then((testId) => {
cy.log(testId)
});

How to prevent websocket from closing after certain time in angular5?

I am using 'rxjs-websockets' to connect with websockets. But after certain time (approx 2min)
the connection gets closed. How can I hold the connection until it is manually closed.
Here is the code snippet I use
constructor(private socketService: WebSocketService) {}
this.socketService.connect('/endpoint');
this.socketSubscription = this.socketService.messages
.subscribe(result => {
// perform operation
});
This is the WebSocketService
import {Injectable} from '#angular/core';
import {QueueingSubject} from 'queueing-subject';
import {Observable} from 'rxjs/Observable';
import websocketConnect from 'rxjs-websockets';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/retryWhen';
import 'rxjs/add/operator/delay';
#Injectable()
export class WebSocketService {
private inputStream: QueueingSubject<string>;
public messages: Observable<string>;
constructor() {
}
public connect(socketUrl) {
this.messages = websocketConnect(
socketUrl,
this.inputStream = new QueueingSubject<string>()
).messages.retryWhen(errors => errors.delay(1000))
.map(message => JSON.parse(message))
.share();
}
public send(message: string): void {
this.inputStream.next(message);
}
}
Websockets usually holds the connection for a long time interval with the help of some message exchange.
In our case we can call it as 'ping => pong', client sends message 'ping' and server may respond with message 'pong'.
You can send ping every 30 seconds as follows.
setInterval(() => {
this.socketService.send('ping');
}, 30000);
As you are converting every message received at WebSocketService into JSON, you have to make these change
to avaoid JSON Parsing error.
export class WebSocketService {
.
.
.
public connect(socketUrl) {
this.messages = websocketConnect(
socketUrl,
this.inputStream = new QueueingSubject<string>()
).messages.retryWhen(errors => errors.delay(1000))
//parse messages except pong to avoid JSON parsing error
.map(message => message === 'pong' ? message : JSON.parse(message))
.share();
}
.
.
.
}

BooleanField with FunctionField change number to Boolean

I have question and I'm sure it will help other developers.
I have field "is_active" which is Boolean in my API side but it return 0 or 1 and not TRUE or FALSE.
I want to use <FunctionField/> to wrap the <BooleanField/> but it didn't work. Someone can help please.
This is my code:
<FunctionField source="is_active" label="is_active" render={(record) => record.is_active ? true : false}>
<BooleanField/>
</FunctionField>
The column is still blank.
Thanks.
I think you misunderstood the FunctionField component. It renders the result of the render prop. What you are trying to achieve is:
<FunctionField source="is_active" label="is_active" render={(record,source) =>
<BooleanField record={{...record,is_active:!!record.is_active}} source={source}/>}/>
But this is not very nice. Better is to wrap your dataProvider/restClient and ensure the data is a boolean.
// In FixMyDataFeature.js
export default restClient => (type, resource, params) => restClient(type,resource,params).then(response=>
if(resource === 'Resource_with_numeric_is_active_field`){
return {
data: mutateIsActiveFieldToBoolean(response.data)
}
}
else{
return response;
}
);
And call it with Admin:
<Admin dataProvider={FixMyDataFeature(dataProvider)}... />
Here is my solution: (you can import it and use instead of BooleanField)
import React from 'react';
import { BooleanField } from "react-admin";
export const BooleanNumField = ({ record = {}, source}) => {
let theRecord = {...record};
theRecord[source + 'Num'] = !!parseInt(record[source]);
return <BooleanField record={theRecord} source={source + 'Num'} />
}
I had an issue where the in a DB table there was a field called disabled but in the Admin was a bit confusing setting disabled to false to actually enable something.
Based on 'Dennie de Lange' answer, I have created a Typescript generic BooleanOppositeField and BooleanOppositeInput. Putting here hoping may help someone:
import { BooleanField, BooleanInput, FunctionField } from 'react-admin';
interface IProps {
label: string;
source: string;
}
/**
* Usually called using:
* <BooleanOppositeField label="Enabled" source="disabled"/>
*/
export const BooleanOppositeField = (props: IProps) => {
return (
<FunctionField {...props} render={(record: any | undefined, source: string | undefined) =>
<BooleanField source="enabled" record={{ ...record, enabled: !(record![source!]) }} />}
/>
);
};
/**
* Usually called using:
* <BooleanOppositeInput label="Enabled" source="disabled" />
*/
export const BooleanOppositeInput = (props: IProps) => {
return (
<BooleanInput format={(v: boolean) => !v} parse={(v: boolean) => !v} {...props} />
)
}
And you can use it by:
<BooleanOppositeField label="Enabled" source="disabled"/>
or
<BooleanOppositeInput label="Enabled" source="disabled" />
Note: I liked more this solution, than the recommended by Dennie

Resources