Usually we define in a Nuxt.js component something like this:
<script>
export default {
components: {
// components
}
data() {
return {
// key/value data
}
},
methods: {
// method definitions
}
}
</script>
Is there a way to read the components object as we read data() and methods ?
This is because I have several components and I want to loop on them to refactor parts of my code.
You can get Component data by using $options.
Try this.
created() {
console.log(this.$options.components)
}
it returns an object, keys are the component names, values are the contructors.
codepen - https://codesandbox.io/s/yk9km5m0wv
Related
I'm trying to pass an array from async method in the controller to a vue component using props through the blade, but I'm getting undefined when I try to console.log it in the vue component, this is my code:
Controller:
public function index(){
$pool = Pool::create();
$pool[] = async(function () {
return Rfm::all();
})->then(function ($output) {
$this->results=$output;
});
await($pool);
return view ("/dashboard", ["data" => $this->results]);
Blade:
<div id="total_customers">
<total-customers :data="{{$data}}"></total-customers>
</div>
Vue component:
export default {
props: ['data'],
data() {
return {
};
},
mounted() {
console.log(this.data);
}
};
I've searched and read somewhere that using async in the controller would be the problem as the object is still empty when it render to vue component, but I'm not sure about that, am i doing it wrong?
Binding props that way might work with strings and integers but doesn't work with array/object data: https://laravel.com/docs/9.x/blade#blade-and-javascript-frameworks
The following should work:
<total-customers :data="{{ Js::from($data) }}"></total-customers>
Addition:
Might want to look into https://inertiajs.com/ if you bind data to vue components in blade a lot, its a nicer way to render vue components with props
I use vuex in my Vue 2 project.
I have this HTML element and I try to implement two way binding:
<input v-model="message">
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
inside get and set I want to use mappers so the code will look cleaner:
computed: {
message: {
get () {
return ...mapState("obj", ["message"])
},
set (value) {
...mapMutations("obj/updateMessage", value)
}
}
}
But I get errors on two rows:
return ...mapState("obj", ["message"]) - Expression expected.
...mapMutations("obj/updateMessage", value) - Declaration or statement expected.
How can I use mappers inside get and set?
UPDATE:
mapMutations and mapState are imported to the component.
You will need to import them first, as you did
import { mapState, mapActions } from 'vuex'
Import actions tho, and not mutations. Indeed, only actions are async and the flow should always be dispatch a action > commit a mutation > state is updated.
Then, plug them where they belong
computed: {
...mapState('obj', ['message']),
// other computed properties ...
}
methods: {
...mapActions('obj', ['updateMessage']),
// other methods ...
}
Then comes the interesting part
computed: {
message: {
get () {
const cloneDeepMessage = cloneDeep(this.message)
// you could make some destructuring here like >> const { id, title, description } = cloneDeepMessage
return cloneDeepMessage // or if destructured some fields >> return { id, title, description }
},
set (value) {
this.updateMessage(value)
}
}
}
As you can see, I would recommend to also import cloneDeep from 'lodash/cloneDeep' to avoid mutating the state directly thanks to cloneDeep when accessing your state.
This is the kind of warning that the Vuex strict mode will give you, that's why I recommend to enable it in development only.
The official docs are not super explicit on this part (you need to read various parts of them and mix them all together) but it's basically a good way of doing things IMHO.
I made a small library to track the loading of all my api calls.
It basically is an array which contains objects describing one loading process. I import this library into a Single File Component to make calls to it. I then pass the array from the library to a child component and want to render all my objects in the array (the loading statuses). I see the data updating in Vue Devtools on the child component, however it is not updated in the page.
What i tried:
passing the vue instance from the parent component and use $set to update the array
using array.splice to update the array
creating a new object with Object.assign, update the new object and then update the array (splice/$set)
passing a key and updating this key when updating thew object
calling methods in the library in the console
sample updating function:
function _finish(name, descFinished) {
const i = _loaders.findIndex(l => l.name == name);
if(i < 0) throw new NameNotFoundException(name);
let loader = Object.assign({}, _loaders[i]);
if(descFinished != undefined) loader.descFinished = descFinished;
loader.loading = false;
loader.error = false;
loader.endTime = new Date();
vm.$set(_loaders, i, loader);
return _loaders[i];
}
I pass to the child component like this
<loading-status v-if="loadstatus" :statuses="loadstatus.getAllLoaders()"></loading-status>
child component looks this this
<template>
<div v-if="statuses">
<div v-for="status in statuses" :key="status.name">
<div>
{{ status.loading == true ? 'true' : 'false' }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'loading-status',
props: {
statuses: {
type: Array,
required: true
}
}
}
image of the array not being reactive on the page
some more information that may be useful:
the library structure:
function loadmanager(vueinstance) {
//library internals here
// for example my array i want to render:
let _loaders = [];
var publicInterface() {
//public accesable functions here
//i call this to pass the array to my child component
getAllLoaders() {
return _loaders;
}
}
return publicInterface;
}
exports.makeLoadManager = loadmanager;
i import the library in the vue SFC and call it in the mounted hook
import loadhelper from "../../helpers/Loadstatus.js";
...
data() {
return {
loadstatus: null
}
}
...
mounted() {
this.loadstatus = loadhelper.makeLoadManager(this);
}
My question boils down to: How do I reactively render or correctly update an array from a js library in vue.
I would be happy to provide more information if that helps answering my question.
If your library is creating (and managing) an array of "loaders", simplest thing you can do is define empty reactive array in your Vue component's data and in mounted hook assign a reference to your's library array into it. Like this:
import LoadingStatus from "./components/LoadingStatus";
import loadhelper from "./helpers/LoadManager.js";
export default {
name: "App",
components: {
LoadingStatus
},
data() {
return {
statuses: []
};
}
mounted() {
this.statuses = loadhelper.getAllLoaders();
}
};
Vue takes the array provided by your library and makes it reactive. You can use template like this:
<LoadingStatus :statuses="statuses"/>
See the demo
As long as both Vue and your "load manager" are working with the same array (reference), its all good. I'v added setTimeout callback into LoadManager to demonstrate that changes "from the outside of Vue" to the array will make Vue component re-render....
So, I've got a huge Vue file which do more than 1500 lines CSS excluded.
This file is a wrapper for a lot of other components but mostly can't be breaked into smaller components.
The things is I still got some logics like groups of methods which could be extracted. However it has impact on some data or watched properties.
Is it possible to export differents parts of my file and use them as a more "global import".
Something which would be like :
// components/component.logicX.vue/js?
export default {
data() {
return {
....
}
},
methods: {
aFewMethods(){ this.data = "something" },
...
},
watch: {
...
}
}
// component/component.vue
import logicX from '.component.logicX.vue'
export default {
components: {
logicX
},
data() {
data: '' //this data is modified from a logicX component method.
}
}
I am trying to modify a moment.js instance that resides in vue.js computed property like so.
computed: {
currentDate() {
return moment();
}
}
Whenever I try to call it with a method like this one, nothing happens:
methods: {
prevMonth() {
this.currentDate = moment(this.currentDate).subtract(1, 'months');
}
}
I am guessing this is because computed properties only allow to act as getters(and optionally setters). How can I change this behavior though?
I oversimplified the example since I am using the cmoputed property to fetch data from my vuex store. I am no longer able to manipulate it though.
Is there some way I can populate a local currentDate property with the vuex store's value so I can still manipulate it and add months, etc?
I have though about using the mounted property for this but I only mount my component once. Any help is welcome.
If your currentDate property belongs to your Vuex store, you shouldn't be manipulating it inside your components. You should instead: 1) map the getter locally as a computed property and 2) map the mutation locally as a method.
Here's an example of what your date Vuex module might look like:
export default {
state: {
currentDate: moment()
},
mutations: {
subtractMonth (state, date) {
state.currentDate = moment(state.currentDate).subtract(1, 'months');
}
},
getters: {
getCurrentDate: (state) => {
return state.currentDate
}
}
}
And this is how the component would make use of it, without actually doing anything "locally":
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters({
currentDate: 'getCurrentDate'
})
},
methods: {
...mapMutations({
prevMonth: 'subtractMonth'
})
}
}
You'd still be able to bind currentDate inside your component and call prevMonth as before, but now everything is being done via the Vuex single state.
Computed properties are not methods, that you can call. If you want to have such method, move currentDate to methods. The you also can invoke it from mounted.