Is possible to catch what object was mutated?
const users = reactive([
{ name: 'User1', phone: '12345'},
{ name: 'User2', phone: '67890'},
{ name: 'User3', phone: '34567'},
]);
watch(users, (val) => {
console.log(val);
}, {deep: true});
Now i getting a full array of users, but want to get something like:
users[0].name was changed
or
users[1].phone was changed
I have tried to use this syntax
watch(users.name, (val) => {
console.log(val);
}, {deep: true});
but got an errors...
Related
Getters in Vuex dont work. Why I dont have table of objects? I get function in string...
export const state = () => {
return{
users: [
{name: 'Test', surname: 'Testowski'},
{name: 'Michał', surname: 'Topór'},
{name: 'Jan', surname: 'Janowski'},
{name: 'Ewa', surname: 'Jakas'},
{name: 'Tessst2', surname: 'Testowska'}
]
}
}
export const getters = {
getUsers: (state) => state.users
}
export default {
name: "StatusPanel",
computed:{
users(){
const tmp = this.$store.users.getters.getUsers;
console.info(tmp);
return tmp
}
}
}
What is wrong, why console.log is:
"ƒ getUsers(state) {
return state.users; }"
I tried add "()" in the end to maybe execute this function but then:
"Cannot read property 'users' of undefined""
What I do wrong?
Per the documentation here: https://vuex.vuejs.org/guide/getters.html
Your store should be defined as:
const store = new Vuex.Store({
state: {
users: [...]
},
getters: {
users: state => state.users
}
})
Then you should be able to access this.$store.getters.getUsers. I believe the problem is that you're not using new Vuex.Store({...}).
i want to seed data onConnect, but i have access denied, using this query :
{
keystone: keystone {
adminMeta {
lists {
key
description
label
singular
plural
path
fields {
path
}
}
}
}
i have this error even iam using sudo, context.sudo().graphql.raw :
[
Error: Access denied
at /Users/sidalitemkit/work/web/yet/wirxe/wirxe-app/node_modules/#keystone-next/admin-ui/system/dist/admin-ui.cjs.dev.js:552:19
at processTicksAndRejections (node:internal/process/task_queues:94:5)
at async Promise.all (index 0)
at async Promise.all (index 0) {
locations: [ [Object] ],
path: [ 'keystone', 'adminMeta' ]
}
]
here my config :
export default auth.withAuth(
config({
db: {
adapter: 'prisma_postgresql',
url:
'postgres://admin:aj093bf7l6jdx5hm#wirxe-app-database-do-user-9126376-0.b.db.ondigitalocean.com:25061/wirxepool?schema=public&pgbouncer=true&sslmode=require',
onConnect: initialiseData,
},
ui: {
isAccessAllowed: (context) => !!context.session?.data,
},
lists,
session: withItemData(
statelessSessions({
maxAge: sessionMaxAge,
secret: sessionSecret,
}),
{ User: 'email' },
),
}),
);
i figured out that when i do :
isAccessAllowed: (context) => true
it's working
any advice here
context.sudo() disabled access control. there could be some issue with your query. isAccessAllowed: (context) => true is related to admin-ui and not to the backend implementation of graphql. This could be a bug please open a bug in the repo. They whould be able to fix it quickly.
I do not see sample initialiseData to try myself. Also the graphql is designed as such if you try to access some non existing item then it may give you access denied error even though there is not access control (all access set to true).
There is also another api which is easier in creating the initial items. You should use new list api, available as context.sudo().lists.<ListName>.createOne or createMany like this
const user = await context.sudo().lists.User.createOne({
data: {
name: 'Alice',
posts: { create: [{ title: 'My first post' }] },
},
query: 'id name posts { id title }',
});
or
const users = await context.lists.User.createOne({
data: [
{
data: {
name: 'Alice',
posts: [{ create: { title: 'Alices first post' } }],
},
},
{
data: {
name: 'Bob',
posts: [{ create: { title: 'Bobs first post' } }],
},
},
],
query: 'id name posts { id title }',
});
for more details see List Items API and Database Items API in their preview documentation.
You can find a working example in keystonejs repository (blog)
You have to await and pass context to the initialiseData() method. The onConnect hook already provides this context for you
also, you can look for an argument like '--seed-data' so it's only run once
and run the code as:
keystone --seed-data
export default auth.withAuth(
config({
db: {
adapter: 'prisma_postgresql',
url:
'postgres://admin:aj093bf7l6jdx5hm#wirxe-app-database-do-user-9126376-0.b.db.ondigitalocean.com:25061/wirxepool?schema=public&pgbouncer=true&sslmode=require',
async onConnect(context) {
if (process.argv.includes('--seed-data')) {
await initialiseData(context);
}
},
},
ui: {
isAccessAllowed: (context) => !!context.session?.data,
},
lists,
session: withItemData(
statelessSessions({
maxAge: sessionMaxAge,
secret: sessionSecret,
}),
{ User: 'email' },
),
}),
);
Greeting,
i need to validate the password form
In addition to the field required
Must have at least one uppercase letter, lowercase letter at least, number at least one and at least one of the following characters "#?! # $% ^ & * -"
I am using this package https://vuelidate.js.org/
EDIT
OR REGEX FOR THIS
Just add a custom function with the rules you want to the Vuelidate validations.
validations: {
password: {
required,
// minLength: minLength(8) // I assume you'd want something like this too
valid: function(value) {
const containsUppercase = /[A-Z]/.test(value)
const containsLowercase = /[a-z]/.test(value)
const containsNumber = /[0-9]/.test(value)
const containsSpecial = /[#?!#$%^&*-]/.test(value)
return containsUppercase && containsLowercase && containsNumber && containsSpecial
}
}
}
It'd probably be helpful to break each requirement up into a separate function, so you can set a different error message for each (which would be helpful to guide the user as to what to they need to fix).
validations: {
password: {
required,
// minLength: minLength(8) // I assume you'd want something like this too
containsUppercase: function(value) {
return /[A-Z]/.test(value)
},
containsLowercase: function(value) {
return /[a-z]/.test(value)
},
containsNumber: function(value) {
return /[0-9]/.test(value)
},
containsSpecial: function(value) {
return /[#?!#$%^&*-]/.test(value)
}
}
}
To extend on Keegan's answer, you can include the helpers.withMessage method to include a custom error message on your password validation. I merged the regex to make it easier and simpler for handling the error message.
import useVuelidate from '#vuelidate/core'
import { helpers, required, email, minLength, maxLength, sameAs } from '#vuelidate/validators'
export default {
setup () {
return {
v$: useVuelidate({
$lazy: true,
$autoDirty: true,
})
}
},
validations () {
return {
firstName: { required, minValue: minLength(4), maxValue: maxLength(40), },
lastName: { required, minValue: minLength(4), maxValue: maxLength(40), },
email: { required, email, },
password: {
required,
minLength: minLength(6),
containsPasswordRequirement: helpers.withMessage(
() => `The password requires an uppercase, lowercase, number and special character`,
(value) => /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])/.test(value)
),
},
confirmPassword: { required, sameAsPassword: sameAs(this.password) }
}
},
data() {
return {
firstName: '',
lastName: '',
email: '',
password: '',
confirmPassword: '',
}
},
...
So I'm building the chat functionality part for my app. I'm using sendbird sdk along with gifted chat for the UI.
My messages won't show up and I keep on getting the following warning.
'GiftedChat: _id is missing for message''
Now I've triple checked my sendbird is configured correctly, I'm logged in correctly and have also created the appropriate channel. These are both working.
Looking at the log of my messages they are posting as shown.
{
messageType: 'user',
messageId: 2122453749,
etc ...
}
But they are posting as messageId , Do I have to change the structure of this? If so how do I go about this as sendbird pre configures it already. Or can I change this in gifted-chat ?
Please take a look at the snippets of my code below.
getChannelMetaData(channel) {
if (channel) {
const self = this;
const messagesQuery = channel.createPreviousMessageListQuery();
messagesQuery.load(50, true, (messages, error) => {
if (error) {
console.error(error);
}
this.setState({
messages,
});
});
}
}
onSend(messages = []) {
const handle = this;
const sb = SendBird.getInstance();
const { channel } = this.props.navigation.state.params;
this.setState(previousState => {
channel.sendUserMessage(messages[0].text, (response, error) => {
if (!error) {
handle.getChannelMetaData(channel);
}
});
console.log(this.state.messages);
return { messages: GiftedChat.append(previousState.messages, messages) };
});
}
<GiftedChat
messages={this.state.messages}
renderBubble={bubble}
loadEarlier
renderLoadEarlier={loadEarlier}
isAnimated
keyboardShouldPersistTaps="never"
onSend={messages => this.onSend(messages)}
user={{
_id: userID,
}}
showUserAvatar
/>
You should use this format:
{
_id: 1,
text: 'message',
createdAt: new Date(),
user: {
_id: 2,
name: 'nickname',
avatar: 'YourimageURL',
},
},
If you don't follow the format specified by them, it throws this warning. So what we did for that ...we just customized our JSON object from the array of chat messages like below
let giftedChatMessages = chatMessages.map((chatMessage) => {
let gcm = {
_id: chatMessage.id,
text: chatMessage.get("text"),
createdAt: chatMessage.get("createdAt"),
user: {
_id: chatMessage.get("user").id,
name: chatMessage.get("user").get("name"),
avatar: chatMessage.get("user").get("avatarUrl")
}
};
return gcm;
});
messages={this.state.messages}
Here, {this.state.messages} should have the following structure
id:
text:
createdAt:
user: {
_id:
name:
avatar:
}
Option 1: You get all the corresponding field values as is from sendbird and update an intermediary array with the expected property names and then update this.state.messages
Option 2: Onsend function should insert the values for the following column headers in sendbird
id:
text:
createdAt:
user: {
_id:
name:
avatar:
}
So that, when you retrieve the message from sendbird, you get exactly the same fields as is expceted by giftedchat format.
I'm learning react redux, I'm currently implementing some reducers using TDD. I'm working with a sample application, it's just a notepad for adding/removing/opening/closing notes.
I'm making some tests for adding a new note, but the second "it" is failing, the error that throws me is the following:
1) AddNote reducer should return two notes:
AssertionError: expected { Object (id-123, id-456) } to equal { Object (byId, ids, ...) }
describe('AddNote reducer' , () => {
it('should return a new note', () => {
const state = getMockState.withNoNotes();
const actualNextState = reducers.byId(
state.byId, actions.addNote(
'Hello World', 'id-123', 1));
const expectedNextState = {
'id-123': {
id: 'id-123',
content: 'Hello World',
timestamp: 1
}
};
expect(actualNextState).to.deep.equal(expectedNextState);
});
it('should return two notes', () => {
const state = getMockState.withOneNote();
const actualNextState = reducers.byId(state.byId, actions.addNote('Bye bye world!', 'id-456', 2));
const expectedNextState = {
...state,
'id-456': {
id: 'id-456',
content: 'Bye bye world!',
timestamp: 2
}
};
expect(actualNextState).to.deep.equal(expectedNextState);
});
});
I'm using also a helper, here it is
export const getMockState = {
withNoNotes: () => ({
byId: {},
ids: [],
openNoteId: null,
}),
withOneNote: () => ({
byId: {
'id-123': {
id: 'id-123',
content: 'Hello world',
timestamp: 1,
},
},
ids: ['id-123'],
openNoteId: 'id-123',
}),
};
The idea is if I add a note in the first case it would return a new state with just that note, but in the second case, it should return a state with the previous note, plus the new one.
I'm using Chai expect library with mocha.
If it helps here's the output of the state (it's a console log), which I think is ok,
{ 'id-123': { id: 'id-123', content: 'Hello World', timestamp: 1 } }
√ should return a new note
{ 'id-123': { id: 'id-123', content: 'Hello world', timestamp: 1 },
'id-456': { id: 'id-456', content: 'Bye bye world!', timestamp: 2 } }
1) should return two notes
Here's is my reducer
import { merge } from 'ramda';
export const byId = (state={}, {type, payload}) => {
switch(type) {
case 'app/addNote':
console.log(merge(state, { [payload.id]: payload})) ;
return merge(state, { [payload.id]: payload});
default:
return state;
}
};
Any ideas? thanks in advance!
Your expectedState is not correct. You are comparing the "next state" of byId slice, but your expectedNextState extends the whole state. It should actually be:
const expectedNextState = {
...state.byId,
'id-456': {
id: 'id-456',
content: 'Bye bye world!',
timestamp: 2
}
};