import { chain, map, Apply } from "fp-ts/lib/IO";
import { log } from "fp-ts/lib/Console";
import { pipe } from "fp-ts/lib/function";
import * as D from "fp-ts/lib/Date";
import { sequenceT } from "fp-ts/lib/Apply";
import { v4 as uuidv4 } from "uuid";
export const Article = (title: string) =>
([id, now]: [string, Date]) => ({
id,
title,
createdAt: now
});
const createArticle = (title: string) =>
pipe(sequenceT(Apply)(uuidv4, D.create), map(Article(title)));
const program = pipe(
createArticle("hello"),
chain(log)
);
program();
In the example above since Article requires 2 params that are side-effects. The question is on createArticle and if that could be written as a point free function.
You can do the piping in reverse by applying the values to the function using ap. I had to create a function that helps TypeScript figure out the type of of.
import { chain, map, Apply, of, ap } from "fp-ts/lib/IO";
import { log } from "fp-ts/lib/Console";
import { flow, pipe } from "fp-ts/lib/function";
import * as D from "fp-ts/lib/Date";
import { sequenceT } from "fp-ts/lib/Apply";
import { v4 as uuidv4 } from "uuid";
const Article =
(title: string) =>
([id, now]: [string, Date]) => ({
id,
title,
createdAt: now,
});
const ofString = (str: string) => of(str);
const createArticle = flow(
ofString,
map(Article),
ap(sequenceT(Apply)(uuidv4, D.create))
);
const program = pipe(createArticle("hello"), chain(log));
program();
Related
I am using nuxt3, pinia.
I can use the user, is_login variables in a specific vue page, as seen below.
import { useAuthStore } from "~/stores/myCustomAuthStore";
import { storeToRefs } from 'pinia'
const authStore = useAuthStore();
const {user, is_login} = storeToRefs(authStore)
What I want is to use the user, is_login variables in another page (or component) without writing the 4 lines of code above.
I think I need to use a plugin or module or nuxtApp.provide, how should I do it detaily?
------ what i tried is -------
I made plugins/common.ts
import { useAuthStore } from "~/stores/myCustomAuthStore";
import { storeToRefs } from 'pinia'
export default defineNuxtPlugin((nuxtApp) => {
const authStore = useAuthStore();
const {user, is_login} = storeToRefs(authStore)
nuxtApp.provide('user', user.value)
nuxtApp.provide('is_login', is_login.value)
}
and I put below code every
const is_login = useNuxtApp().$is_login
const user = useNuxtApp().$user
This is not work.
You can write a composable for this (see https://nuxt.com/docs/guide/directory-structure/composables#composables-directory):
Create a composables/use-auth.ts file in the "composables" directory
// composables/use-auth.ts
import { useAuthStore } from '~/stores/myCustomAuthStore';
import { storeToRefs } from 'pinia';
export const useAuth = () => {
const pinia = usePinia();
const authStore = useAuthStore(pinia);
const { user, is_login } = storeToRefs(authStore);
return {
user,
is_login,
};
}
Then in your component you can use it like:
<script setup>
const { user, is_login } = useAuth();
</script>
I am currently trying to build a simple example of redux using redux/toolkit. I am not sure what I am doing wrong.
This is my store.
import { configureStore } from "#reduxjs/toolkit";
import reducer from "./workouts";
export default function () {
return configureStore({ reducer });
}
Here is my workouts.ts file
import { createAction } from '#reduxjs/toolkit'
export interface IWorkout {
id: number
restTime: number
workoutTime: number
exercises: number[]
numReps: number
}
export interface IState {
workouts: IWorkout[]
}
export interface IAction {
type: string
payload: IWorkout[] | any
}
export const workoutAdded = createAction('workoutAdded')
export const workoutDeleted = createAction('workoutDeleted')
const defaultState: IState = {
workouts: [],
}
let lastId = 0
const reducer = (state: IState = defaultState, action: IAction): IState => {
switch (action.type) {
case workoutAdded.type:
return {
...state,
workouts: [
...state.workouts,
{
id: ++lastId,
restTime: action.payload.restTime,
workoutTime: action.payload.workoutTime,
exercises: action.payload.exercises,
numReps: action.payload.numReps,
},
],
}
case workoutDeleted.type:
return {
...state,
workouts: state.workouts.filter((workout) => workout.id !== action.payload.id),
}
default:
return state
}
}
export default reducer
And here is my index.ts file
import { render } from "react-dom";
import configureStore from "./store/store";
import { workoutAdded } from "./store/workouts";
import App from "./App";
const store = configureStore();
const newWorkout = {
restTime: 10,
workoutTime: 30,
exercises: [1, 2, 3],
NumReps: 6
};
store.dispatch(workoutAdded(newWorkout));
const rootElement = document.getElementById("root");
render(<App />, rootElement);
My error appears in index.ts file in this line:
store.dispatch(workoutAdded(newWorkout));
Not sure what I am missing. I reproduced the error in this sandbox
This is the error that I am getting in the console:
workoutAdded has 0 arguments, if you want to add arguments, you can implement Action
export class workoutAdded implements Action {
readonly type = "workoutAdded"
constructor(public payload: Workout){}
}
This fixed the issue. createAction can receive as a second argument a callback function that returns an object with the payload.
export const workoutAdded = createAction("workoutAdded", (workout) =>{
return { payload: workout };
});
I have this piece of code which I want to apply a filter on. The state uses EntityState and Adapter.
getAllObjects needs to get categories with parentId of 13 and the getAllTextures gets the remaining.
import { Category } from './models/category';
import { createFeatureSelector, createSelector } from '#ngrx/store';
import { AppState } from './app-state';
import { EntityState } from '#ngrx/entity';
import * as fromCategory from './services/reducers/category.reducer';
export interface CategoriesState extends EntityState<Category> {
allCategoriesLoaded: boolean;
}
const getCategories = createFeatureSelector<AppState, CategoriesState>('categories');
export const getAllObjects = createSelector(getCategories, fromCategory.selectAll); // filter(x => x.parentId === 13)
export const getAllTextures = createSelector(getCategories, fromCategory.selectAll); // filter(x => x.parentId !== 13)
export const getAllCategoriesLoaded = createSelector(getCategories, state => state.allCategoriesLoaded);
and this is the reducer:
import {createReducer, on, State} from '#ngrx/store';
import { EntityState, EntityAdapter, createEntityAdapter } from '#ngrx/entity';
import { Category } from 'src/app/models/category';
import { RefreshCategoryDone, CategoryActionTypes, CategoryActions } from '../actions/category.actions';
import { CategoriesState } from 'src/app/categories-state';
export const categoryAdapter: EntityAdapter<Category> = createEntityAdapter<Category>();
export const initialState: CategoriesState = categoryAdapter.getInitialState({allCategoriesLoaded : false});
export function categoryReducer(state = initialState, action: CategoryActions) {
switch (action.type) {
case CategoryActionTypes.LoadCategories:
categoryAdapter.addMany(action.payload, state);
break;
default:
return state;
}
}
export const {
selectAll,
selectEntities,
selectIds,
selectTotal
} = categoryAdapter.getSelectors();
Is it possible to select data from adapter by condition? If so, how?
Here's a few simple steps to set this up:
1) add a 'selectedId' property to your State and set the initial state to null
export interface State extends EntityState<Category> {
selectedCategoryId: string | null;
}
export const adapter: EntityAdapter<Category> = createEntityAdapter<Category>({
selectId: (category: Category) => category.id,
sortComparer: false,
});
export const initialState: State = adapter.getInitialState({
selectedCategoryId: null,
});
2) Add actions to set the selected ID
// in the reducer, the action does following:
on(ViewReportPageActions.selectCategory, (state, { id }) => ({
...state,
selectedCategoryId: id,
})),
3) add a selector for the selectedId in the reducer
export const getSelectedId = (state: State) => state.selectedReportId;
4) add a selector at your FeatureSelector file, this is where you add the filter
// you've implemented another name for you Feature Selector here
export const getDataState = createFeatureSelector<State, DataState>('data');
export const getCategoryEntitiesState = createSelector(
getDataState,
state => state.categories
);
export const getSelectedCategoryId = createSelector(
getCategoryEntitiesState,
fromCategorys.getSelectedId
);
export const getSelectedCategory = createSelector(
getCategoryEntities,
getSelectedCategoryId,
(entities, selectedId) => {
if (selectedId != "0"){
return selectedId && entities[selectedId];
} else {
//this is what I use, when I want to create a new category.
//I set the selectedId to 0 which indicitates I'm creating a new one
return selectedId && generateMockCategory();
}
}
);
export const getNotSelectedCategory = createSelector(
getCategoryEntities,
getSelectedCategoryId,
(entities, selectedId) => {
// You'll have to test this...
return entities.filter(e => e.id !== selectedID);
}
);
example pages:
Reducer file;
FeatureSelector Page reference function
'selectSelectedBook'
Found the answer:
import { Category } from './models/category';
import { createFeatureSelector, createSelector } from '#ngrx/store';
import { AppState } from './app-state';
import { EntityState } from '#ngrx/entity';
import * as fromCategory from './services/reducers/category.reducer';
import { map } from 'rxjs/operators';
export interface CategoriesState extends EntityState<Category> {
allCategoriesLoaded: boolean;
}
const getCategories = createFeatureSelector<AppState, CategoriesState>('categories');
export const getAllCategories = createSelector(getCategories, fromCategory.selectAll);
export const getAllObjects = createSelector(getAllCategories, (categories) => categories.filter(x => x.categoryId !== 13));
export const getAllTextures = createSelector(getAllCategories, (categories) => categories.filter(x => x.categoryId === 13));
export const getAllCategoriesLoaded = createSelector(getCategories, state => state.allCategoriesLoaded);
I have a state and I'd like to create selectors from ngrx/data entities.
import {
Action,
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '#ngrx/store';
import {environment} from '../../environments/environment';
import * as fromRouter from '#ngrx/router-store';
import * as fromDrawer from './drawer';
import {InjectionToken} from '#angular/core';
import {NavigationItem} from '../models/navigation-item';
export interface State {
router: fromRouter.RouterReducerState<any>;
drawerNavigationItems: fromDrawer.State;
}
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<State, Action>>('Root reducers token', {factory: () => ({
router: fromRouter.routerReducer,
drawerNavigationItems: fromDrawer.reducer,
}),
});
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');
const {
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectUrl, // select the current url
} = fromRouter.getSelectors(selectRouter);
export const selectRouteId = selectRouteParam('id');
export const selectStatus = selectQueryParam('status');
// Drawer
export const selectDrawerNavigationItems = (state: State) => state.drawerNavigationItems.items as NavigationItem[];
How do I use the pre-defined selectors or write my own with entities or services that came from ngrx/data?
As an example I'd like to create a selector that selects all "Community" entities and then, step 2, select 1 by selectRouteId.
If you imagine a route /communities/:id, selectRouteId returns the Id, and now I'd like the data from the CommunityService and use the selector created or somehow imported and used in step 1 and return a result, 1 Community with the selectRouteId's id, so I can later do something like this.store.dispatch(selectCommunityByCurrentRouteId);
This question is specific to #ngrx/data.
a supplementary answer, to concretely answer my own question,
here's what the reducers/index.ts looks like now
import {
Action,
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '#ngrx/store';
import {environment} from '../../environments/environment';
import * as fromRouter from '#ngrx/router-store';
import * as fromDrawer from './drawer';
import {InjectionToken} from '#angular/core';
import {NavigationItem} from '../models/navigation-item';
import {EntitySelectorsFactory} from '#ngrx/data';
import {Community} from '../models/community';
export interface State {
router: fromRouter.RouterReducerState<any>;
drawerNavigationItems: fromDrawer.State;
}
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<State, Action>>('Root reducers token', {factory: () => ({
router: fromRouter.routerReducer,
drawerNavigationItems: fromDrawer.reducer,
}),
});
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');
const {
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectUrl, // select the current url
} = fromRouter.getSelectors(selectRouter);
export const selectRouteId = selectRouteParam('id');
// export const selectStatus = selectQueryParam('status');
// Data
export const communitySelectors = new EntitySelectorsFactory().create<Community>('Community');
export const selectCommunityByRouteId = createSelector(
selectRouteId,
communitySelectors.selectEntities,
(id, communities) => communities.find(c => c.id === id)
);
// Drawer
export const selectDrawerNavigationItems = (state: State) => state.drawerNavigationItems.items as NavigationItem[];
You create a selector for the Community model with
export const communitySelectors = new EntitySelectorsFactory().create<Community>('Community');
and then you combine those two and return 1 Community by the route id.
export const selectCommunityByRouteId = createSelector(
selectRouteId,
communitySelectors.selectEntities,
(id, communities) => communities.find(c => c.id === id)
);
really simple, you pick the input streams, provide a projection function and return the result.
Later, in the component
export class OneCommunityComponent implements OnInit {
community$: Observable<Community>;
constructor(
private store: Store<State>,
) {
this.community$ = this.store.select(selectCommunityByRouteId);
}
}
See https://github.com/peterbsmith2/platform/blob/b2f17bfcc987bf63d10dd207263c0ca2a2e44373/projects/ngrx.io/content/guide/data/extension-points.md#custom-selectors.
/* src/app/reducers/index.ts */
import * as fromCat from './cat.reducer';
import { Owner } from '~/app/models'
export const ownerSelectors = new EntitySelectorsFactory().create<Owner>('Owner');
export interface State {
cat: fromCat.State;
}
export const reducers: ActionReducerMap<State> = {
cat: fromCat.reducer
};
export const selectCatState = (state: State) => state.cat;
export const {
selectAll: selectAllCats
} = fromCat.adapter.getSelectors(selectCatState);
export const selectedCatsWithOwners = createSelector(
selectAllCats,
ownerSelectors.selectEntities,
(cats, ownerEntities) => cats.map(c => ({
...c,
owner: ownerEntities[c.owner]
}))
);
The best solution in your situation it's adapters is very nice.
You can look just here : https://ngrx.io/guide/entity/adapter
You can remove, add, update every object in your store with that and very easily.
You just need to extend your state :)
I am using moment.js and am having trouble mocking the format function am recieving this error:
TypeError: this.$moment(...).format is not a function
could any one point me in the right direction on this please?
import { mount, createLocalVue, } from '#vue/test-utils'
import { expect } from 'chai'
import sinon from 'sinon'
import Vuex from 'vuex'
import authorisation from '../../src/components/settings/Authorisation.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
const renewAuthStub = sinon.stub()
localVue.filter('translate', () => {})
describe ('settings authorisation', () => {
let wrapper
let store
let state
let getters
let actions
beforeEach(() => {
state = {
authorisation: {
authorised: '2018-03-18',
expiry: '03-04-2019',
canBeExtended: true
}
}
getters = {
fetchAuth: () => '',
isLoading: () => false,
authorisation: () => state.authorisation,
highlightDates: () => {},
disabledDates: () => {},
requestLoading: () => false
}
actions = {
fetchAuth: () => {}
}
store = new Vuex.Store({
state,
getters,
actions
})
wrapper = mount(authorisation, {
localVue,
store,
mocks: {
$moment: () => sinon.stub().resolves()
}
})
})
it('lets user know to reactivate when not authorised', () => {
So I realised that I was using udateLocale('en', English) so as a consequence I needed to also use this within my test so therefore needed to add the following:
import { mount, createLocalVue, } from '#vue/test-utils'
import { expect } from 'chai'
import sinon from 'sinon'
import Moment from 'moment'
import English from '../../src/i18n/moment/en'
import Vuex from 'vuex'
import Vue from 'vue'
import authorisation from '../../src/components/settings/Authorisation.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
const renewAuthStub = sinon.stub()
localVue.filter('translate', () => {})
Vue.prototype.$moment = Moment
Moment.updateLocale('en', English)
describe ('settings authorisation', () => {