Implement field normalization for all form fields - redux-form

Hi i would like to implement form normalization for uppercase like here Normalizing Form Data
const reducer = combineReducers({
// other reducers
form: form.normalize({
normalizing: { // <--- name of the form
upper: value => value && value.toUpperCase(), // normalizer for 'upper' field
phone: normalizePhone, // normalizer for 'phone' field
min: normalizeMin, // normalizer for 'min' field
max: normalizeMax // normalizer for 'max' field
}
})
});
I have 100 fields and i don't want to write each one of the field but pass some kind of array,something like that.
const reducer = combineReducers({
// other reducers
form: form.normalize({
normalizing: { // <--- name of the form
fieldsArray: value => value && value.toUpperCase(), // normalizer for 'upper' field
}
})
});
how this can be achieved?

Erik R Posted answer in his project
https://github.com/erikras/redux-form/issues/685

Related

Apollo Client 3, some way to filter each payload of mutations

I wonder if there is a way (plugin, middleware, etc) to apply some function to filter the string that is used on each mutation.
For instance:
// In my schema
input Comment {
status: "SENT",
comment: String
}
mutation updateStatus($id: String, $input: Comment!) {
updateStatus(id: $id, input: $input)
}
// and I call the mutation from React
useMutation(UPDATE_STATUS, { options });
But I have many similar mutations where I use some "input", so I wonder if it's possible to filter each used input with a simple function like:
// This will take each value of the input object and replace the strings that match with "some char"
const clearMutationPayload = (input) => Object.values(input).map(val => val.replace('some char', '');
Any idea?

RxJS logic which solves a filter/merge issue

This is more a logical problem then a RxJS problem, I guess, but I do not get it how to solve it.
[input 1]
From a cities stream, I will receive 1 or 2 objects (cities1 or cities2 are test fixtures).
1 object if their is only one language available, 2 objects for a city with both languages.
[input 2]
I do also have a selectedLanguage ("fr" or "nl")
[algo]
If the language of the object corresponds the selectedLanguage, I will pluck the city. This works for my RxJS when I receive 2 objects (cities2)
But since I also can receive 1 object, the filter is not the right thing to do
[question]
Should I check the cities stream FIRST if only one object exists and add another object. Or what are better RxJS/logical options?
const cities1 = [
{city: "LEUVEN", language: "nl"}
];
const cities2 = [
{city: "BRUSSEL", language: "nl"},
{city: "BRUXELLES", language: "fr"}
];
const selectedLang = "fr"
const source$ = from(cities1);
const result = source$.pipe(
mergeMap((city) => {
return of(selectedLang).pipe(
map(lang => {
return {
lang: city.language,
city: city.city,
selectedLang: lang
}
}),
filter(a => a.lang === selectedLang),
pluck('city')
)
}
)
);
result.subscribe(console.log)
If selectedLang is not an observable (i.e. you don't want this to change) then I think it would make it way easier if you keep it as a value:
const result = source$.pipe(
filter(city => city.language === selectedLang)
map(city => city.city)
);
There's nothing wrong from using external parameters, and it makes the stream easier to read.
Now, if selectedLang is an observable, and you want result to always give the city with that selectedLang, then you probably need to combine both streams, while keeping all the cities received so far:
const selectedLang$ = of(selectedLang); // This is actually a stream that can change value
const cities$ = source$.pipe(
scan((acc, city) => [...acc, city], [])
);
const result = combineLatest([selectedLang$, cities$]).pipe(
map(([selectedLang, cities]) => cities.find(city => city.language == selectedLang)),
filter(found => Boolean(found))
map(city => city.city)
)
Edit: note that this result will emit every time cities$ or selectedLang$ changes and one of the cities matches. If you don't want repeats, you can use the distinctUntilChanged() operator - Probably this could be optimised using an exhaustMap or something, but it makes it harder to read IMO.
Thanks for your repsonse. It's great value for me. Indeed I will forget about the selectedLang$ and pass it like a regular string. Problem 1 solved
I'll explain a bit more in detail my question. My observable$ cities$ in fact is a GET and will always return 1 or 2 two rows.
leuven:
[ { city: 'LEUVEN', language: 'nl', selectedLanguage: 'fr' } ]
brussel:
[
{ city: 'BRUSSEL', language: 'nl', selectedLanguage: 'fr' },
{ city: 'BRUXELLES', language: 'fr', selectedLanguage: 'fr' }
]
In case it returns two rows I will be able to filter out the right value
filter(city => city.language === selectedLang) => BRUXELLES when selectedLangue is "fr"
But in case I only receive one row, I should always return this city.
What is the best solution to this without using if statements? I've been trying to work with object destruct and scaning the array but the result is always one record.
// HTTP get
const leuven: City[] = [ {city: "LEUVEN", language: "nl"} ];
// same HTTP get
const brussel: City[] = [ {city: "BRUSSEL", language: "nl"},
{city: "BRUXELLES", language: "fr"}
];
mapp(of(brussel), "fr").subscribe(console.log);
function mapp(cities$: Observable<City[]>, selectedLanguage: string): Observable<any> {
return cities$.pipe(
map(cities => {
return cities.map(city => { return {...city, "selectedLanguage": selectedLanguage }}
)
}),
// scan((acc, value) => [...acc, { ...value, selectedLanguage} ])
)
}

How can the evaluation of a ngrx-store selector be controlled?

I have a selector:
const mySelector = createSelector(
selectorA,
selectorB,
(a, b) => ({
field1: a.field1,
field2: b.field2
})
)
I know the selector is evaluated when any of its inputs change.
In my use case, I need to control "mySelector" by a third selector "controlSelector", in the way that:
if "controlSelector" is false, "mySelector" does not evaluate a new value even in the case "selectorA" and/or "selectorB" changes, and returns the memoized value
if "controlSelector" is true, "mySelector" behaves normally.
Any suggestions?
Selectors are pure functions..its will recalculate when the input arguments are changed.
For your case its better to have another state/object to store the previous iteration values.
You can pass that as selector and based on controlSelector value you can decide what you can return.
state : {
previousObj: {
...
}
}
const prevSelector = createSelector(
...,
(state) => state.previousObj
)
const controlSelector = createSelector(...);
const mySelector = createSelector(
controlSelector,
prevSelector,
selectorA,
selectorB,
(control, a, b) => {
if(control) {
return prevSelector.previousObj
} else {
return {
field1: a.field1,
field2: b.field2
};
}
}
)
Sorry for the delay...
I have finally solved the issue not using NGRX selectors to build up those "higher selectors" and creating a class with functions that use combineLatest, filter, map and starWith
getPendingTasks(): Observable<PendingTask[]> {
return combineLatest(
this.localStore$.select(fromUISelectors.getEnabled),
this.localStore$.select(fromUISelectors.getShowSchoolHeadMasterView),
this.memStore$.select(fromPendingTaskSelectors.getAll)).pipe(
filter(([enabled, shmView, tasks]) => enabled),
map(([enabled, shmView, tasks]) => {
console.log('getPendingTasks');
return tasks.filter(task => task.onlyForSchoolHeadMaster === shmView);
}),
startWith([])
);
}
Keeping the NGRX selectors simple and doing the heavy lifting (nothing of that in this example, though) in this kind of "selectors":
- will generate an initial default value (startWith)
- will not generate new value while filter condition fails (that is, when not enabled, any changes in the other observables do not fire a new value of this observable)

Custom column filter on a formatted column

I need help using a custom column filter for handling JS objects.
I have a slickgrid table where the values in one column are JS object:
[
{ id: "1234", text: "Batman"},
{ id: "2345", text: "Robin"}
]
I use a custom formatter to smash the object into a string:
// convert [{id:string, text:string}...] to string
const optionFormatter: Formatter = (row, cell, value, columnDef, dataContext: any) =>
value ? value.map(o => o.text).join(', ') : '';
Which displays in slickgrid as
Batman, Robin
my slickgrid options use gridmenu and enables column filtering:
this.gridOptions = {
enableGridMenu: true,
enableFiltering: true,
enableAutoResize: true,
enableColumnReorder: true
};
My columnDef enables filtering for this column:
{
id: 'owners',
name: 'Owners',
field: 'owners',
sortable: false,
formatter: optionFormatter,
filterable: true
}
Everything works if the value in the cell is a string, but the filter doesn't work if the cell is an object. I assume the filter is searching the pre-formatted value.
Is there a way to provide the column with a custom filter function that knows how to search the JS object for the query string? For example if I could just search the JSON.stringify(value), that would be good enough.
Alternatively, this answer describes how I could use the formatter to store the formatted text as a different string property in dataContext. If I do that, how do I specify which property to filter, seeing as it is a different property than the column field?
I found a workaround.
preprocess my data, calling JSON.stringify on all values that are objects:
flattenFeature(f: Feature): any{
var res = {};
for (var prop in f) {
res[prop] = (typeof f[prop] === 'object') ? JSON.stringify(f[prop]) : f[prop];
}
return res;
}
Then in my formatter, I parse the json, before formatting:
// convert [{id:string, text:string}...] to string
const optionFormatter: Formatter = (row, cell, value, columnDef, dataContext) =>
value ? JSON.parse(value).map(o => o.text).join(', ') : '';
This allows the standard string filter to search the stringify'd JSON

Object mutations in graphQL

Here is my EventAttendee Object.
const EventAttendee = new GraphQLInputObjectType({
name: 'EventAttendee',
fields: () => ({
attendeeName: {type: GraphQLString},
personalizedDateSelection: {type: new GraphQLInputObjectType()}
})
});
The personalizedDateSelection property is an dynamic one and its properties are not known now. So, In this case, I have given GraphQLInputObjectType().
But it gives an error stating EventAttendee.personalizedDateSelection field type must be Output Type.
How to define an ObjectType whose properties are not known ?
I believe you want to define an InputObjectType and not an ObjectType as stated.
With that said, what you can do is to declare personalizedDateSelection as a string for now, and then later adapt it as needed with a custom input object, once you know what it should contain. GraphQLInputObjectType is used to define new input object types, it isn't a type per se.
I wanted personalizedDateSelection property of EventAttendee to be of objectType but I dont know those properties in advance, but I am sure that it is of Object type.
So declaring this as GraphQLScalarType was the correct way to do it. Check out about GraphQLScalarType. But we need to create a custom scalar type. All scalar types can be of input type. So here is my implementation:
const PersonalizedDateSelection = new GraphQLScalarType({
name: 'PersonalizedDateSelection',
serialize: value => {
return value;
},
parseValue: value => {
return value;
},
parseLiteral: ast => {
console.log("coming in parseLiteral");
console.log(ast);
let value = {};
if (ast.kind !== Kind.OBJECT) {
throw new GraphQLError("Query error: Can only parse object but got a: " + ast.kind, [ast]);
}
ast.fields.forEach(field => {
value[field.name.value] = parseJSONLiteral(field.value);
});
return value;
}
});

Resources