Vuex state item is always empty - javascript

I'm using Vuex and realy like it. However I've got a weird problem. I've got a module called filters it looks like this:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default {
namespaced: true,
state: {
filters: [{ key: 'page', value: 1 }],
criterias: [
{
name: "🔥 LIFE",
filter: "LIFE",
active: false
},
{
name: "🚻 FACILITIES",
filter: "FACILITIES",
active: false
},
{
name: "📡 WIFI",
filter: "WIFI",
active: false
},
{
name: "😀 FUN FOR KIDS",
filter: "FUN_FOR_KIDS",
active: false
},
{
name: "😀 FUN FOR ADULTS",
filter: "FUN_FOR_ADULTS",
active: false
},
{
name: "💰 COSTS",
filter: "COSTS",
active: false
},
{
name: "🌊 WATER QUALITY",
filter: "WATER_QUALITY",
active: false
},
{
name: "⛵ SAIL BOAT FRIENDLY",
filter: "SAIL_BOAT_FRIENDLY",
active: false
},
{
name: "🛥️ MOTOR BOAT FRIENDLY",
filter: "MOTOR_BOAT_FRIENDLY",
active: false
},
{
name: "🇪🇸 SPANISH SPEAKING",
filter: "SPANISH_SPEAKING",
active: false
},
{
name: "🍲 RESTAURANTS",
filter: "RESTAURANTS",
active: false
},
{
name: "✌️ PEACE",
filter: "PEACE",
active: false
},
{
name: "🅿️ PARKING SPOTS (CAR)",
filter: "PARKING_SPOTS",
active: false
},
{
name: "🏴󠁧󠁢󠁥󠁮󠁧󠁿 ENGLISH SPEAKING",
filter: "ENGLISH_SPEAKING",
active: false
}
]
}
}
(I import this module in my main.js file)
When I try to get the criterias from this module in a custom component:
<script>
export default {
data() {
return {
criterias: []
}
},
computed: {
...mapState({
criteriasVuex: state => state.filters.criterias
})
},
created() {
this.criterias = this.criteriasVuex.slice(0, 7);
}
}
</script>
Criterias is always empty! When I look in vue-devtools no criterias are visible in my component or vuex state!!! How is this possible?
The weird thing is that filters on the state is not empty! I can see that in vue-devtools.

It does work with the code you have shown (see sample CodeSandbox), so likely the problem is in the way the filter module is added to the store.
Here is a store that works
import Vue from "vue";
import Vuex from "vuex";
import filters from "./filtersModule";
Vue.use(Vuex);
const modules = {
filters
};
export default new Vuex.Store({
modules
});

<script>
import {mapState} from 'vuex'
export default {
computed: {
...mapState({
criterias
})
},
}
</script>
Should bring you further. 'state.filters.criterias' won't work because:
You're in the MapState function, thus the "state" namespace needs to be omitted.
criterias is not a subobject of filters.

Related

Setting preselected value as object via v-model in child component made of DevExtreme DxSelectBox not working in Vue3 composition API

I have made a child component - Dropdown based on Devextreme DxSelectBox.
I set in parent component v-model as attribute and forward to it an variable as ref, which is set to initial selected valueconst item = ref({ value: "option 1" }), but the DxSelectBox is empty when loading the project.
Child component - Dropdown is emitting the right selected option, but unfortunately the initial selected value is not set.
Code for Parent App.vue component is:
<template>
<template>
<Dropdown :items="items" v-model:option="item" />
<div class="emitting">Emitting: {{ item }}</div>
</template>
<script>
import { ref } from "vue";
import Dropdown from "./components/Dropdown.vue";
export default {
name: "App",
components: {
Dropdown: Dropdown,
},
setup(props, context) {
const emitValue = (e) => {
context.emit("update:option", e.value);
};
const items = ref([
{
value: "option 1",
},
{
value: "option 2",
},
{
value: "option 3",
},
]);
const item = ref({
value: "option 1",
});
return {
items,
item,
emitValue,
};
},
};
</script>
<style>
#import "https://cdn3.devexpress.com/jslib/18.2.8/css/dx.light.css";
#import "https://cdn3.devexpress.com/jslib/18.2.8/css/dx.common.css";
</style>
and code for Dropdown.vue component is:
<template>
<template>
<div class="dx-field">
<DxSelectBox
:data-source="items"
:value="option"
#valueChanged="emitValue"
:width="width"
:height="height"
:drop-down-options="{ width: width }"
display-expr="value"
:disabled="disabled"
:read-only="readOnly"
:hint="hint"
:placeholder="placeholder"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, watch, ref } from "vue";
import DxSelectBox from "devextreme-vue/select-box";
export default defineComponent({
name: "DpmDropdown",
components: {
DxSelectBox,
},
emits: ["update:option"],
props: {
label: { type: String, default: "" },
showLabel: { type: Boolean, default: true },
headingTooltip: { type: String, default: "" },
items: { type: Array, default: () => [] },
option: { type: Object, default: () => ({}) },
width: { type: [Number, String], default: "100%" },
height: { type: String, default: "40px" },
icon: { type: String, default: "" },
hint: { type: String, default: "" },
disabled: { type: Boolean, default: false },
readOnly: { type: Boolean, default: false },
placeholder: { type: String, default: "" },
},
setup(props, context) {
const emitValue = (e: any) => {
context.emit("update:option", e.value);
};
return {
emitValue,
};
},
});
</script>
The link for sandbox project is here.

quill Overwriting formats/div with?

Why am I getting a warning in the console, my codes are as follows?
Quill.tagName = 'DIV'; I used the line nothing changed.
import '#vueup/vue-quill/dist/vue-quill.core.css';
import '#vueup/vue-quill/dist/vue-quill.snow.css';
import '#vueup/vue-quill/dist/vue-quill.bubble.css';
import { QuillEditor, Quill } from '#vueup/vue-quill'
import htmlEditButton from "quill-html-edit-button";
Quill.register("modules/htmlEditButton", htmlEditButton);
export default {
name: "Edit",
components: { ValidateError, QuillEditor },
props: {
dat: Object,
},
data(){
return {
submitted: false,
editorOption: {
modules: {
clipboard: {
matchVisual: false
},
toolbar: [
["bold", "italic", "underline", "strike"],
[{ header: [!1, 1, 2, 3, 4, 5, 6] }, "blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }, { indent: "-1" }, { indent: "+1" }],
[{ align: [] }],
["link"],
["clean"],
],
htmlEditButton: {},
},
placeholder: "Write...",
},
}
},
}
I am getting warnings like below in console. I've been struggling for 2 days and I couldn't get rid of this error message.
quill Overwriting formats/div with
function e()​
allowedChildren: Array(29) [ e(), e(), e()
, … ]
blotName: "div"
length: 0
name: "e"
prototype: Object { constructor: e()
}
tagName: "DIV"
<prototype>: function e(t)

How to make quill text editor readonly when you click the button in vue

I have a quill text editor and as a default I want to set it as readonly and when the button is clicked, this should be true/false. So here is my component:
<template>
<div ref="editor"></div>
</template>
<script>
import Quill from 'quill';
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css';
import 'quill/dist/quill.snow.css';
export default {
props: {
disabled: { type: Boolean, default: false },
value: {
type: String,
default: ''
}
},
data() {
return {
editor: null,
};
},
mounted() {
this.editor = new Quill(this.$refs.editor, {
modules: {
toolbar: [
[{ header: [1, 2, 3, 4, false] }],
['bold', 'italic', 'underline']
]
},
readOnly: this.disabled,
theme: 'snow',
formats: ['bold', 'underline', 'header', 'italic']
});
this.editor.root.innerHTML = this.value;
this.editor.on('text-change', () => this.update());
},
methods: {
update() {
this.$emit('input', this.editor.getText() ? this.editor.root.innerHTML : '');
}
}
}
</script>
I am sending this value as a prop to my component which is 'disabled' and when I click the button true becomes false and when I click again visa versa. So there is nothing wrong with that. Also when I do readOnly: true, or readOnly: false it works but with this way, readOnly: this.disabled it doesnt work.
I am using vue but I created a component based on quilljs directly.
The documentation I use is this one: https://quilljs.com/docs/configuration/#readonly

How do I create reusuable external functions in Vue?

As my project is growing, I've noticed a lot of repetitions. I'm starting with the navigations buttons, as they can appear in multiple places (side menu, navbar).
I'd like to centralize them and let the component import them as needed. So I've tried creating this file to hold all my menu items:
// menuItems.js
export default {
data() {
return {
items: [
{ title: 'Profile', icon: 'mdi-account-circle', reqAuth: true, hideOnAuth: false},
{ title: 'Dashboard', icon: 'mdi-view-dashboard', reqAuth: true, hideOnAuth: false },
{ title: 'Settings', icon: 'mdi-cog', reqAuth: true, hideOnAuth: false },
{ title: 'Signup', icon: 'mdi-account-circle-outline', reqAuth: false, hideOnAuth: true},
{ title: 'Login', icon: 'mdi-login', reqAuth: false, hideOnAuth: true },
{ title: 'Logout', icon: 'mdi-logout', reqAuth: true, hideOnAuth: false},
]
}
},
methods: {
menuItems: function(authenticated) {
if (!authenticated) {
// Gets items that do and don't require Auth, except for ones that should hide on Auth
return this.items.filter(o => o.reqAuth || !o.reqAuth && !o.hideOnAuth)
}
// Gets items that don't require Auth
return this.items.filter(o => !o.reqAuth)
}
}
}
Buttons can require authentication to be visible, and they can also be hidden once authenticated (eg. The login button).
Now lets assume I have a vue component for my nav bar, how do I import in the method that returns the filtered items?
// NavBarComponent.vue
<template>
<div>
<v-btn v-for="(item, i) in menuItems(authenticated)">
{{ item.title }}
</v-btn>
</div>
</template>
<script>
export default {
name: "NavBarComponent",
data() {
return {
authenticated: true,
}
},
methods: {
}
}
</script>
In this case, how do i make menuItems in the component reference the external file that will do the work of filtering?
You can create a mixin file and put your methods in that mixin and apply the mixin your component.
https://v2.vuejs.org/v2/guide/mixins.html
Your mixin would look like this:
// /mixins/menuItemsMixin.js
const menuItemsMixin= {
data() {
return {
items: [
{ title: 'Profile', icon: 'mdi-account-circle', reqAuth: true, hideOnAuth: false},
{ title: 'Dashboard', icon: 'mdi-view-dashboard', reqAuth: true, hideOnAuth: false },
{ title: 'Settings', icon: 'mdi-cog', reqAuth: true, hideOnAuth: false },
{ title: 'Signup', icon: 'mdi-account-circle-outline', reqAuth: false, hideOnAuth: true},
{ title: 'Login', icon: 'mdi-login', reqAuth: false, hideOnAuth: true },
{ title: 'Logout', icon: 'mdi-logout', reqAuth: true, hideOnAuth: false},
]
}
},
methods: {
menuItems: function(authenticated) {
if (!authenticated) {
// Gets items that do and don't require Auth, except for ones that should hide on Auth
return this.items.filter(o => o.reqAuth || !o.reqAuth && !o.hideOnAuth)
}
// Gets items that don't require Auth
return this.items.filter(o => !o.reqAuth)
}
}
};
export default menuItemsMixin
And in your component:
// NavBarComponent.vue
<script>
import menuItemsMixin from './mixins/menuItemsMixin.js'
export default {
mixins:[menuItemsMixin]
}
</script>
You can import this mixin in multiple components and you can also add more mixins in this component where the unique methods will be added.
I ended up creating a javascript file:
// views.js
export const views = [
{title: 'Dashboard'},
{title: 'Profile'},
{title: 'Login/Signup'},
]
then in my navbar component I imported it like so:
import {mapGetters} from "vuex";
import {views} from "../../common/views";
export default {
data: () => ({
items: views
}),
computed: {
...mapGetters(['isAuthenticated',])
menuItems: function (){
if (this.isAuthenticated) {
// do this
} else {
// do this
}
},
}
}
Then I did the same for the filtering function, but I could also just re-code it as needed if required in each component. I determined authentication state using Vuex's store, and retrieve it with mapgetters.
<componentA v-if='isAuthenticated'>
<navItem v-for='item in menuItems'>
</componentA>

Test react component can't get clientWidth

I'm trying to develop unit test for my react component with jest and enzyme. So basically my component have resize listener, when resize occured my component will update component state. But i just couldn't get the clientWidth for my react component. Below is some code of my component.
import React, { Component } from "react";
import moment from "moment";
// import PropTypes from "prop-types";
import Table from "./Table";
import Grid from "./Grid";
import ActionBlock from "../ActionBlock";
import ConfirmDialog from './ConfirmDialog';
import ReactTooltip from 'react-tooltip'
import { debounce } from '../../utils';
import styles from './styles.scss';
export default class Pagination extends Component {
constructor(props) {
super(props);
this.state = {
index: props.index,
type: props.type,
config: props.config,
data: props.data,
currentPage: 1,
dataPerPage: 20,
enableActionBlock: props.enableActionBlock,
confirmDialogIndex: null,
confirmDialogActionName: null,
confirmDialogData: null,
width: 0
};
this.handleWindowResize = debounce(this.handleWindowResize.bind(this), 100); //delay trigger resize event
}
componentDidMount() {
this.setState({ width: this.refs.pagination_wrapper.clientWidth })
window.addEventListener('resize', this.handleWindowResize)
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleWindowResize);
}
handleWindowResize = () => {
this.setState({ width: this.refs.pagination_wrapper.clientWidth })
}
render() {
return (
<div ref="pagination_wrapper" className={styles.pagination_wrapper}>
<ReactTooltip />
{this.renderViewType()}
{this.renderConfirmDialog()}
</div>
)
}
}
Pagination.defaultProps = {
enableActionBlock: true,
dataPerPage: 20
};
And below is my test code.
import React from 'react'
import { shallow, mount, render } from 'enzyme';
import Pagination from '../index';
let img = 'https://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg';
let imageStream = 'http://192.168.100.125:8080/';
let imgQuoteError = `http://192.168.100.71/target-data/fr/target-person-images/1111112222233333#Rizkifika-Asanuli'nam/qTD8vYa.jpeg`;
describe('Testing Pagination', () => {
let action = (actionName, indexData) => {
console.log('action APP', actionName, indexData);
}
let dataListProps = {
index: 'id',
type: 'grid',
config: [
{ text: 'Image', type: 'image', textPath: 'image', textColor: 'red', valuePath: 'image' },
{ text: 'Fullname', type: 'string', textPath: 'fullname', valuePath: 'fullname' },
{ text: 'Role', type: 'string', textPath: 'role', valuePath: 'role' },
{ text: 'Datetime', type: 'date', textPath: 'datetime', valuePath: 'datetime' },
{ text: 'Json', type: 'json', textPath: 'json', valuePath: 'json' },
],
data: [
{ id: 305, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 1 }, cam_detail: { id: 2, name: 'kamera huawei' }, vas_detail: { id: 3, name: 'VAS 3' }, image: img },
{ id: 306, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 2, name: '' }, cam_detail: { id: 3, name: 'kamera avigilon' }, vas_detail: { id: 4, name: 'VAS 4' }, image: imageStream },
{ id: 306, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 2, name: null }, cam_detail: { id: 3, name: 'kamera avigilon' }, vas_detail: { id: 4, name: 'VAS 4' }, image: imgQuoteError },
{ id: 306, created_at: '2018-02-23T09:43:08.928Z', rule_detail: { id: 2, name: 'Crowd Behaviour' }, cam_detail: { id: 3, name: 'kamera avigilon' }, vas_detail: { id: 4, name: 'VAS 4' }, image: imageStream },
],
onAction: action,
enableActionBlock: false
}
it('snapshot', () => {
const wrapper = shallow(<Pagination {...dataListProps}/>)
expect(wrapper).toMatchSnapshot();
})
})
I need help for solving this
As pointed by Xarvalus. If wanna access refs, the component have to be mounted first by using mount from import { shallow, mount, render } from 'enzyme';.
But it will have bug (RangeError: Invalid string length). So to overcome this, we have to convert enzyme to json by using import toJson from 'enzyme-to-json';
full working code
import React from 'react';
import { shallow, mount, render } from 'enzyme';
import toJson from 'enzyme-to-json';
import Pagination from '../index';
describe('Testing Pagination', () => {
it('snapshot', () => {
const wrapper = mount(<Pagination {...dataListProps}/>)
expect(toJson(wrapper)).toMatchSnapshot();
})
})
reference issue
You can access the window object inside your component, and so retrieve the window.innerWidth field which is, I guess, what you're looking for.

Categories

Resources