Vuelidate 2 (aka "Next") is supposed to support using Vuelidate outside of Vue components. I'd like to store a global validation state (v$) in a Pinia store instead of in one or more components, using a mapped state property. One of the authors offered an example for doing this with the Composition API, where you pass a reactive state to useVuelidate():
const formState = reactive({})
const rules = {}
const state = {
someProperty: '',
validations: useVuelidate(rules, formState)
}
export {
state
}
I'm still using the Options API and Vue 2, but haven't been able to figure out to "translate" the above for the Options API. I've tried passing mapped state properties, but they get flagged as "undefined". E.g., something like this in my root component (note: you have to install the CompositionAPI module and use setup to use Vuelidate 2 in Vue 2):
setup: () => ({
v$: useVuelidate(this.mappedValidationRulesObject, this.mapped.StoreState)
})
Does anyone know how to do this?
Related
I'm trying to read and change the value of this.$vuetify.dark using composition API in Vue 2 + Vuetify 2. Now that this.myGlobalOption is no longer accessible in composition API, how do I do this? I'm gonna need to do this both from within the template and from outside.
You can make a helper function(composable?) like this:
import { getCurrentInstance } from 'vue';
export const useVuetify = () => {
const vm = getCurrentInstance();
return vm.proxy?.$vuetify || undefined;
};
Then in your component you can get access to vuetify instance via:
const vuetify = useVuetify();
In my app there is a composable called useLocalization. It provides translated strings based on the user's language preferences, and is used throughout the app.
The problem is, that useLocalization can be configured, and accepts the following arguments, but I don't know what the best way of passing these arguments from the root component to the composable:
interface ILocalizationProps {
currentLocale: Ref<string>
fallbackLocale: Ref<string>
locales: Ref<Partial<ILocale>[]>
}
The root component that uses useLocalization accepts the same arguments as props, so the consuming App can configure/override the language used.
const DEFAULT_LANG = 'en'
export const withLocalizationProps = () => ({
currentLocale: { type: String, default: null },
fallbackLocale: { type: String, default: DEFAULT_LANG },
locales: { type: Array as () => Partial<ILocale>[], default: () => [] },
})
How can I initialize my composable with the props passed to the root component? Here's what I tried/issues I found:
useLocalization(props) doesn't work here, as the props are not available deeper in the component tree.
Using provide/inject I can use props in the provide part, and get the correctly configured version with inject, but this prevents my from using useLocalization in the root component, as the injection is not available.
Use a hacky solution such as in vee-validate / injectWithSelf. Even then, The signature of the composable would be useLocalization(props?: IProps), and my root component has to be the first one to call this function with the props.
Use a helper such as createInjectionState, but it's the same problem as 2.
Is there a best way to solve this? Composable that don't depend component state, such as useMouse work great, but (globally) configurable composables cause the afore mentioned issues :(
In that case, it'd be better to use provide/inject than using props.
Here's the reference.
https://vuejs.org/guide/components/provide-inject.html
Trying to implement the new Vue composition API I ran into an issue, my vuex state values are not working and it will return an $store undefined error. When I check if my language state is set it is set so theres data present. I could not find any good information about this but I did find some helper plugin but I dont want to use plugins for every issue so is there a way to do this?
I am using vue 3 with vuex 4
export default {
setup (props, context) {
console.log(context.root.$store.getters['i18n/language'])//not working
// more logic here
}
}
You should use the composable function called useStore to get the store instance :
import {useStore} from 'vuex'
export default {
setup (props, context) {
const store=useStore()
console.log(store.getters['i18n/language'])
}
}
I want to write a simple JS method that I will use both in Store GETTERS.
I created a new file: /plugins/myMethod.js and added it to nuxt.config.js (at plugins part).
Follows the content of the file:
export default ({ app }, inject) => {
// Inject $hello(msg) in Vue, context and store.
inject('exportaData', msg => console.log(`Data is: ${msg}!`))
}
I can use this method in Vue Components and in store ACTIONS, but not in Store GETTERS. I tried this.$myMethod(1) without any success.
Follows the code of store/data.js
export const getters = {
hoje: state => {
console.log('Running getter HOJE')
this.$exportaData('12345')
},
}
and in my Vue component:
<p>DATA: {{ $store.getters['datas/hoje'] }}</p>
Any idea how to access my custom method in the store getter?
Thanks
I recently came across this blog post: Stop using Page Objects and Start using App Actions. It describes an approach where the application exposes its model so that Cypress can access it in order to setup certain states for testing.
Example code from the link:
// app.jsx code
var model = new app.TodoModel('react-todos');
if (window.Cypress) {
window.model = model
}
I'd like to try this approach in my VueJS application but I'm struggling with how to expose "the model".
I'm aware that it's possible to expose the Vuex store as described here: Exposing vuex store to Cypress but I'd need access to the component's data().
So, how could I expose e.g. HelloWorld.data.message for being accessible from Cypress?
Demo application on codesandbox.io
Would it be possible via Options/Data API?
Vue is pretty good at providing it's internals for plugins, etc. Just console.log() to discover where the data sits at runtime.
For example, to read internal Vue data,
either from the app level (main.js)
const Vue = new Vue({...
if (window.Cypress) {
window.Vue = Vue;
}
then in the test
cy.window().then(win => {
const message = win.Vue.$children[0].$children[0].message;
}
or from the component level
mounted() {
if (window.Cypress) {
window.HelloWorld = this;
}
}
then in the test
cy.window().then(win => {
const message = win.HelloWorld.message;
}
But actions in the referenced article implies setting data, and in Vue that means you should use Vue.set() to maintain observability.
Since Vue is exposed on this.$root,
cy.window().then(win => {
const component = win.HelloWorld;
const Vue = component.$root;
Vue.$set(component, 'message', newValue);
}
P.S. The need to use Vue.set() may go away in v3, since they are implementing observability via proxies - you may just be able to assign the value.
Experimental App Action for Vue HelloWorld component.
You could expose a setter within the Vue component in the mounted hook
mounted() {
this.$root.setHelloWorldMessage = this.setMessage;
},
methods: {
setMessage: function (newValue) {
this.message = newValue;
}
}
But now we are looking at a situation where the Cypress test is looking like another component of the app that needs access to state of the HelloWorld.
In this case the Vuex approach you referenced seems the cleaner way to handle things.