Connect your Feathers API to the elegant data store for Vue
![Feathers Pinia Website Hero](https://user-images.githubusercontent.com/128857/190210685-df1dbd54-eb41-442e-90c1-d82ca44fc786.jpg)
Connect your Feathers API to the elegant data store for Vue
The documentation shows an example of being able to get the authenticated user through a getter on the Auth store:
https://feathers-pinia.pages.dev/guide/auth-stores.html#setup
The code referenced is:
const authStore = defineAuthStore({
feathersClient,
state: () => ({
userId: null
}),
getters: {
user() {
return this.userId ? User.getFromStore(this.userId) : null
},
},
actions: {
handleResponse(response: any) {
this.userId = response.user.id || response.user._id
User.addToStore(response.user)
return response
},
},
})
There are a couple of problems with this:
response: any
should be typed appropriately.Property 'userId' does not exist on type '{ user(): any; }'. Did you mean 'user'?
'user' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
user(): User | null {
return (this.userId ? User.getFromStore(this.userId) : null) as User | null
}
using const user = computed(() => props.id ? userStore.useGetOnce(props.id).data.value : null)
, a record that does not exist on server will trigger an infinite loop of querying the server with an uncaught 404 error.
I've tried wrapping the call to useGetOnce in a try/catch without success
Using handleClones, the save handler sends the data to patch, but we can't provide params as an argument.
An example use case:
When we use feathers-graphql-populate, this leads to the returned value being merged to the store without populated data, because we didn't pass $populateParams
for the patch event.
pinia: the native defineStore()
method requires state to be a function which returns an object e.g. state: () => ({...})
.
feathers-pinia: the defineStore()
method expects an object directly e.g. state: {...}
.
Developers can easily forget to pass an object causing store state to not be merged correctly.
The feathers-pinia defineStore()
wrapper should also only expect a function so that the wrapper can pass the options 'as is' to the original defineStore()
.
This appears to only occur when $sort
is set in the params sent to useFind
, but it may occur under similar scenarios. As seen below, as the query parameters change, such as changing the $sort
value, it causes a slight disruption in the UI before rendering the new data set.
This appears to be caused by this block of code in useFind
. What ends up happening is a call to makeUseFindItems is made multiple times when $sort
changes. When this occurs, the results from makeUseFindItems
are the following:
This is ultimately the cause of the UI disruption. To ensure it's not just an implementation, here are the following code blocks currently used.
domainStore
export class Domain extends BaseModel {
_id: string;
domain: string;
description: string;
createdAt: Date;
createdBy: string;
createdByUser?: User;
updatedAt: Date;
updatedBy: string;
updatedByUser?: User;
}
const servicePath = "system/domains";
export const useDomainStore = defineStore({ servicePath, Model: Domain });
Vue component
const domainStore = useDomainStore();
const { storeParams, pagination, loading, fetchRecords, setPaginationParams, setPagination } = useFeathersFindStore({ sortBy: "domain" });
const { data: domains, limit, currentPage, total, find } = domainStore.useFind(storeParams);
// This is called by Quasar when it is necessary to get records for the table.
const fetchDomains: QTableProps["onRequest"] = async (requestProps) => {
setPaginationParams(requestProps);
await fetchRecords(find, limit.value, total.value, currentPage.value);
};
useFeathersFindStore
import { ref, reactive } from "vue";
import { QTableProps } from "quasar";
import { FindFn, BaseModel, FindClassParams } from "feathers-pinia";
import { IFeathersStore, Pagination } from "src/types";
export const useFeathersFindStore = ({
sortBy = "_id",
sortDirection = 1
}: IFeathersStore) => {
const loading = ref(false);
const pagination = ref(new Pagination(sortBy));
const requestPagination = ref(new Pagination(sortBy));
const storeParams = reactive<FindClassParams>({
query: {
$limit: 10,
$skip: 0,
$sort: {
[sortBy]: sortDirection
}
},
paginate: true,
onServer: true,
immediate: false
});
const setPaginationParams: QTableProps["onRequest"] = (requestProps) => {
requestPagination.value = requestProps.pagination;
const {
sortBy: sort,
descending,
rowsPerPage,
page
} = requestProps.pagination;
storeParams.query = {
...storeParams.query,
$limit: rowsPerPage,
$skip: (page - 1) * rowsPerPage,
$sort: {
[sort]: descending ? -1 : 1
}
};
};
const setPagination = (limit: number, total: number, currentPage: number) => {
pagination.value = {
sortBy: requestPagination.value.sortBy,
descending: requestPagination.value.descending,
rowsPerPage: limit,
rowsNumber: total,
page: currentPage
};
};
const setLoading = (value: boolean) => {
loading.value = value;
};
const fetchRecords = async (
find: FindFn<BaseModel>,
limit: number,
total: number,
currentPage: number
) => {
setLoading(true);
try {
await find();
setPagination(limit, total, currentPage);
} catch (err: unknown) {
console.log("failed", err);
}
setLoading(false);
};
return {
storeParams,
pagination,
loading,
fetchRecords,
setPaginationParams,
setPagination,
setLoading
};
};
create
, update
, and patch
functions to BaseModel.tempIdField
.removeFromStore
to Feathers-Vuexparams.copies
from Feathers-VuexFeathersForm
wrapper around useClones
FeathersFind
wrapper around useFind
FeathersGet
wrapper around useGet
FeathersPagination
wrapper around usePagination
Originally posted by ryaposov November 8, 2022
$or
query operator does not work for me for .findInStore
getter. If it supported locally?
I found a test with associatedFind
using it, however do not see any related code that could make it work.
Could someone clarify? @marshallswain
Thanks!
In feathers-vuex
cloning a clone isn't possible. item.clone().clone()
throws an error: 'You cannot clone a copy'
.
// see https://github.com/feathersjs-ecosystem/feathers-vuex/blob/8643bd7b0cd045fcfd1e407143c3221ae58b7486/src/service-module/make-base-model.ts#L305-L313
if (this.__isClone) {
throw new Error('You cannot clone a copy')
}
In feathers-pinia
cloning a clone is totally possible. You already have a test for it. See:
feathers-pinia/tests/model-instance-clone-and-commit.test.ts
Lines 39 to 47 in 19a13ee
Don't know, if it's intended, though. In feathers-pinia
there is item.reset()
as in feathers-vuex
, but it calls store.resetCopy()
, which is not existent. See:
feathers-pinia/src/service-store/base-model.ts
Lines 131 to 135 in 19a13ee
If clone.clone()
is intended to work, that's a breaking change from feathers-vuex
worth mentioning in the docs!
Also constructor adds __isClone
differently compared to feathers-vuex
. In feathers-vuex
it only gets added when it should be a clone:
// see https://github.com/feathersjs-ecosystem/feathers-vuex/blob/8643bd7b0cd045fcfd1e407143c3221ae58b7486/src/service-module/make-base-model.ts#L151-L157
// Mark as a clone
if (options.clone) {
Object.defineProperty(this, '__isClone', {
value: true,
enumerable: false
})
}
In feathers-pinia
it always gets added as true
or false
:
You also make it explicitely defined in (see exclamation mark):
But on clone.commit()
you remove it. You have a test for that:
feathers-pinia/tests/model-instance-clone-and-commit.test.ts
Lines 27 to 37 in 19a13ee
That's because fast-copy
does not add the enumerable: false
property of __isClone
. Whats the intended way?
Todos:
clone.clone()
should work or not @marshallswainclone.__isClone
should always return a boolean or if it is not existent @marshallswainclone.reset()
running again, add old resetCopy
mutation as resetClone
action @fratzingerresetClone
renaming to the 'What's Different from Feathers-Vuex' sectionclone.reset()
@fratzingerclone.clone()
is intended to work, add it to the 'What's Different from Feathers-Vuex' section @fratzingerclone.__isClone
should always return a boolean, add Object.defineProperty
to store.commit
; change the test of commit()
accordingly @fratzingerconst { item, error } = useGet({ model, id })
Bug behavior, error not showing properly
error.value = null
After a call to create, item should contain the id returned from server.
To reproduce:
const post = New Post({
name: 'New post'
})
await post.create()
console.log(post.id) // returns undefined
Here is a quick fix :
let post = New Post({
name: 'New post'
})
await post.create()
const { data } = postStore.findInStore({
query: {
name: 'New post'
}
})
post = data[0]
console.log(post.id) // ok
But it's kind of broken as you could have many posts with the same name.
Just a quick thought:
MyModel.find({...})
returns MyModel[] | Paginated<MyModel>
.
When I need an array of values I do this:
const arrOrPaginated = MyModel.find({ ... });
const arr = Array.isArray(arrOrPaginated) ? arrOrPaginated : arrOrPaginated.data;
// now I can use arr but why so complicated?!
const arr = MyModel.find({ query: {}, paginate: false });
// I can safely know, that `arr` is an array
The asArray
flag could do exactly this:
const arr = Array.isArray(arrOrPaginated) ? arrOrPaginated : arrOrPaginated.data;
Even the type could be inferred to be an array (pretty much as paginate: false
on the server side).
Where to put this logic? Maybe here: https://github.com/marshallswain/feathers-pinia/blob/main/src/service-store/make-actions.ts#L112
We have afterFind
, but it does not know about params
. The signature of afterFind probably should be: afterFind(response, params)
anyways.
As the save_handler
, it would be nice to have a save('name')
option to send a PATCH
with an object containing only the mentioned property.
I can see that the setupInstance method is called from the BaseModel constructor, but it doesn't appear to be called for items returned from an API call that are added to the store automatically. Is this expected?
In case an error happens during server patch request on line 357, saved
variable will be undefined.
That results in the following error below on line 366.
TypeError: Cannot read properties of undefined (reading 'clone')
feathers-pinia/src/service-store/base-model.ts
Lines 335 to 367 in 71a9b39
When using useFind with onServer: true
, if a new item is added to server from another source, this newItem is not automagically shown.
It is listed in the store's itemsById though.
@marshallswain said
oh yes. This is by design. I might need to clarify that in the docs. When you enable server-side pagination there is no way to know where the new items fit into pagination.
Actually, this is no different than previous behavior. Do get the automagic lists, you skip the onServer: true to take control on the client again. Then call find manually or with a watcher.I think it’s time to implement hybrid pagination. A combination of the two techniques. Outer pagination has a high limit. Inner pagination has maybe 10x smaller limit.
We can definitely make it smarter.
Vite throws ERROR: Could not resolve "vue-demi"
when attempting to build a freshly generated Vue project using feathers-pinia.
Initially I ran into this problem when using Vue + feathers-pinia with a custom webpack build process. I thought I might have misconfigured something and attempted again with a fresh project.
These are the steps I took to run into this issue:
I generated a fresh Vue project with npm init vue@latest
as instructed by Vue's quickstart.
After this, I installed pinia and feathers-pinia with npm. According to the current feathers-pinia documentation, these should be all the required steps to get the library up and running.
I added a feathers-pinia store according to the store.pinia.ts
code snippet in the setup instructions and added the newly created pinia store to the app with .use(pinia)
.
At this point, attempting to run npm run dev
results in the error in question.
Hi Marshall,
Well time to upgrade or at least start poking a stick at Feathers Dove with your Feathers Pinia lib. Real cool everything i read in your docs and not significant changes from the feathers-vuex pattern. Looks easy, good docs so far everything very readable and relatable.
So, i might have missed it - was hoping to find a param in the Models something like inheritSchema
that would setup my instance default to whatever schema I create in Feathers for the service. Feathers Vuex saves a ton of time and boilerplate but the one thing - the only thing - I found tedious was keeping my feathers db models in sync with my feathers-vuex models.
Reading about dove schemas the first thing came to mind was yay now I can have a source of truth / write once and everything is magic from there.
I'm sure you've considered this. any feedback or link to look at?
Ok, crash after first npm install:
@feathersjs/feathers @feathersjs/authentication-client feathers-hooks-common @feathersjs/socketio-client socket.io-client
Error: You are trying to use the Socket.io client version 3 or later with Feathers v4 which only supports Socket.io version 2. Please use socket.io-client version 2 instead.
But I will use Feathers v5/Dove anyway..., so I suppose doc is wrong? Why does not everybody has this?
Reported in Feathers, so suppose yo can close here...
An update to Pinia has made it so that the localStorage adapter fails if it runs BEFORE the app.use(pinia)
occurs. A solution is required.
I'm making a pagination wrapper component, the idea is to have it reusable between pages with different data.
Here's the code laurensiusadi/vitesse-feathers-pinia-example@6e00d75
But the pageCount
is not updating when limit, filter, or search is changing. It seems the computed isn't triggered.
I noticed it doesn't work when usePagination
is in children component, or one level deeper than useFind
.
The supplied pagination
and latestQuery
to usePagination
comes from props
.
In this next commit, I put usePagination
up on same level as useFind
and now pageCount
works as expected.
laurensiusadi/vitesse-feathers-pinia-example@fca3941
What could be the problem?
This:
feathers-pinia/src/define-store.ts
Line 34 in 19a13ee
Currently _id
is taken by default. Should _id
be the default?
I just started a new project to move my older one to this new stack: Vue 3, Pinia, Feathers-Pinia. So I'm new to this.
Version:
"feathers-pinia": "^0.29.5",
"pinia": "^2.0.11",
"vue": "^3.2.30",
I found a bug when calling auth.authenticate
.
The call to feathersClient.authenticate
went through and got the response.
The error occurs here https://github.com/marshallswain/feathers-pinia/blob/main/src/define-auth-store.ts#L45
It can't assign this
with the new object response to replace state.
Error message: TypeError: 'set' on proxy: trap returned falsish for property 'user'
Here's how I fixed it in my auth store:
async authenticate(authData) {
try {
const response = await feathersClient.authenticate(authData)
Object.assign(this.$state, { ...response, isAuthenticated: true })
return this.handleResponse(response) || response
} catch (error) {
this.error = error
return this.handleError(error)
}
},
The only fix being this.$state
instead of just this
.
Enabling 'build.minify': true
in vite.config.ts
leads to the following build error:
Uncaught (in promise) TypeError: n.api.User is not a constructor
at setupInstance (index.7faf713a.js:formatted:42194)
at new mo (index.7faf713a.js:formatted:14221)
at new _1e (index.7faf713a.js:formatted:42192)
at index.7faf713a.js:formatted:13772
at Array.forEach (<anonymous>)
at Object.addOrUpdate (index.7faf713a.js:formatted:13767)
at Object.<anonymous> (index.7faf713a.js:formatted:6922)
at Object.handleFindResponse (index.7faf713a.js:formatted:13678)
at Object.<anonymous> (index.7faf713a.js:formatted:6922)
at index.7faf713a.js:formatted:13665
This is due to the setupInstance()
method being minified to the following:
// at setupInstance (index.7faf713a.js:formatted:42194)
class _1e extends Ar {
static setupInstance(t, {store: r, models: n}) {
return t.user && (t.user = new n.api.User(t.user).addToStore()),
t.workspace && (t.workspace = new n.api.Workspace(t.workspace).addToStore()),
t
}
}
The following is the vite.config.ts
build setting that causes this issue. Note that setting build.minify: false
solves this, but obviously does not minify the code (keeps it as is, therefore no minifying error).
// vite.config.ts
const config = defineConfig({
...
build: {
minify: true, // FIXME: this breaks on build due to setupInstance
},
})
Might be an easy fix, not sure what might be leading to this though.
feathers-vuex
has __isTemp
by Object.defineProperty
.
feathers-pinia
does not have it anymore. I use it in feathers-vuex
. The removement is a breaking change and should be stated in the docs.
oooor we add a getter to the BaseModel:
get __isTemp() {
const { idField } = this.constructor as typeof BaseModel
return getId(this, idField) != null
}
Todo:
__isTemp
getter @fratzinger__tempId
, item with __tempId
and [idField]
, item with [idField]
@fratzingerSince associations is defined on the BaseModel, all associations are assigned to the BaseModel.associations
property. This will cause collisions between Models, so it must be fixed.
The current workaround is to define the associations
property on each class:
import { BaseModel } from '../store/store.pinia'
import { associateFind, BaseModelAssociations, type AssociateFindUtils } from 'feathers-pinia'
import { Task } from './task'
export class User extends BaseModel {
_id?: string
name = ''
email = ''
password = ''
static associations: BaseModelAssociations = {} // RIGHT HERE!!!
tasks?: Task[]
_tasks?: AssociateFindUtils<Task>
constructor(data: Partial<User> = {}, options: Record<string, any> = {}) {
super(data, options)
this.init(data)
}
static setupInstance(data: Partial<User>) {
associateFind(data as any, 'tasks', {
Model: Task,
makeParams: () => ({ query: { userId: data._id } }),
handleSetInstance(task: Task) {
task.userId = this._id
},
})
}
}
Getting the following error on latest version w/vite:
[ERROR] Could not resolve "@feathersjs/rest-client"
node_modules/feathers-pinia/dist/feathers-pinia.mjs:5:34:
5 │ import { FetchClient as bt } from "@feathersjs/rest-client";
╵ ~~~~~~~~~~~~~~~~~~~~~~~~~
You can mark the path "@feathersjs/rest-client" as external to exclude it from the bundle, which
will remove this error.
Assuming this came about with the addition of the ohmyrest
from pre.16
Currently trying to get Feathers Pinia installed. The doc's look awesome, so I thought peace of cake:
If you already have Pinia in your app, I suppose we can use the existing file? Maybe add this to the docs?
// src/plugins/pinia.ts
import { createPinia } from 'pinia'
// The setupFeathersPinia utility wraps defineStore
// ... provides a global configuration
// https://feathers-pinia.pages.dev/guide/setup.html#pinia
import { api } from '../feathers'
import { setupFeathersPinia } from 'feathers-pinia'
export const { defineStore, BaseModel } = setupFeathersPinia({
clients: { api },
idField: '_id'
})
// Feathers and other Pinia stores
//
export default createPinia()
I have a very basic query that uses $select:
const { data, find } = store.useFind({
query: computed(() => ({
$select: ['Text', 'Value'],
})),
})
The server side data retrieval is as expected (server query always returns the ID field from feathers, even if not specified in $select query)
The local query however does not return the id
in the data set causing it to set a _tempId
instead.
It's still using the original template.
TLDR: Original defineStore()
called with options.storeId
instead of options.id
and pinia requires options.id
to register store in vue devtools.
Stores created with feathers-pinia not showing-up in vue devtools (at least when using vue2).
From pinia docs:
This name, also referred as id, is necessary and is used by Pinia to connect the store to the devtools.
In this case, the only fix should be to rename the expected options.storeId
to options.id
to at least have a more transparent/direct mapping to the pinia API. This change does not break tests and remains backwards compatible.
So basically change the following code:
// Create and initialize the Pinia store.
const storeOptions: any = makeServiceStore({
- storeId: options.id || `service.${options.servicePath}`,
+ id: options.id || `service.${options.servicePath}`,
idField: options.idField || idField || 'id',
clientAlias,
servicePath,
clients,
Model: options.Model,
state,
getters,
actions,
whitelist,
})
function useStore(pinia?: Pinia): any {
const useStoreDefinition = definePiniaStore(storeOptions)
...
}
Docs say that the old state ids
now is the getter itemIds
. For ids being strings it's perfectly fine. For ids as integers from sequelize/knex/objection that is not true.
https://github.com/marshallswain/feathers-pinia/blob/main/src/service-store/make-getters.ts#L34
transform the integer values to string values.
Same for tempIds
and for cloneIds
.
This PR to @feathersjs/memory
probably directly applies to the findInStore
getter: https://github.com/feathersjs/feathers/pull/2844/files
The only difference will be that we won't do the $select
stage.
Following the example from the docs: https://feathers-pinia.pages.dev/guide/handle-clones.html#using-the-savehandlers
When save_user()
saveHandler is called with no argument, it's supposed to compare all the clone object and then patch.
But when we pass no argument, it doesn't pass the check inside the handler, resulting in the error message:
The first argument to 'saveuser' must be a prop name, an array of prop names, or an object
.
The patch doesn't happen (there's no websocket event sent).
Hi, thanks for maintain this library and make it useful out of box!
Here is my question, I'm writing a custom adapter at the featherJS server side, the data return will become
{
bookmark: data.bookmark || 'nil',
total: data.docs.length,
limit: filters.$limit || 0,
skip: filters.$skip || 0,
data: data.docs,
};
If I using feathers-pinia
, how do I add hooks / replace handleFindResponse method to add bookmark data into the pagination data ? Is this possible?
If this is not the way, can you guide me any method that I can store the bookmark for the next query? Any guidance will be appreciated. Thank you in advance.
ssr
optionisSsr
computed getteruseFind
: Add isSsr
and request
propertiesuseGet
: Add isSsr
and request
propertiesHere is a sample code to reproduce the error :
// init
const postStore = usePosts()
const { item: post, isPending } = useGet({ model: postStore.Model, id: props.id })
// clone
const postClone = post.clone()
postClone.name = 'new name'
postClone.commit() // WORKS
// -------- post changed from server ------------------
// clone again
postClone2 = post.clone()
postClone.name = 'other name'
postClone2.commit() // ERROR 'You cannot call commit on a non-copy'
To me, it seems to be an issue with the existing clone that gets updated with a __isClone: false
when an updated item is received from server.
Here is a quick fix
// clone
const postClone = post.clone()
postClone.name = 'new name'
postClone.commit()
// -------- post changed from server ------------------
// reset existing clone
postStore.clonesById[props.id] = null // HOTFIX
postClone2 = post.clone()
postClone.name = 'other name'
postClone2.commit() // WORKS
Hello,
could you give an example with typescript ?
It’s not clear for me how and where to define the types of a model.
Thx,
Filip
I am using a before hooks in my feathers-client setup where I'm using feathers-hooks-common paramsForServer method to pass whitelisted params to the server. The $client object is being included in the queryId on useFind with pagination:true.
This is making it so I cannot use pagination because my params object that becomes the queryId never matches the local one being passed as an argument to useFind - because the hook is adding the $client object with paramsForServer.
This is causing some less show-stopping, but obnoxious side effects such as the long accessToken being included in the queryId as well.
TLDR: UI is blank (fails to populate with useFind/useGet data) due to reactivity issues when using vue2
with feathers-pinia
.
Basically when I refresh the page, all find()s and get()s are executed successfully (as shown by the devtools network tab), but they won't be rendered in the UI components (despite stores containing the fetched data). Only for example changing page, and then returning to the previous page re-flushes the UI and renders the components with the already fetched data (without refetch).
This I'm sure has to do with the vue2 reactivity caveats. After much thought and time spent trying to fix this, I figured these issues are inevitable for vue2
due to the Observer-bases reactivity (compared to the newer Proxy based reactivity of vue3
which fixes many of these issues).
For users stuck with vue2
for now, I settled with using a simple workaround by wrapping the useFind
and useGet
composables as follows:
import type { BaseModel, ServiceStore, UseFindOptionsStandalone, UseGetOptionsStandalone } from 'feathers-pinia'
import { useFind, useGet } from 'feathers-pinia'
interface M extends BaseModel {}
export function useFindVue2(
store: ServiceStore,
options: UseFindOptionsStandalone<M>,
) {
const { items: _items, haveLoaded, ...rest } = useFind(options)
const items = computed(() => haveLoaded.value
? _items.value
: store.findInStore(options.params.value).data,
)
return { items, haveLoaded, ...rest }
}
export function useGetVue2(
store: ServiceStore,
options: UseGetOptionsStandalone<M>,
) {
const { item: _item, hasLoaded, ...rest } = useGet(options)
// NOTE: check if _item.value is truthy to force computed to re-evaluate ternary
// operator since _item.value is sometimes null just after hasLoaded.value is set
// to true, so watch both to trigger updates when _item finally updates (if it does)
const item = computed(() => hasLoaded.value && _item.value // check both
? _item.value
: store.getFromStore(options.id?.value ?? null, options.params?.value) ?? {},
)
return { item, hasLoaded, ...rest }
}
I've been using the above and it is working properly, however, there might be certain edge cases where this does not solve the reactivity issues, though I don't believe this will break.
@marshallswain mentioned conditionally returning these as useFind
/useGet
for vue2
apps might be something worth adding to feathers-pinia
, so opening up this issue.
useGet throws an error when id is null.
It would be nice to allow id to be null for patterns where id is an optional prop.
import { useUsers } from '../store/users'
interface Props {
id?: string | number
}
const props = defineProps<Props>()
const userStore = useUsers()
const { id } = toRefs(props)
const { data: user } = userStore.useGetOnce(id)
This triggers an error when id is null.
Here is how we fixed it for now :
import { useUsers } from '../store/users'
interface Props {
id?: string | number
}
const props = defineProps<Props>()
const userStore = useUsers()
const user = computed(() => props.id ? userStore.useGetOnce(props.id).data.value : null)
What is eventLocksById
for? I saw the mention in the docs and I saw the implementation in the code. As of now it's not used anywhere.
I stumbled upon this while adding types. See this as a friendly reminder to tackle this topic sometimes in the future. If you like to share, I would love to hear your idea behind this.
When running vue-tsc
I am receiving multiple type errors: Property 'isPending' does not exist on type 'UseFindComputed<>'
This seems to happen when trying to access isPending
from useFind() per the docs:
const { items, isPending } = someStore.useFind()
looking into the type definition and interface, seems like some of the state isn't defined as per the docs:
useFind: (options: UseFindOptions) => UseFindComputed<M>;
export interface UseFindComputed<M> {
items: ComputedRef<M[]>;
servicePath: ComputedRef<string>;
paginationData: ComputedRef<AnyData>;
isSsr: ComputedRef<boolean>;
}
So after a long amount of trial and error as to why nothing was being added to the store, it seem settings idField
to any value other than 'id' seems to get ignored.
example code below:
import { defineStore, BaseModel } from '@/plugins/pinia'
import { api } from '@/plugins/feathers'
export class AuthUser extends BaseModel {}
const servicePath = 'users'
export const useAuthUser = defineStore({
idField: 'userID',
Model: AuthUser,
servicePath,
})
api.service(servicePath).hooks({})
Results from get
/find
return results properly (which can be done by peeking at the results via a hook) but its never added (getFromStore
/ findInStore
always return 0 records)
Currently just working around this by setting id
to whatever the fieldID i want on my backend.
I'm trying this repo https://github.com/marshallswain/vitesse-feathers-pinia-example
And updating it to latest 0.29.5, the search bar becomes unusable.
Inside devtools, queryId
is not updating despite the search.value
changing.
So I downgrade one by one version to find latest working version and that's 0.25.0.
Starting from 0.26.0 the search breaks.
The problem seems to be inside useFind
. But I can't figure out what yet.
I've found an issue when changing query : previous queries are kept in cache inside useFind if a property is not explicitly changed
// Updates the _params with everything from _newParams except `$limit` and `$skip`
export function updateParamsExcludePage(_params: Ref<FindClassParams>, _newParams: MaybeRef<FindClassParams>) {
_newParams = unref(_newParams)
const query = _.omit(_newParams.query, '$limit', '$skip')
const newParams = _.omit(_params.value, 'store', 'query')
Object.assign(_params.value.query, query)
Object.assign(_params.value, newParams)
}
if _params.value.query is { firstname: Emmanuel } and _newParams.value.query is { age: 16 }, resulting query is { firstname: Emmanuel , age: 16 }
I think expected behavior is to have resulting query be { age: 16 }
I encountered this issue when using a computed params.
The setLoaded
function's type definition and function definition differ.
Interface - https://github.com/marshallswain/feathers-pinia/blob/main/src/define-auth-store.ts#L53
Function - https://github.com/marshallswain/feathers-pinia/blob/main/src/define-auth-store.ts#L125
The question is, which one should be the correct one to use? Should setLoaded
always set isLoading
to false? Or is the type definition correct in that the user should be able to toggle isLoading
?
Hello,
(like already on discord): Using feathers-pinia with a whitelisted query argument does only return an empty value.
If you change the whitelist.test.ts in /tests with the following code you see the issue.
Thanks,
Martin
test('enables custom query params for the find getter', async () => {
const { defineStore, BaseModel } = setupFeathersPinia({
clients: { api },
whitelist: ['$regex'],
})
const useMessagesService = defineStore({ servicePath: 'messages' })
const messagesService = useMessagesService(pinia)
await messagesService.create({ text: 'test' })
await messagesService.create({ text: 'yo!' })
const dataREGEX = messagesService.findInStore({ query: { $regex: 'test' } }).data
const dataNOREGEX = messagesService.findInStore({ query: {} }).data
console.log('CHECK DATA regex', dataREGEX) // -> NO DATA RETURNED
console.log('CHECK DATA NOregex', dataNOREGEX) // -> DATA RETURNED
expect(Array.isArray(dataREGEX)).toBeTruthy()
expect(Array.isArray(dataNOREGEX)).toBeTruthy()
})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.