Vuelidate using Vue 2.6 and Composition API - vue.js

I'm using the Composition API together with Vue 2 (by using #vue/composition-api) combined with the following two libraries (#vuelidate/core": "^2.0.0-alpha.18,
#vuelidate/validators": "^2.0.0-alpha.15).
I am trying to use sameAs to check whether the entered email and repeated email are a match and return an error if not. Although this is not working as smooth as expected.
This is my validation.js file
import { required, email, maxLength, sameAs } from "#vuelidate/validators";
export default {
email: {
required,
email,
maxLength: maxLength(100),
},
repeatEmail: {
required,
email,
maxLength: maxLength(100),
sameAsEmail: sameAs('email')
},
}
and this is my validation-errors.js file. (probably irrelevant for this question though)
export default {
email: [
{
key: "required",
message: "Email is required.",
id: "emailRequired",
},
{
key: "email",
message: "Wrong format on your email.",
id: "emailFormat",
},
{
key: "maxLength",
message: "Email can't be longer than 100 characters.",
id: "emailLength",
},
],
repeatEmail: [
{
key: "required",
message: "Email is required.",
id: "emailRequired",
},
{
key: "email",
message: "Wrong format on your email.",
id: "emailFormat",
},
{
key: "maxLength",
message: "Email can't be longer than 100 characters.",
id: "emailLength",
},
{
key: "sameAsEmail",
message: "Email isn't matching.",
id: "sameAsEmailFormat",
},
],
}
And this is how I try to use it in my component.
import validations from "#/validation";
import validationErrors from "#/validation-errors";
import { useVuelidate } from "#vuelidate/core";
import { reactive, toRefs } from '#vue/composition-api';
export default {
setup() {
const state = reactive({
email: "",
repeatEmail: "",
});
const $v = useVuelidate(validations, state);
return {
...toRefs(state)
}
}
}
So when I enter the same input in both the inputfield for email and repeatEmail correctly it gives me true as an invalid value.

Built-in validators such as sameAs don't have access to the state, so they aren't supposed to be workable when used like sameAs('email').
This way validators are supposed to be defined in setup function in order to access the state:
const emailRef = computed(() => state.email);
const validations = {
...
repeatEmail: {
...
sameAsEmail: sameAs(emailRef)
},
Otherwise this needs to be done with custom validators instead of built-ins that will access the state on component instance with getCurrentInstance.

if you can use the same validation in vuejs 3 with composition syntax because using compute function get update state
code password = computed(() => state.password);
const validations = {
...
confirm-password: {
...
sameAs: sameAs(emailRef)
},

Related

React Native GraphQL use Apollo : Pass array to mutation input

I just use apollo to call api graph QL for some days
I just have a simple question
how can I pass an array like a variable to mutation
in my code, line 7, i want to pass "waypoints" array to mutation.
Please!!!
const GET_QUOTATION = gql`
query($date: String!) {
getQuotation(
input: {
pickupTime: $date
serviceType: CAR
waypoints: [
{
type: PICKUP
address1: "Anson Road, #23-01"
address2: "079906"
location: {lat: "1.307274", lng: "103.883998"}
name: "Jack"
phone: "82187877"
}
]
}
) {
delivery {
provider
price
}
poolWindow {
price
window
}
}
}
`;
const [getQuotation, quotationData] = useLazyQuery(GET_QUOTATION, {
onCompleted() {
console.log('-> success');
},
onError() {
console.log('-> error');
},
variables: {
date: '2021-03-12T18:54:00Z',
},
});

How to validate password with Vuelidate?

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: '',
}
},
...

How to test an Error of a Method in a Vue.js Component using jest

How can I test the method createUser() of my Vue component? I want to test if the createUser() method throws an Error if the firstname < 2 for example. How is this possible?
I'm not really familiar with testing VUE components. It's my first time, so I have no idea how to get access the VUE component and how to submit for a example a username to the component
<script>
import {ApiService} from '../ApiService.js';
import {User} from '../User.js';
//const API_URL = 'http://localhost:8080';
const apiService = new ApiService();
export default {
name: "CreateUser",
data() {
return {
input: {
username: "",
firstname: "",
lastname: "",
title: "",
password: "",
groupId: "",
groups: [],
},
}
},
/.../
methods: {
getAllGroups() {
apiService.getAllGroups().then((data) => {
this.input.groups = data;
});
},
createUser() {
if (this.input.firstname == null || this.input.firstname.length < 2 || this.input.firstname > 50) {
throw ("Firstname to short/long/empty");
} else {
let user = new User(this.input.username, this.input.lastname, this.input.title, this.input.firstname, this.input.password, this.input.groupId)
apiService.createUser(user).then(() => {
location.reload()
});
}
},
I tried the following, but something doesn't not work
import { shallowMount } from '#vue/test-utils';
import UserModal from "../src/views/UserModal";
describe('UsarModal', () => {
it('should throw error when first name is too short', () => {
const myItems = [
{
username: "Heinz",
firstname: "H",
lasname: "Müller"}
]
const wrapper = shallowMount(UserModal, {
input: {
myItems
}
})
expect(wrapper.vm.createUser()).toThrow("Firstname to short/long/empty")
})
})
since in the code, it is throwing an error, so we will need to add a catch block in our test case to test this scenario. PFB example for your case:
try {
wrapper.vm.createUser();
} catch (error) {
expect(error).toBe('Firstname to short/long/empty');
}
let me know if you face any issue.
What you did in your example is very close. You just need to wrap the function call that is going to throw the exception in a lambda, e.g.
expect(() => wrapper.vm.createUser()).toThrow("Firstname to short/long/empty")
As described in the docs: https://jestjs.io/docs/expect#tothrowerror
It's probably doing something like internally wrapping the lambda in a try/catch, but I think using this method is a bit nicer than wrapping in a try/catch in your own test.
Thanks for the tip! But still something is not working right. It seems for me, that it does not accept my mock-data for input. I want to test if a got an error when I use a username which is too short. But even when I put a username that's long enough, the try catch block catches my error as you can see in the attached picture.
import {shallowMount} from '#vue/test-utils'
import UserModal from "./UserModal";
describe('ParentComponent', () => {
test("displays 'Emitted!' when custom event is emitted", () => {
const wrapper = shallowMount(UserModal, {
input: {
username: "asfsf",
firstname: "thomas",
lastname: "bird",
title: "Dr.",
password: "asdasda",
groupId: "2",
groups: [],}
});
try {
wrapper.vm.createUser();
} catch (error) {
expect(error).toBe("username missing");
}
})
});
Error Log

Options not displaying in react-select dropdown, but can see the array looks good in console.log

I have an async api call (using axios) to fetch all my 'options' from the server. I can see the response coming in even modified the API to return the options with the "label" and "value" in the array.
I have tried using the component which also did not work. I am not even trying to set the value yet, just want the list of options to display.
Have stripped the component to its bare minimum
Here is my complete component:
import Api from "../API/Api";
import Select from "react-select/async";
// so I have access to the AccessToken for any requests I need to send..
// Might want to 'catch' a 401 error and retry the submission, but lets first
// see if the form works properly
import { useCookies } from "react-cookie";
const GetSuppliers = () => {
const [cookies] = useCookies("accessToken");
const [input, setInput] = useState("");
const [suppliers, setSuppliers] = useState([]);
const askApi = async searchInput => {
await Api.get("supplier", {
headers: {
Authorization: "Bearer ".concat(cookies.accessToken)
},
params: {
searchString: ""
}
}).then(response => {
setSuppliers(response.data);
return response.data;
});
};
useEffect(() => {
askApi();
}, []);
if (suppliers.length == 0) {
return <div>Loading ... </div>;
} else {
console.log(suppliers);
return (
<div className="DropdownField">
<Select cacheOptions options={suppliers} defaultOptions />
</div>
);
}
};
export default GetSuppliers;
The console.log(suppliers); returns :
1: {value: 5609, label: "AAE02-01-AP", name: "Supplier name 2"}
2: {value: 6197, label: "AAG01-01-AP", name: "Supplier name 3"}
3: {value: 6402, label: "AAL01-01-AP", name: "Supplier name 4"}
4: {value: 6486, label: "AAN01-02-AP", name: "Supplier name 5"}
So I am expecting it to work. Where am I missing the plot?
This was a rookie mistake (at least I am a rookie, so nobody can blame me).
During my original struggles I attempted the Async select, so the import was still reference the "Async" select.
Changed
import Select from "react-select/async";
to:
import Select from "react-select";
And voila, options showing!

Vuejs (vuex) error unknown local mutation type

I have an error when I type in my input at the register page. The error said:
unknown local mutation type: setRegisterEmail, global type: authentication/setRegisterEmail
I've tried many way still can't fix it.
Here is my register.vue:
Import { mapState, mapMutations, mapActions } from 'vuex';
export default {
computed: {
...mapState('authentication', [
'registerEmail',
'registerPassword',
'registerError',
]),
},
methods: {
...mapMutations('authentication', [
'setRegisterEmail',
'setRegisterPassword',
]),
...mapActions('authentication', [
'register',
]),
},
};
Here is my autentication.js:
export default {
namespaced: true,
state: {
registerEmail: null,
registerPassword: null,
registerError: null,
token: null,
},
mutation: {
setToken(state, token) {
state.token = token;
},
setRegisterEmail(state, email) {
state.registerEmail = email;
},
setRegisterPassword(state, password) {
state.registerPassword = password;
},
},
};
You have a typo into your authentication.js. Replace mutation by mutations
I am using Vuex Advance Application Structure and faced this problem. This issue was resolved by defining complete directory of mutations after modules folder e.g cart/setCartData
this.$store.commit('cart/setCartData', payload);
Image from VuexSite is attached for better explaination of Advance Application Structure