I'm currently doing a setup of an ngrx app. One of the goal is to use ngrx to train myself even if the application is too small.
It's a small project for beer inventory management.
I've a component in which I want to display my beer list:
export class BeerListComponent implements OnInit {
availableBeers$: Beer[];
constructor(private breadcrumbService: BreadcrumbService,
private beerService: BeersService,
private store: Store<fromBeer.BeerState>) {
this.breadcrumbService.setItems([
{ label: 'Beers' },
{ label: 'List' }
]);
}
ngOnInit() {
this.store.select(fromBeer.getAvailableBeers).subscribe((beers: Beer[]) => {
console.log(beers);
this.availableBeers$ = beers;
})
console.log('fetching beers');
this.beerService.fetchBeersList();
}
}
my beer service does the following:
export class BeersService {
private firebaseSubscription: Subscription[] = [];
constructor(
private db: AngularFirestore,
private store: Store<fromBeers.BeerState>) { }
fetchBeersList() {
this.firebaseSubscription.push(this.db
.collection('beers')
.valueChanges()
.subscribe((beers: Beer[]) => {
console.log("Received a firebase change");
console.log(beers);
this.store.dispatch(new SetAvailableBeers(beers));
}, error => {
console.log(error);
}));
}
}
and here are my reducer/actions:
Actions
export enum BeersActionTypes {
SET_AVAILABLE_BEERS = '[Beers] SET_AVAILABLE_BEERS'
};
export class SetAvailableBeers implements Action {
readonly type = BeersActionTypes.SET_AVAILABLE_BEERS;
constructor(public payload: Beer[]) {
console.log(payload);
}
}
export type BeersActions
= SetAvailableBeers;
Reducer
export interface BeerState {
availableBeers: Beer[];
};
const initialState: BeerState = {
availableBeers: []
};
export function beersReducer(state = initialState, action: BeersActions): BeerState {
switch (action.type) {
case BeersActionTypes.SET_AVAILABLE_BEERS: {
console.log("Setting available beerse")
return {
...state,
availableBeers: action.payload
};
}
default: {
return state;
}
}
}
export const getAvailableBeers = (state: BeerState) => state.availableBeers;
What I cannot understand:
I receive beers from firebase, but my component never gets the update. If I check the chrome dev tools, I only get one this undefined but this doesn't get updated after.
Here are my logs:
I feel I did not register correctly to my ngrx state, but I cannot figure out what I did wrong?
I think I found my error!
I was subscribing directly to the beerReducers.getAvailableBeers and not the one of my app.reducer(which contains the CreateSelector and stuff)
Related
Building state with ngxs in angular
//Component.ts
ngOnInit(): void {
const dataInsdeStore = this.store.selectSnapshot(MarketState);
if(!dataInsdeStore.loaded){
const category = this.marketplaceService.getMarketplaceCategories().subscribe(cat => {
const item = this.marketplaceService.getMarketplaceItems().subscribe(i => {
const obj = {
categories: cat,
items: i,
loaded: true
}
this.store.dispatch(new SetMarketAction(obj))
})
})
}
const test = this.store.selectSnapshot(MarketState);
this.store.dispatch(new GetMarketStateAction())
}
Upon checking wether the state is empty or not, and dispatching SetMarketAction I get empty object anyways
//Market.actions.ts
import { MarketStateInterface } from "../interfaces/interfaces";
class SetMarketAction {
static readonly type = '[MARKET] Set';
constructor(public marketState : MarketStateInterface){}
}
class GetMarketStateAction {
static readonly type = '[MARKET] Get';
}
export { SetMarketAction, GetMarketStateAction };
//Market.state.ts
import { Action, State, StateContext } from '#ngxs/store';
import { MarketStateInterface } from '../interfaces/interfaces';
import { Injectable } from '#angular/core';
import { GetMarketStateAction, SetMarketAction } from './market.actions';
#State<MarketStateInterface>({
name: 'market',
defaults: {
categories: [],
items: [],
loaded: false,
},
})
#Injectable()
export class MarketState {
constructor() {}
#Action(GetMarketStateAction)
getMarket(ctx: StateContext<MarketStateInterface>) {
const state = ctx.getState();
console.log(state);
return state;
}
#Action(SetMarketAction)
setMarket(ctx: StateContext<MarketStateInterface>, action: SetMarketAction) {
ctx.setState(action.marketState);
}
}
Every time I conosle.log the state at any given time I get the empty array, the interface of the state is correct
An unknown error is showing with ngrx and angular. When i use store.select of ngrx in my component with state variable. than it is giving an unknown error.
component file
#Component({
selector: 'app-all-user',
templateUrl: './all-user.component.html',
styleUrls: ['./all-user.component.css']
})
export class AllUserComponent implements OnInit {
constructor(private userService: UsersService, private readonly store: Store) { }
users: Users[] = []
isLoading:boolean = false
isLoaded: boolean = false
p: number = 1;
ngOnInit() {
this.getAllUsers()
}
getAllUsers(){
this.store.dispatch(new UserListAction())
this.userService.getAllUser().subscribe((res:any) => {
this.store.dispatch(new UserListCreatorSuccess(res.users))
})
this.store.select(getUserStateUser).subscribe((res:any) => {
this.users = res.users
})
}
}
Index reducer
export interface RootReducerState {
usersstate: fromUser.userReducerState
}
export const RootReducer: ActionReducerMap<RootReducerState> = {
usersstate: fromUser.userReducer
};
// get user state
export const getUserState = (state:fromUser.userReducerState) => state
export const getUserStateLoaded = createSelector(getUserState, (state:fromUser.userReducerState) => state.loaded)
export const getUserStateLoading = createSelector(getUserState, (state:fromUser.userReducerState) => state.loading)
export const getUserStateUser = createSelector(getUserState, (state:fromUser.userReducerState) => state.users)
export const metaReducers: MetaReducer<RootReducerState>[] = !environment.production ? [] : [];
It is working fine with angular 10 but on working with angular 14. It is giving issue of overload.
Please have a look at the screenshot
getUserState selector is not correct. it should be
export const getUserState = createFeatureSelector<fromUser.userReducerState>('usersstate')
A quick note and edit, it looks like this tutorial might be a winner
https://recursive.codes/blog/post/37
I am using the twilio conversation javascript client sdk on a angular 8 project.
Subscriptions, and async operations are still something I am working on understanding. My entire component that I am using twilio conversations on is below. After i will list my problems.
import {Component, Input, OnInit} from '#angular/core';
import {Client as ConversationsClient} from '#twilio/conversations';
#Component({
selector: 'app-shochat-contentcreator-chat',
templateUrl: './shochat-contentcreator-chat.component.html',
styleUrls: ['./shochat-contentcreator-chat.component.scss']
})
export class ShochatContentcreatorChatComponent implements OnInit {
constructor() { }
#Input() twiliochattoken = null;
conversationsclient;
currentconnectionstate = null;
ngOnInit(): void {
console.log('here we are and stuff tho');
let initConversations = async () => {
this.conversationsclient = await ConversationsClient.create(this.twiliochattoken);
this.conversationsclient.join().then((result) => {
console.log('here is the result of joining the conversation');
console.log(result);
});
}
this.conversationsclient.on("connectionStateChanged", (state) => {
if (state == "connecting") {
this.currentconnectionstate = 'connecting';
}
if (state == "connected") {
this.currentconnectionstate = 'connected';
}
if (state == 'disconnecting') {
this.currentconnectionstate = 'disconnecting';
}
if (state == 'disconnected') {
this.currentconnectionstate = 'disconnected';
}
if (state == 'denied') {
this.currentconnectionstate = 'denied';
}
});
this.conversationsclient.on("conversationJoined", (conversation) => {
console.log('here is the result of the conversationJoined hook');
console.log(conversation);
});
}
}
The below code snippet from the above is the problem:
this.conversationsclient.on("connectionStateChanged", (state) => {
if (state == "connecting") {
this.currentconnectionstate = 'connecting';
}
......
I am getting the error that the code cannot perform the .on function on undefined. Which makes sense, the above function is being called on the init function.
conversationsclient is undefined still. However what is the proper way to put this code? Inside the await ConversationsClient.create(.....) code?
Will that create the subscription that I want for when state changes?
Also how is my code looking based on its intent. I feel like I have missed the mark and not sure if I am close or far from hitting it.
im referencing the following docs
https://www.twilio.com/docs/chat/initializing-sdk-clients
This tutorial has the answer. I need to use a service.
chatservice:
import {EventEmitter, Injectable} from '#angular/core';
import * as Twilio from 'twilio-chat';
import Client from "twilio-chat";
import {Util} from "../util/util";
import {Channel} from "twilio-chat/lib/channel";
import {Router} from "#angular/router";
import {AuthService} from "./auth.service";
#Injectable()
export class ChatService {
public chatClient: Client;
public currentChannel: Channel;
public chatConnectedEmitter: EventEmitter<any> = new EventEmitter<any>()
public chatDisconnectedEmitter: EventEmitter<any> = new EventEmitter<any>()
constructor(
private router: Router,
private authService: AuthService,
) { }
connect(token) {
Twilio.Client.create(token).then( (client: Client) => {
this.chatClient = client;
this.chatConnectedEmitter.emit(true);
}).catch( (err: any) => {
this.chatDisconnectedEmitter.emit(true);
if( err.message.indexOf('token is expired') ) {
localStorage.removeItem('twackToken');
this.router.navigate(['/']);
}
});
}
getPublicChannels() {
return this.chatClient.getPublicChannelDescriptors();
}
getChannel(sid: string): Promise<Channel> {
return this.chatClient.getChannelBySid(sid);
}
createChannel(friendlyName: string, isPrivate: boolean=false) {
return this.chatClient.createChannel({friendlyName: friendlyName, isPrivate: isPrivate, uniqueName: Util.guid()});
}
}
component:
ngOnInit() {
this.isConnecting = true;
this.chatService.connect(localStorage.getItem('twackToken'));
this.conSub = this.chatService.chatConnectedEmitter.subscribe( () => {
this.isConnected = true;
this.isConnecting = false;
this.getChannels();
this.chatService.chatClient.on('channelAdded', () => {
this.getChannels();
});
this.chatService.chatClient.on('channelRemoved', () => {
this.getChannels();
});
this.chatService.chatClient.on('tokenExpired', () => {
this.authService.refreshToken();
});
})
this.disconSub = this.chatService.chatDisconnectedEmitter.subscribe( () => {
this.isConnecting = false;
this.isConnected = false;
});
}
I am using ngrx in an Angular 6 app and I can successfully load data from a database and display it on the screen. However, when I navigate to a new page in the app and then navigate back, the state is not preserved and it appears the #Effect is called again and loads the data from the database again. Here is my code:
Effect
export class ProductEffects {
constructor(private productService: ProductService, private actions$: Actions) { }
#Effect()
loadProducts$: Observable<Action> = this.actions$.pipe(
ofType(productActions.LOAD_ACTION),
switchMap(action =>
this.productsService.getProductDetails().pipe(
map(product => new productActions.LoadSuccess(product)),
catchError(err => of(new productActions.LoadError(err)))
)
)
);
Reducer
export interface ProductState {
product: Product;
}
const initialState: ProductState = {
product: {}
};
export function reducer(state = initialState, action: Action) {
switch (action.type) {
case SET_PRODUCT:
return { ...state, product: (action as SetProductAction).payload };
case LOAD_SUCCESS:
return { ...state, product: (action as LoadSuccess).payload, error: '' };
default: return state;
}
}
Actions
export const SET_PRODUCT = '[Product] Set Product';
export const LOAD = '[Product] Load';
export const LOAD_SUCCESS = '[Product] Load Success';
export const LOAD_ERROR = '[Product] Load Error';
export class SetProductAction implements Action {
readonly type = SET_PRODUCT;
constructor(public payload: Product) { }
}
export class Load implements Action {
readonly type = LOAD;
}
export class LoadSuccess implements Action {
readonly type = LOAD_SUCCESS;
constructor(public payload: Product) { }
}
export type ProductActions = SetProduct | Load | LoadSuccess;
Store
export interface State extends fromRoot.AppState {
product: fromProduct.ProductState;
}
// selectors
const getProductState = createFeatureSelector<fromProduct.ProductState>('product');
export const getProduct = createSelector(
getProductState,
state => state.product
}
Component
products$: Observable<Product>;
constructor(private store: Store<fromProduct.State>) { }
ngOnInit() {
this.store.dispatch(new Load());
this.products$ = this.store.pipe(select(fromProduct.getProduct));
}
Your ngrx implementation seems all ok to me. What you describe is an absolutely normal behavior. As soon as you navigate away from a page, the component destroyed and the new one is created, i.e. the ngOnInit is called. If you put the Loading logic in the ngOnInit, the state is loaded everytime you navigate to this page.
I am trying to do a get request with Angular2 and Firebase database. The post request works perfectly well but a get request won't work. I don't know what am doing so wrong.
Here is my list.component
export class ListComponent implements OnInit {
notes = []
constructor(
private store: Store,
private noteService: ListingService
) {
}
ngOnInit() {
this.noteService.getNotes()
.subscribe();
this.store.changes.pluck('notes')
.subscribe((notes: any) => { this.notes = notes; console.log(this.notes)});
}
onCreateNote(note) {
this.noteService.createNote(note)
.subscribe();
}
onNoteChecked(note) {
this.noteService.completeNote(note)
.subscribe();
}
}
Here is my api.service
export class ApiService {
headers: Headers = new Headers({
'Content-Type': 'application/json',
Accept: 'application/json'
});
api_url: string = 'https://someapp-94b34.firebaseio.com/';
constructor(private http: Http) {
}
private getJson(response: Response) {
return response.json();
}
private checkForError(response: Response): Response {
if (response.status >= 200 && response.status < 300) {
return response;
} else {
var error = new Error(response.statusText)
error['response'] = response;
console.error(error);
throw error;
}
}
get(path: string): Observable<any> {
return this.http.get(`${this.api_url}${path}.json`, { headers: this.headers })
.map(this.checkForError)
.catch(err => Observable.throw(err))
.map(this.getJson)
}
post(path: string, body): Observable<any> {
return this.http.post(
`${this.api_url}${path}.json`,
JSON.stringify(body),
{ headers: this.headers }
)
.map(this.checkForError)
.catch(err => Observable.throw(err))
.map(this.getJson)
}
}
Here is my listing.service
export class ListingService {
path: string = 'notes';
constructor(private storeHelper: StoreHelper, private apiService: ApiService) {}
createNote(note: Note) {
return this.apiService.post(this.path, note)
.do(savedNote => this.storeHelper.add('notes', savedNote))
}
getNotes() {
return this.apiService.get(this.path)
.do(res => this.storeHelper.update('notes', res.data));
}
completeNote(note: Note) {
return this.apiService.delete(`${this.path}/${note.id}`)
.do(res => this.storeHelper.findAndDelete('notes', res.id));
}
}
Here is my store.ts
export interface Note {
color: string,
title: string,
value: string,
id?: string | number,
createdAt?: string,
updatedAt?: string,
userId?: string
}
export interface State {
notes: Array<Note>
}
const defaultState = {
notes: []
}
const _store = new BehaviorSubject<State>(defaultState);
#Injectable()
export class Store {
private _store = _store;
changes = this._store.asObservable().distinctUntilChanged()
setState(state: State) {
this._store.next(state);
}
getState(): State {
return this._store.value;
}
purge() {
this._store.next(defaultState);
}
}
Here is my store-helper.ts
export class StoreHelper {
constructor(private store: Store) {}
update(prop, state) {
const currentState = this.store.getState();
this.store.setState(Object.assign({}, currentState, { [prop]: state }));
}
add(prop, state) {
const currentState = this.store.getState();
const collection = currentState[prop];
this.store.setState(Object.assign({}, currentState, { [prop]: [state, ...collection] }));
}
findAndUpdate(prop, state) {
const currentState = this.store.getState();
const collection = currentState[prop];
this.store.setState(Object.assign({}, currentState, {[prop]: collection.map(item => {
if (item.id !== state.id) {
return item;
}
return Object.assign({}, item, state)
})}))
}
findAndDelete(prop, id) {
const currentState = this.store.getState();
const collection = currentState[prop];
this.store.setState(Object.assign({}, currentState, {[prop]: collection.filter(item => item.id !== id)}));
}
}
And here is how am injecting the services to my app.module provider index.ts
import * as services from './services';
import { Store } from './store';
export const mapValuesToArray = (obj) => Object.keys(obj).map(key => obj[key]);
export const providers = [
Store,
...mapValuesToArray(services)
];
And app.module
import { providers } from './index'
providers: [providers, AnimationService]
The posting request works just well but the get request doesn't.
Here is the error I get:
Error encountered resolving symbol values statically.
Function calls are not supported.
Consider replacing the function or lambda with a reference to an exported function (position 3:33 in the original .ts file), resolving symbol mapValuesToArray in D:/angular2/someapp/src/app/index.ts, resolving symbol providers in D:/angular2/someapp/src/app/index.ts, resolving symbol providers in D:/angular2/someapp/src/app/index.ts, resolving symbol AppModule in D:/angular2/someapp/src/app/app.module.ts, resolving symbol AppModule in D:/angular2/someapp/src/app/app.module.ts
The AoT compiler cannot statically analyse the services you are providing, as you are using the Object.keys method to enumerate them.
You could solve the problem by adding an export to ./services.ts that explicitly lists the services:
export const SERVICE_PROVIDERS = [
ServiceOne,
ServiceTwo,
ServiceThree
];
Your import would then look like this:
import { SERVICE_PROVIDERS } from "./services";
import { Store } from './store';
export const providers = [
...SERVICE_PROVIDERS,
Store
];