Cannot create new Instance Properties in Vue.js (laravel project) - javascript

I'm trying to create localization property, like __('text') in Laravel blade template.
I have created a global window variable, which contents all needed data with name window.i18n
Here is my resourses/js/app.js file:
require('./bootstrap');
window.Vue = require('vue');
// Here I tried to create new Instance properties
import _ from 'lodash'
window.Vue.prototype.__ = str => _.get(window.i18n, str)
Vue.component('autocomplete',require('./components/SearchComponent.vue').default);
const app = new Vue({
el: '#app',
});
And my /components/SearchComponent.vue file, only script part:
<script>
export default{
props: [
'categories'
],
data(){
return {
query: '',
results: [],
categoryText: __('text.save'), // Here I tried to use __() property
categoryId: 0
}
},
methods: {
autoComplete() {
this.results = [];
if(this.query.length > 2){
axios.get('/posts/search',{params: {query: this.query, category_id: this.categoryId}}).then(response => {
this.results = response.data;
this.categoryText = "Избери";
this.categoryId = 0;
});
}
},
categoryChange(e) {
this.categoryText = e.target.text;
var id = event.target.getAttribute('data-category-id');
this.categoryId = id;
},
mounted: function () {
console.log(this.categories);
}
}
}
</script>
I got this error:
app.js:38639 [Vue warn]: Error in data(): "ReferenceError: __ is not
defined"
Cannot handle what I'm missing.

You've added the __ to Vue.prototype, which makes it an instance property. To access instance properties, use this inside data():
export default {
data() {
return { 👇
categoryText: this.__('text.save')
}
}
}
Alternatively, you could make __ a global by attaching it directly to window, which would allow your current code to work:
window.__ = str => _.get(window.i18n, str)

Related

Snapshot test using vue is failing with "TypeError: Cannot read property of undefined"

The component itself works on the page with no errors.
So does storybook, the only issue is the unit test.
import { mount } from '../../vue';
import { createLocalVue } from '#vue/test-utils';
import Vuex from 'vuex';
import SessionGymChart from '#/components/SessionGymChart.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
const defaultprops = {
endDateOveride: '',
totalHours: 0,
sessionsTotal: 1,
isLoading: false,
hasTooMuchData: false,
swapChart: false,
chartData: {}
};
describe('SessionGymChart', () => {
let store;
beforeEach(() => {
store = new Vuex.Store({
state: {
user: { billing: true },
chartData: {
days: 10,
sessions: [
{
id: '324365egdfgfd',
sessionReference: '056343',
dateStarted: '2022-08-26T16:23:14.909Z',
dateEnded: '2022-08-26T16:23:22.000Z',
userId: 'dfgdfgdfg545645',
userDisplayName: 'Tester'
}
]
}
},
mutations: {},
actions: {}
});
});
Is there anything obvious I'm doing wrong?
There is a computed property where it breaks on the component. It seems like it can't access the sessions data (saying undefined) and I'm not sure why as it is inside defaultprops.
This is where it shows to be breaking in the component, at a computed property and this shows on the snapshot error.
gymSeries() {
const { sessions } = this.chartData ? this.chartData : {};
> if (sessions.length === 0) return {};
^
else {
const sessionsUsage = sessions.map(x => {
return {
Any help would be much appreciated!
This actually returns undefined if this.chartData is undefined:
const { sessions } = this.chartData ? this.chartData : {};
The problem there is that you're destructuring chartData, and you expect the return value of the ternary statement to be an object that contains a sessions property.
If chartData is undefined, that results in:
const { sessions } = {};
You can fix that by adding an empty sessions property:
const { sessions } = this.chartData ? this.chartData : { sessions: {} };
Or, shorter, using optional chaining:
const sessions = this.chartData?.sessions || {};

vuex shared state in chrome extension

I have a chrome extension with the following webpack.config.js:
module.exports = {
mode,
entry: {
"content/content": [
"./src/js/content/content.js",
"./src/js/store.js",
"./src/js/content/overlay/style.scss",
],
"background/background": [
"./src/js/background/utils.js",
"./src/js/background/background.js",
],
"overlay/overlay": "./src/js/content/overlay/index.js",
"popup/popup": "./src/js/content/popup/index.js",
},
looking at
Shared vuex state in a web-extension (dead object issues)
https://github.com/xanf/vuex-shared-mutations
Adding a wrapper around browser local storage:
browserStore.js
import browser from "#/js/browser";
export function getStorageValue(payload) {
return new Promise((resolve) => {
browser.storage.local.get(payload, (items) => {
if (items) {
resolve(items);
}
});
});
}
export function setStorageValue(payload) {
return new Promise((resolve) => {
browser.storage.local.set(payload, (value) => {
resolve(value);
});
});
}
In "./src/js/content/popup/firstpage/store/index.js" vuex store is defined as:
import Vue from "vue";
import Vuex from "vuex";
import "es6-promise/auto";
import createMutationsSharer from "vuex-shared-mutations";
import dummyData from "./dummyData";
import { getStorageValue, setStorageValue } from "#/js/store";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
chromePagesState: {
allSections: [],
},
},
getters: {
...
},
mutations: {
setChromePagesState(state, value) {
...
},
// this function is to be called from a content script
addWhiteListedItem(state, item) {
// state not initialized here
state.chromePagesState.allSections[0].itemSectionCategory[0].tasks.splice(
0,
0,
item
);
},
...
}
actions: {
async saveChromePagesState({ state }) {
// Save only needed fields
let data = {
...
};
await setStorageValue({ inventoryData: JSON.stringify(data) });
},
async loadChromePagesState({ commit }) {
const json = await getStorageValue("inventoryData");
// json always an empty object
commit(
"setChromePagesState",
Object.keys(json).length === 0 && json.constructor === Object
? json
: dummyData
);
},
async loadChromePagesStateBrowser({ commit }) {
browser.runtime
.sendMessage({ type: "storeinit", key: "chromePagesState" })
.then(async (chromePagesState) => {
const json = await getStorageValue("inventoryData");
commit(
"setChromePagesState",
Object.keys(json).length === 0 && json.constructor === Object
? json
: dummyData
);
});
},
plugins: [
createMutationsSharer({
predicate: [
"addWhiteListedItem",
"loadChromePagesState",
"loadChromePagesStateBrowser",
],
}),
],
},
the background script has a listener; src/background/background.js:
browser.runtime.onMessage.addListener((message, sender) => {
if (message.type === "storeinit") {
return Promise.resolve(store.state[message.key]);
}
});
The content script that needs to make use of the shared store has an entry point in content.js:
import { initOverlay } from '#/js/content/overlay';
import browser from '#/js/browser';
browser.runtime.onMessage.addListener(function (request, _sender, _callback) {
// vue component gets created here:
if (request && request.action === 'show_overlay') {
initOverlay();
}
return true; // async response
});
initOverlay() creates a vue component in ./src/js/content/overlay/index.js:
import Vue from "vue";
import Overlay from "#/js/content/overlay/Overlay.vue";
import browser from "#/js/browser";
import { getStorageValue } from "#/js/store";
import store from "../popup/firstpage/store";
Vue.prototype.$browser = browser;
export async function initOverlay(lockScreen = defaultScreen, isPopUp = false) {
...
setVueOverlay(overlayContainer, cover);
...
}
function setVueOverlay(overlayContainer, elem) {
if (!elem.querySelector("button")) {
elem.appendChild(overlayContainer);
elem.classList.add("locked");
new Vue({
el: overlayContainer,
store,
render: (h) => h(Overlay, { props: { isPopUp: isPopUp } }),
});
}
}
Overlay.vue only needs to call a mutation (addWhiteListedItem) from store:
<template>
<button
#click="addToWhiteList()"
>White list!</button
>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
data() {
return {
};
},
computed: mapState(["chromePagesState"]),
methods: {
...mapMutations(["addWhiteListedItem"]),
addToWhiteList() {
console.log("addToWhiteList()");
let newItem = {
...
};
// store not defined fails with:
Uncaught TypeError: Cannot read property 'itemSectionCategory' of undefined
at Store.addWhiteListedItem (index.js:79)
at wrappedMutationHandler (vuex.esm.js:853)
at commitIterator (vuex.esm.js:475)
at Array.forEach (<anonymous>)
at eval (vuex.esm.js:474)
at Store._withCommit (vuex.esm.js:633)
at Store.commit (vuex.esm.js:473)
at Store.boundCommit [as commit] (vuex.esm.js:418)
at VueComponent.mappedMutation (vuex.esm.js:1004)
at eval (Overlay.vue?./node_modules/vue-loader/lib??vue-loader-options:95)
this.addWhiteListedItem(newItem);
}, 1500);
},
},
};
</script>
Why doesn't Overlay.vue "see" the state of store?
Flow:
enabling the extension injects a content script into a page
content script imports store object (that is not yet initialized)
upon clicking popup (/new tab) popup.js sends a message to the background script that also imports store and calls a mutation (that initializes state):
background.js
import store from "../content/popup/firstpage/store";
browser.runtime.onMessage.addListener((message, sender) => {
console.log("in background");
if (message.type === "storeinit") {
console.log("got storeinit message. Message key: ", message.key);
store.dispatch("loadChromePagesState");
console.log("current state in store:", JSON.stringify(store.state));
console.log(
"store.state[message.key]:",
JSON.stringify(store.state[message.key])
);
return Promise.resolve(store.state[message.key]);
}
});
now the store's state should be initialized and the mutation callable from the content script (vue-shared-mutations guarantees it)
Does export default new Vuex.Store mean that every script that imports the store gets a new instance with a default state that is not in sync with other imports?
As the error message suggests itemSectionCategory can not be found as it is expected to be an element of allSections[0]. However you never define index 0 of allSections before calling it.
So in short you need to either define allSections index 0 before using it, or make the index part optional and create it if it's not found.
Otherwise you could try one of the following solutions:
if you need to rely on index 0 being available, check if it is set before calling your function
!state.chromePagesState.allSections[0] ? [... insert initialize function call ...]
Maybe optional chaining could be another solution depending on what you use it for afterwards, for an example How to use optional chaining with array or functions?

How can I use a Global Mixin method from a Vue instance

Let's suppose that I have the following situation, using a Global Mixin to create a global helper method with Vue:
import Vue from "vue";
Vue.mixin({
methods: {
replaceString: function (word) {
return word.toLowerCase().replace(/\W/g, '');
}
}
});
let vm = new Vue({
methods: {
doSomething: function() {
console.log(this.replaceString('Hello World'); //helloword
}
}
});
I know that I can invoke the method inside the other methods, inside of the component and their childs. But how can I invoke the mixin method "replaceString" from the Vue instance "vm"?
I tried to use "vm.replaceString", but keeps returning "undefined".
Few changes to your code and it works:
You should change the definition of your mixin (var mixin instead of Vue.mixin)
Import the mixin to your new vue component (mixins = [mixin])
import Vue from "vue";
var mixin = {
methods: {
replaceString: function (word) {
return word.toLowerCase().replace(/\W/g, '');
}
}
};
let vm = new Vue({
mixins: [mixin]
methods: {
doSomething: function() {
console.log(this.replaceString('Hello World'); //helloword
}
}
});
I think this chunk o code is what you are looking for:
var mixin = {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
})
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
From the docs

How to fetch data from server in vue js?

I am trying to fetch data from the server using Vue + Vuex + Vue resource.On button click I want to hit Http request and show in list format .I tried like that.Here is my code
https://plnkr.co/edit/EAaEekLtoiGPvxkmAtrt?p=preview
// Code goes here
var store = new Vuex.Store({
state: {
Item: []
},
mutations: {
getItems: function (state) {
}
},
actions: {
fetchData:function (context) {
this.$http.get('/data.json', function(v1users)
{
// this.$set('v1_user',v1users);
});
}
}
})
var httprequest = Vue.extend({
"template": '#http_template',
data: function () {
return {
items: store.state.Item
}
},
methods: {
fetchData: function () {
store.dispatch('fetchData')
},
}
})
Vue.component('httprequest', httprequest);
var app = new Vue({
el: '#App',
data: {},
})
;
any udpdate?
Try using Vue.http.get instead of this.$http.get.
Vuex doesn't have access to $http directly from instance.

Add custom property to vuejs component

How do I set a custom property inside a vue component?
var myComponent = Vue.extend({
data: function() {
return {
item: {}
}
},
created: function() {
// This does not seem to work
this.item.customProperty = 'customProperty';
}
});
You can use Vue.set to add reactivity:
var myComponent = Vue.extend({
data: function() {
return {
item: {}
}
},
created: function() {
Vue.set(this.item, 'customProperty', 'customProperty');
}
});
It seems that you should use Object.assign:
var myComponent = Vue.extend({
data: function() {
return {
item: {}
}
},
created: function() {
// This does not seem to work
this.item = Object.assign(this.item, {customProperty:'customProperty'});
}
});

Categories