Replacing the query causing NavigationDuplicated error in Vue-router - vue.js

I need to remove the value from arrayed query parameter. Suppose, when query is
{
item_ids: [ "12", "13" ],
other_param: [ "alpha", "bravo" ]
}
my function removeElementFromArrayedQueryParameter('item_ids', 13) must turn query to:
{
item_ids: [ "12" ],
other_param: [ "alpha", "bravo" ]
}
Implementation (TypeScript):
function removeElementFromArrayedQueryParameter(key: string, value: string): void {
/** 〔Theory〕 Preventing 'NavigationDuplicated: Navigating to current location ("/〇〇") is not allowed' */
if (isEmptyObject(RoutingHelper.router.currentRoute.query)) {
return;
}
if (!Array.isArray(RoutingHelper.router.currentRoute.query[key])) {
return;
}
const updatedQuery: QueryParameters = {
...RoutingHelper.router.currentRoute.query as object
};
removeSingleElementFromArrayByPredicateMutably(
updatedQuery[key] as Array<string>, (arrayElement: string): boolean => arrayElement === value
);
console.log(JSON.stringify(updatedQuery, null, 2)); // I checked: the element has been romoved
// it's the router instance created by new VueRouter({})
RoutingHelper.router.push({
query: updatedQuery
})
.catch((error: Error): void => {
console.error(error)
});
}
function isEmptyObject(potentialObject: unknown): potentialObject is object {
if (typeof potentialObject !== "object" || potentialObject === null) {
return false;
}
return Object.entries(potentialObject as {[key: string]: unknown}).length === 0;
}
Although the removing of target element from updatedQuery successful (checked by manual testing), I have console error:
{
"_name": "NavigationDuplicated",
"name": "NavigationDuplicated",
"message": "Navigating to current location (\"/page?item_ids=12\") is not allowed"
}
"message" in console error contains right target location, but actually one of item_ids has not been removed from URI.
The console error is right about route name is same, but I don't going to redirect on same page: I just want to remove one query parameter. router.push casts similar error.
Update
Please note that TypeScript does not allow to write as
this.$router.replace({
...this.$router.currentRoute,
query
});
TS2769: No overload matches this call.
Overload 1 of 2, '(location: RawLocation): Promise<Route>', gave the following error.
Argument of type '{ query: Dictionary<string | (string | null)[] | null | undefined>; path: string; name?: string |
null | undefined; hash: string; params: Dictionary<string>; fullPath: string; matched: RouteRecord[]; redirectedFrom?: s
tring | undefined; meta?: any; }' is not assignable to parameter of type 'RawLocation'.
Type '{ query: Dictionary<string | (string | null)[] | null | undefined>; path: string; name?: string | null | und
efined; hash: string; params: Dictionary<string>; fullPath: string; matched: RouteRecord[]; redirectedFrom?: string | un
defined; meta?: any; }' is not assignable to type 'Location'.
Types of property 'name' are incompatible.
Type 'string | null | undefined' is not assignable to type 'string | undefined'.
Type 'null' is not assignable to type 'string | undefined'.
Overload 2 of 2, '(location: RawLocation, onComplete?: Function | undefined, onAbort?: ErrorHandler | undefined): void
', gave the following error.
Argument of type '{ query: Dictionary<string | (string | null)[] | null | undefined>; path: string; name?: string |
null | undefined; hash: string; params: Dictionary<string>; fullPath: string; matched: RouteRecord[]; redirectedFrom?: s
tring | undefined; meta?: any; }' is not assignable to parameter of type 'RawLocation'.
Type '{ query: Dictionary<string | (string | null)[] | null | undefined>; path: string; name?: string | null | und
efined; hash: string; params: Dictionary<string>; fullPath: string; matched: RouteRecord[]; redirectedFrom?: string | un
defined; meta?: any; }' is not assignable to type 'Location'.
Types of property 'name' are incompatible.
Type 'string | null | undefined' is not assignable to type 'string | undefined'.
Type 'null' is not assignable to type 'string | undefined'.
If the are no mistake in TypeScript types, above solution is not safe.
this.$router.replace({
...this.$router.name === null ? {} : RoutingHelper.router.currentRoute,
query: updatedQuery
})
does not fix it.

You should update your new route like this
function removeFromQuery(route, queryName, queryValue)
{
const query = Object.assign({}, route.query);
if (queryName in query)
{
const idx = query[queryName].indexOf(queryValue);
if (idx !== -1)
{
query[queryName].splice(idx, 1);
this.$router.replace({
...this.$router.currentRoute,
query
});
}
}
}

The updatedQuery query is not the deep clone of RoutingHelper.router.currentRoute.query. Below code is not enough to create the deep copy of query:
const updatedQuery: QueryParameters = {
...RoutingHelper.router.currentRoute.query as object
};
So, when execute
RoutingHelper.router.push({
query: updatedQuery
})
we don't subtitute query to new value. That why error occurs.
Use lodash or other libraries provides deep cloning, or use own implementation of deep cloning.

Related

react hook form typescript rules error when using returntype

I want to get the ReturnType of useControllerProps rules but I get an ts error:
Type 'Omit<Partial<{ required: string | ValidationRule<boolean>; min: ValidationRule<string | number>; max: ValidationRule<string | number>; ... 12 more ...; deps: string | string[]; }>, "valueAsNumber" | ... 2 more ... | "disabled"> | undefined' does not satisfy the constraint '(...args: any) => any'.
Type 'undefined' is not assignable to type '(...args: any) => any'.ts(2344)
Code:
export interface IReactHookFormInput {
label: string;
name: string;
rules: ReturnType<UseControllerProps['rules']>
}

Return custom Error in Union Graphql Type while show list data | returning list with union type in graphql [duplicate]

This question already has answers here:
GraphQL Expected Iterable, but did not find one for field xxx.yyy
(10 answers)
Closed 2 years ago.
I'm using Apollo server for my project returning list(array) of data when I try to return Error union type it shows this error:
"errors": [
{
"message": "Expected Iterable, but did not find one for field \"Query.getReports\".",
My Schema:
type Query {
getReports(id: ID!, patient_id: Int): [getReportUnion]!
}
union getReportUnion = Error | getReportResult
type getReportResult {
id: ID!
patient_id: Int!
}
type Error {
error: Boolean!
message: String!
}
my Resolver:
getReports: async (parent: any, args: any, context: any, info: any) => {
/**
* Simplify
*/
const { id, patient_id } = args;
const { isAuth, userId } = context.Auth;
/**
* Authenticating user is logged in
*/
if (!!!isAuth || userId !== id)
return { __typename: "Error", error: err, message: mesg };
// if a user is logged in then it works well
}
and my query:
query {
getReports(id: "5f449b73e2ccbc43aa5204d88", patient_id: 0) {
__typename
... on getReportResult {
patient_id
date
}
... on Error {
error
message
}
}
}
The problem is when I tried to pass the wrong id argument or jwt token, it shows the error. if every id and jwt token as header are right then it works like charm. so question is when id or jwt token is wrong, I want to show the Error type to inform user something isn't alright!
I already tried but not working:
type Query {
getReports(id: ID!, patient_id: Int): getReportUnion!
}
union getReportUnion = Error | [getReportResult]
it shows another error, is there any workaround to get rid of this error and show the Error. your answer is valuable to us!
If your field's type is a List, then your resolver must return either an iterable (i.e. an array) or a Promise that resolves to one.
The type for your field is a List ([getReportUnion]). However, inside your resolver, you are returning an object literal:
return { __typename: "Error", error: err, message: mesg }
You should return an array instead:
return [{ __typename: "Error", error: err, message: mesg }]
There is no way for you to return either a List of getReportResult objects or a single Error object. The only way to do that would be to wrap getReportResult with another type and use that type inside your union instead.
type Query {
getReports(id: ID!, patient_id: Int): GetReportPayload!
}
union GetReportPayload = Error | GetReportResults
type GetReportResults {
results: [Report!]!
}
type Report {
id: ID!
patientId: Int!
}
type Error {
error: Boolean!
message: String!
}

Nuxt.js : combine transition(to, from) and enter / leave events

So I have this code, it decides what to do on a transition between two pages and it works well :
export default {
transition: {
mode: 'out-in',
css: false,
beforeEnter (el) {
console.log('set transition');
},
enter (el, done) {
console.log('enter transition');
done();
},
leave (el, done) {
console.log('leave transition');
done();
},
}
}
Now I would like to specify what to do depending on what the next page is. So I've to use the transition(to, from) method according to the documentation. But unfortunately I can't mix this function with parameters like mode: 'out-in' and I can't manage to call the leave(el, done) function with the to, from parameters.
Does anyone knows how to combine this ? Thanks.
Maybe you found the solution, but I managed to combine transition with routes (to, from) and the object transition.
Let me explain :
If you look a the TypeScript declaration for vue, especially the ComponentOptions interface (node_modules/#nuxt/types/app/vue.d.ts)
Here's what we have :
declare module 'vue/types/options' {
// eslint-disable-next-line no-unused-vars,#typescript-eslint/no-unused-vars
interface ComponentOptions<V extends Vue> {
// eslint-disable-next-line #typescript-eslint/ban-types
asyncData?(ctx: Context): Promise<object | void> | object | void
fetch?(ctx: Context): Promise<void> | void
fetchKey?: string | ((getKey: (id: string) => number) => string)
fetchDelay?: number
fetchOnServer?: boolean | (() => boolean)
head?: MetaInfo | (() => MetaInfo)
key?: string | ((to: Route) => string)
layout?: string | ((ctx: Context) => string)
loading?: boolean
middleware?: Middleware | Middleware[]
scrollToTop?: boolean
transition?: string | Transition | ((to: Route, from: Route | undefined) => string | Transition)
validate?(ctx: Context): Promise<boolean> | boolean
watchQuery?: boolean | string[] | ((newQuery: Route['query'], oldQuery: Route['query']) => boolean)
meta?: { [key: string]: any }
}
}
The transition attribute is defined as follow :
transition?: string | Transition | ((to: Route, from: Route | undefined) => string | Transition)
So, by using the function approach, you must give either a string or a Transition object.
Example :
transition(to, from) {
if(!from) {
// returns a string
return 'my-custom-css-animation';
}
// returns an object Transition
return {
name: 'custom',
appear:false,
css: false,
beforeLeave() {
// before leave hook
},
leave(el, done) {
// leave hook
},
beforeEnter(el) {
// before enter hook
},
enter(el, done) {
// Enter hook
}
}
}
You should use pageTransition instead.
And did you try with a function?
export default {
pageTransition (to, from) {
if (!from) { return 'slide-left' }
return +to.query.page < +from.query.page ? 'slide-right' : 'slide-left'
}
}
Doc here: https://fr.nuxtjs.org/api/pages-transition/#fonction

Strapi graphql mutation Syntax Error: Unterminated string

I always get Syntax Error: Unterminated string when I try to update my database using javascript strapi sdk. this.chapter.content is a html string generated by ckeditor. How can I escape this string to update my database using graphql?
async updateChapter() {
const q = `
mutation {
updateChapter(input: {
where: {
id: "${this.$route.params.chapterId}"
},
data: {
content: "${this.chapter.content.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/(?:\r\n|\r|\n)/g, '\n')}"
title: "${this.chapter.title}"
}
}) {
chapter{
title
id
content
}
}
}
`;
const res = await strapi.request("post", "/graphql", {
data: {
query: q
}
});
this.chapter = res.data.chapter;
}
Technically you could use block string notation to get around this issue. However, you really should supply dynamic input values using variables instead of string interpolation. This way you can easily provide any of sort of values (strings, numbers, objects, etc.) and GraphQL will parse them accordingly -- including strings with line breaks.
const query = `
mutation MyMutation ($chapterId: ID!, $content: String!, $title: String!) {
updateChapter(input: {
where: {
id: $chapterId
},
data: {
content: $content
title: $title
}
}) {
chapter{
title
id
content
}
}
}
`
const variables = {
chapterId: '...',
content: '...',
title: '...',
}
const res = await strapi.request("post", "/graphql", {
data: {
query,
variables,
},
})
Note that $chapterId may need to be of the type String! instead if that's what's called for in the schema. Since variables can also be input object types, instead of providing 3 different variables, you could also provide a single variable to be passed to the input argument instead:
const query = `
mutation MyMutation ($input: SomeInputObjectTypeHere!) {
updateChapter(input: $input) {
chapter{
title
id
content
}
}
}
`
const variables = {
input: {
where: {
id: '...',
},
data: {
content: '...',
title: '...',
},
},
}
Again, just replace SomeInputObjectTypeHere with the appropriate type in your schema.
Another solution maybe help
Code with issue: For example mainReason and actionTaken fields are text inputs and data contains some white spaces. This action give error: Unterminated string
mutation { updateApplicationForm(input:{ where:{id:"${ticketData.id}"}
data:{
mainReason: "${ticketData.mainReason}"
actionTaken: "${ticketData.actionTaken}"
appStatus: ${ticketData.appStatus}
action: "${ticketData.action}"
}
Fix this problem with JSON.stringify method
mutation { updateApplicationForm(input:{ where:{id:"${ticketData.id}"}
data:{
mainReason:${JSON.stringify(ticketData.mainReason)}
actionTaken:${JSON.stringify(ticketData.actionTaken)}
appStatus: ${ticketData.appStatus}
action: "${ticketData.action}"
}

'|' or operator meaning in vuejs src code "vnode.js"?

I want to know the | operator meaning, could anyone explain?
here's some short code snippet in src/core/vdom/vnode.js
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
child: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
Here | is an OR unary operator.
ns: string | void;
which means ns is of type string, but if string is not defined due to some reason, it will be of type void. This should be just for precaution if it is not defined in some browser version.