How to extend EntityCollectionReducerMethods NgRx Data - ngrx

I need to parse the data field before adding it to the store.
I was hoping to parse the data field from the override getAll().
This code doesn't work can someone explain why ?
export interface Alert {
id: string;
data: any;
}
const entityMetadata: EntityMetadataMap = {
Alert: {}
};
#Injectable({providedIn: 'root'})
export class AlertService extends EntityCollectionServiceBase<Alert> {
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('Alert', serviceElementsFactory);
}
getAll(options?: EntityActionOptions): Observable<Alert[]> {
return super.getAll(options)
.pipe(
map(alerts => {
alerts = alerts.map((alert: any) => ({...alert, data: JSON.parse(alert.data)}));
return alerts;
})
);
}

Related

Accessing a variable name in vue 3 like a class component does

I have only use Vue 2 with class components. Example:
#Options({
components: {
HelloWorld,
},
})
export default class Home extends Vue {}
However now that it is deprecated I am having issues access variables. I am not sure why I cannot use this anymore; I do not quite understand how the template will be aware of the variables and how the void methods can manipulate them.
</button>
{{name}}-{{age}}
env: {{ mode }} - My name: {{ myName}}
</div>
</template>
<script lang="ts">
import {api} from "#/lib/api";
export default {
name: "Home",
data() {
return {
name: String,
age: Number,
mode: process.env.NODE_ENV,
myName: process.env.VUE_APP_TITLE
}
},
methods: {
submit(): void {
api.getTest().then(
response => {
const testResponse = JSON.stringify(response)
this.name = JSON.parse(testResponse).name
this.age = parseInt(JSON.parse(testResponse).age)
}).catch(response => {
console.log("Error while getting the response", response)
})
},
counter(age: number): void {
age = age + 1
}
}
}
</script>
--- update 1 ----
I received some excellent advice from a poster, suggesting I ref or reactive.
Vue 3 is built with typescript which is why class components were decided to be deprecated. However I am not able to use my gRPC generated type objects, or at least I do not know how at this moment
IDE
axios
export const api = {
async getTest() {
try{
return await grpcClient.get<TestResponse>("/v1/test")
.then(res => {
console.log(url.baseUrl)
return res.data
})
}catch (err) {
console.log("error" + err);
}
},
}
So vue3 way of defining component is a bit different than v2 - more like native JS. Here's quick example how you component could look like in vue3. Instead of methods, just create function. Instead of data use reactive or ref.
import { reactive, computed } from 'vue'
import { api } from '#/lib/api'
export default {
setup() {
const state = reactive({
name: '',
age: 0,
mode: process.env.NODE_ENV,
myName: process.env.VUE_APP_TITLE
})
const submit = async () => {
try {
const response = await api.getTest()
state.name = response.name
state.age = response.age
} catch (error) {
console.log('Error while getting the response:', error)
}
}
const counter = (age) => {
state.age = age + 1
}
return {
...state,
submit
}
}
}

NGRX Select returning Store not a value

catalogueSelection in Store Image
I have the data I require in state (catalogueSelection: searchTextResult & categoryCheckboxResult) and need to pass the string 'SearchTextResult' into one component and the Array 'categoryCheckboxResult' into another.
When I try to retrieve the required values I am retrieving the whole store. I have looked at numerous websites and entries here but getting very confused now.
Model:
export class SearchTextResult {
searchTextResult: string;
}
export class CategoryCheckboxResult {
categoryCheckboxResult:Array<CategoryCheckboxResult>;
}
Actions:
import { Action } from '#ngrx/store';
import { SearchTextResult, CategoryCheckboxResult } from 'app/#core/services/products/products.model';
export enum UserCatalogueSelectionTypes {
AddSearchTextResult = '[SearchTextResult] AddResult',
AddCategoryCheckboxResult = '[CategoryCheckboxResult] AddResult',
GetSearchTextResult = '[SearchTextResult] GetResult',
}
export class AddSearchTextResult implements Action {
readonly type = UserCatalogueSelectionTypes.AddSearchTextResult;
constructor(public payload: SearchTextResult){
}
}
export class AddCategoryCheckboxResult implements Action {
readonly type = UserCatalogueSelectionTypes.AddCategoryCheckboxResult;
constructor(public payload: CategoryCheckboxResult){
}
}
export class GetSearchTextResult implements Action {
readonly type = UserCatalogueSelectionTypes.GetSearchTextResult;
}
export type UserCatalogueSelectionUnion =
| AddSearchTextResult
| AddCategoryCheckboxResult
| GetSearchTextResult
Reducers:
import { SearchTextResult, CategoryCheckboxResult} from "app/#core/services/products/products.model";
import { UserCatalogueSelectionTypes, UserCatalogueSelectionUnion} from "../actions/products.actions";
export interface UserCatalogueSelectionState {
searchTextResult: SearchTextResult | null;
categoryCheckboxResult: CategoryCheckboxResult | null;
}
export const initialState: UserCatalogueSelectionState = {
searchTextResult: null,
categoryCheckboxResult: null,
}
export function reducer(state:UserCatalogueSelectionState = initialState, action: UserCatalogueSelectionUnion ): UserCatalogueSelectionState{
switch (action.type) {
case UserCatalogueSelectionTypes.AddSearchTextResult:
return {
...state,
searchTextResult: action.payload,
};
case UserCatalogueSelectionTypes.AddCategoryCheckboxResult:
return {
...state,
categoryCheckboxResult: action.payload,
};
case UserCatalogueSelectionTypes.GetSearchTextResult: {
return state;
}
default: {
return state;
}
}
}
Selectors:
import { createSelector,createFeatureSelector } from "#ngrx/store";
import {UserCatalogueSelectionState} from '../../store/reducer/products.reducer';
export const fetchSearchTextResults = createFeatureSelector<UserCatalogueSelectionState>("searchTextResult");
export const fetchSearchTextResult = createSelector (
fetchSearchTextResults,
(state:UserCatalogueSelectionState) => state.searchTextResult.searchTextResult
);
export const fetchCatalogueCheckBoxResults = createFeatureSelector<UserCatalogueSelectionState>("catalogueCheckboxResult");
export const fetchCatalogueCheckBoxResult = createSelector (
fetchCatalogueCheckBoxResults,
(state: UserCatalogueSelectionState) => state.categoryCheckboxResult.categoryCheckboxResult
);
My Component 1
Observable:
public searchTextResult: Observable<String>;
Contructor: (part of)
private store: Store<fromCatalogueSelection.UserCatalogueSelectionState>
Code Snippet: (asking for the data)
this.searchTextResult = this.store.select('SearchTextResult');
console.log('TESTING SEARCH TEXT: ', this.searchTextResult);
Console:
TESTING SEARCH TEXT: StoreĀ {_isScalar: false, actionsObserver: ActionsSubject, reducerManager: >ReducerManager, source: Store, operator: DistinctUntilChangedOperator}
My Component 2
Observable
searchTextResult$: Observable<CatalogueSelectionActions.GetSearchTextResult>;
Code Snippet: (asking for the data)
this.searchTextResult$ = this.store.select('GetSearchTextResult');
console.log('TESTING SEARCH TEXT: ', this.searchTextResult$);
Console:
TESTING SEARCH TEXT: StoreĀ {_isScalar: false, actionsObserver: ActionsSubject, reducerManager: > ReducerManager, source: Store, operator: DistinctUntilChangedOperator}
I've given up on the Selectors for the moment. Any help much appreciated as I'm going a round in circles.
You are almost there, the value from the console log is the observable object, everytime you select something from the store, you will get the value wrapped within an observable. You just need to subscribe to it:
this.searchTextResult$ = this.store.select('GetSearchTextResult');
this.searchTextResult$.subscribe((yourData) => console.log(yourData));
Also, since you are working with selectors, use them, you don't have to write the state/selector name:
selector
...
export const fetchCatalogueCheckBoxResult = createSelector (
fetchCatalogueCheckBoxResults,
(state: UserCatalogueSelectionState) =>
state.categoryCheckboxResult.categoryCheckboxResult
);
component
import * as YourSelectors from './store/something/selectors/yourthing.selectors'
...
...
this.searchTextResult$ = this.store
.select(YourSelectors.fetchCatalogueCheckBoxResult)
.subscribe(console.log);
Additionally, try to subscribe using the async pipe delegating that to your template html so you don't have to deal with the subscription in the code, for example:
component
...
export class Component {
searchTextResult$!: Observable<any> // your data type here
...
...
this.searchTextResult$ = this.store
.select(YourSelectors.fetchCatalogueCheckBoxResult)
}
html
<ng-container *ngIf="(searchTextResult$ | async) as result">
<p>Your result value: {{ result }}</p>
</ng-container>

In ionic how to read and display the information from firestore

In ionic, I want to get and display information from firestore from its specific fields like Name there, but the problem is that it is displaying other documents' field Names too.
ngOnInit(){
this.authService.readc().subscribe(data => {
this.datas = data.map(e => {
return {
Name: e.payload.doc.data()['Name'],
};
})
console.log(this.datas);
});
}
}
name() {
var counter = this.firestore.doc(`info/${this.authService.userDetails().uid}`);
counter.set({
Name: this.Name
})
}
In authService
readc() {
return this.firestore.collection('info').snapshotChanges();
}
Something like this maybe?
home.page.ts
export class HomePage {
names: any;
constructor(auth: AuthService) {
auth.readc().subscribe(data => {
this.names = data.map(person => ({ name: person.name }));
});
}
}
auth.service.ts
export class AuthService {
people: any;
constructor(db: AngularFirestore) {
this.people = db.collection('people').valueChanges();
}
readc(){
return this.people;
}
}
Check out the angularfire docs for more detailed information on using Collections and Documents in Firebase.
Hope this helps.

NGRX 4: How to combine data from multiple sources

Question on how to combine reducers / data in app state:
Example of Data (Firebase):
{
"artists" : {
"168dgYui7ExaU612eooDF1" : {
"name" : "Brand New",
"id" : "168dgYui7ExaU612eooDF1",
}
},
"genres" : {
"popPunk" : {
"name" : "pop punk"
},
"screamo" : {
"name" : "screamo"
}
},
"genresPerArtist" : {
"168dgYui7ExaU612eooDF1" : {
"popPunk" : true,
"screamo" : true
}
}
}
App State:
export type State = { app: AppState };
export type AppState = { genres: IGenre[], artistGenres: IGenre[], artists: any[], currentArtist: any };
export const initialState: State = {
app: {
artists: [],
currentArtist: null,
genres: [],
artistGenres: []
}
}
Reducer:
export function appReducer(state: AppState, action: GenreActions.Actions): AppState {
// TODO: add reducers for Fetch Artist and Fetch Artists
switch (action.type) {
case GenreActions.FETCH_GENRES: {
return { ...state };
}
case GenreActions.FETCH_GENRES_SUCCESS: {
return { ...state, genres: action.payload };
}
case GenreActions.FETCH_ARTIST_GENRES: {
return { ...state };
}
case GenreActions.FETCH_ARTIST_GENRES_SUCCESS: {
return { ...state, artistGenres: action.payload };
}
default: {
return state;
}
}
}
Effects:
#Effect()
FetchGenres$: Observable<Action> = this.actions$
.ofType(appActions.FETCH_GENRES)
.switchMap(() => {
return this.firebaseProvider.genres
.map(payload => new appActions.FetchGenresSuccess(payload))
.catch(() => of(new appActions.FetchGenresSuccess([])));
});
// TODO: See if I should be using store's genres here, or if that should be combined elsewhere
#Effect()
FetchArtistGenres$: Observable<Action> = this.actions$
.ofType(appActions.FETCH_ARTIST_GENRES)
.map((action: appActions.FetchArtistGenres) => { return action.payload })
.switchMap((artistId: string) => {
return this.firebaseProvider.genresByArtist(artistId)
.withLatestFrom(this.store.select(state => state.app.genres))
.map((results) => {
let artistGenres = results[0];
let genres = results[1];
return genres.filter(genre => {
let result = artistGenres.findIndex(g => g.$key === genre.$key);
return (result ? true : false);
});
})
.map(payload => new appActions.FetchArtistGenresSuccess(payload));
});
Firebase Provider:
#Injectable()
export class FirebaseProvider {
genres: FirebaseListObservable<IGenre[]>;
constructor(
private db: AngularFireDatabase
) {
this.genres = this.db.list('/genres');
}
genresByArtist(artistId: string): Observable<IDictionary[]> {
return this.db.list(`/genresPerArtist/${artistId}`);
}
}
Artist Page:
My question is how are you supposed to combine different slices of the data in NGRX4?
Example: I have an Artist Detail page. In that page, I want to display the Artist info, along with the Artist's genres. Elsewhere, I might have a Artist List page where I display multiple artists and their genres. Where do I fetch and merge all of that relevant information in my app? In the reducer? In the effect (how FetchArtistGenres$ is currently doing it)?
Other concerns: I don't think I want to have the entire "genresPerArtist" or "artists" node in my app state, because it will eventually become very large and I'm not sure that would be performant (if I'm wrong here, and that's the way to go, let me know). Instead, I'd fetch the specific Artists genre node when I need it (/genresPerArtist/${artistId})

Ngrx: combine two selectors

I've built this IStore:
export interface IStore {
user: IUser;
sources: ISourceRedux;
}
where IUser is:
export interface IUser {
id: string;
cname: string;
sname: string;
...
}
and ISourceRedux is:
export interface ISourceRedux {
entities: { [key: string]: ISource };
ids: Array<string>;
selectedIds: Array<string>;
editingSource: ISource;
defaultId: string;
}
So, I've created these selectors:
export const getSourcesState = (state: IStore) => state.sources;
export const getSelectedIds = (sourceRdx: ISourceRedux) => sourceRdx.selectedIds;
export const getSelectedSourceIds = createSelector(getSourcesState, fromSources.getSelectedIds);
So, up to now, in order to check if a user is logged I did that:
this.store$
.select(fromRoot.getUserState)
.filter(user => user.id != null && user.logged)
.do(user => this.store$.dispatch(...))
...
Now I'm strugling for getting user information and selectedSourceIds at the same time in order to check if:
a user is logged (this.store$.select(fromRoot.getUserState)
then get all selectedSourceIds (this.store.select(fromRoot.getSelectedSourceIds))
dispatch an action
How could I get this?
Would it make sense to add that code to a selector:
// Selector functions
const getProductFeatureState = createFeatureSelector<ProductState>('products');
const getUserFeatureState = createFeatureSelector<UserState>('users');
export const getCurrentProduct = createSelector(
getProductFeatureState,
getUserFeatureState,
getCurrentProductId,
(state, user, currentProductId) => {
if (currentProductId === 0) {
return {
id: 0,
productName: '',
productCode: 'New',
description: 'New product from user ' + user.currentUser,
starRating: 0
};
} else {
return currentProductId ? state.products.find(p => p.id === currentProductId) : null;
}
}
);
This code is in the product.reducer file. Here I define the feature selector both for the products and for the users.
I then build a getCurrentProduct selector using both the product and user feature.
This is my solution:
this.store$.combineLatest(
this.store$.select(fromRoot.getUserEntity),
this.store$.select(fromRoot.getSelectedSourceIds),
(store, user, selectedSourceIds) => ({user: user, selectedSourceIds: selectedSourceIds})
)
.filter((proj) => proj.user.id != null && proj.user.logged)
.do((proj) => this.store$.dispatch({type: 'DELETE_CARDS', payload: {username: proj.user.username, tokens: proj.selectedSourceIds}}))
.take(1)
.subscribe();
I hope it's useful.
I created a feature selector that combines two features to accomplish this.
The feature selector for the global module:
export interface ScaffoldPartialState extends GlobalPartialState {
readonly [SCAFFOLD_FEATURE_KEY]: State;
}
which I import to the Scaffold selectors and have ScaffoldPartialState extend it.
export interface ScaffoldPartialState extends GlobalPartialState {
readonly [SCAFFOLD_FEATURE_KEY]: State;
}
The createFeatureSelector only returns the state typed so that the returned type looks like it contains only the state for this feature.
The value is the complete application state, but the type makes it looks like it's only for one module.
By one type extending the other, the resulting type provides a property for both modules.
AppState
{
module1: { ... },
module2: { ... },
module3: { ... }
}
Module2PartialState
{
module2: { ... },
}
Module2PartialState extends Module3PartialState
{
module2: { ... },
module3: { ... }
}
This way the ScaffoldPartialState feature selector works for selectors of both modules.
Example:
export const getAuthorizedMenuItems = createSelector(
getScaffoldState,
GlobalSelectors.getUserPermissions,
getMenuItems,
(_globalState, userPermissions, menuItems) =>
userPermissions ? menuItems.filter(e => userPermissions.checkAllPermissions(e.permissions)) : []
);

Resources