I am trying to get select the list of files(path) from directory and display it on the DOM,but in order to display data i have to first assign in vuejs data object.
mainRenderer
ipcMain.on("channel1", (e, args) => {
const files = getFileFromUserSelection();
e.reply("channel1", files); // sending to channel1 in app.vue
});
const getFileFromUserSelection = () => {
const files = dialog.showOpenDialog({
properties: ["multiSelections"]
});
if (!files) return;
return files;
};
App.Vue
<template>
<div>
<p v-for="file in files">{{file}}</p>
</div>
</template>
<script>
import { ipcRenderer } from "electron";
//this gets files from the main process
ipcRenderer.on("channel1", (e, files) => {
console.log(files); // i want this file into data:function()
});
export default {
data: function() {
return {
files: []
};
},
methods: {
clicked: function() {
ipcRenderer.send("channel1", "open dialog to getFiles from user");
}
}
};
</script>
You can use the beforeCreate hook on the Vuecomponent in order to hydrate the data property:
export default {
data: function() {
return {
files: []
};
},
beforeCreate() {
ipcRenderer.on("channel1", (e, files) => {
this.files = files
});
},
methods: {
clicked: function() {
ipcRenderer.send("channel1", "open dialog to getFiles from user");
}
}
};
Note that, of course, you cannot interact directly with the files array until you know it's been hydrated, so a computed getter here may be of some use to you, or just use files.length.
Related
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?
I am trying to integrate the webSDK from https://www.pollfish.com/docs/webplugin in our Vue app.
Ideally I want to load jquery only in one component.
I wrote the following code but when I click the button it doesnt work.
Here is an example with working code that does NOT use Vue https://github.com/pollfish/webplugin-rewarded-example/blob/master/index.html but does run locally.
I get no errors and I can console.log(Pollfish) inside the the showFullSurvey method.
My code is:
<template>
<div class="container" v-if="isFreePlan">
<h2>Remove ads and save unlimited projects for 5 days</h2>
<button #click="showFullSurvey">Take {{lengthOfInteraction}} Survey Now</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
data() {
return {
surveyAvailable: false,
lengthOfInteraction: ''
}
},
methods: {
showFullSurvey() {
Pollfish.showFullSurvey();
console.log('show survey')
}
},
mounted() {
const pollFishConfig = {
api_key: "api-key",
debug: process.env.NODE_ENV === 'production' ? false : true,
ready: () => {},
uuid: this.userId,
surveyAvailable: onSurveyAvailable,
surveyNotAvailable: onSurveyNotAvailable,
surveyCompletedCallback: onSurveyCompleted,
userNotEligibleCallback: onUserDisqualified
};
console.log('POllfish config');
const onSurveyAvailable = (data) => {
console.log('SUrvey Available');
};
const onSurveyNotAvailable = () => {
console.log('SUrvey Not Available');
};
const onSurveyCompleted = () => {
console.log('SUrvey Completed');
};
const onUserDisqualified = () => {
console.log('USer Disqualified');
};
this.addJQuery;
this.addPollFishSDK;
},
computed: {
...mapGetters("session", ['userId']),
...mapGetters("account", ["isFreePlan"]),
addJQuery() {
const url = 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js';
if(document.querySelector(`script[src='${url}']`)){ return; }
let jquery = document.createElement('script');
jquery.setAttribute('src', url);
document.body.appendChild(jquery);
console.log('jquery script')
},
addPollFishSDK() {
const url = 'https://storage.googleapis.com/pollfish_production/sdk/webplugin/pollfish.min.js';
if(document.querySelector(`script[src='${url}']`)){ return; }
let pollFishSdk = document.createElement('script');
pollFishSdk.setAttribute('src', url);
document.body.appendChild(pollFishSdk);
console.log('pollfish script')
}
}
}
</script>
In order to integrate our web plugin in your Vue.js app, you need to set the pollfishConfig object in the window. Please be careful with the object's name to be exactly the same as the following example.
window.pollfishConfig = {
api_key: "api-key",
debug: process.env.NODE_ENV === 'production' ? false : true,
ready: () => {},
uuid: this.userId,
surveyAvailable: onSurveyAvailable,
surveyNotAvailable: onSurveyNotAvailable,
surveyCompletedCallback: onSurveyCompleted,
userNotEligibleCallback: onUserDisqualified
};
Also, based on your example, you need to be sure that the jQuery library is loaded first and be available for our WebPlugin SDK. So you need to handle the onload event. An example solution based on your code is the following:
const addScript = (url, onLoad) => {
const scriptExists = document.querySelector(`script[src='${url}']`);
if (!scriptExists) {
let script = document.createElement('script');
document.body.appendChild(script);
script.onload = () => {
onLoad && onLoad();
}
script.src = url;
}
}
addScript('https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', () => {
addScript('https://storage.googleapis.com/pollfish_production/sdk/webplugin/pollfish.min.js')
});
I'm using vue in laravel and trying to get a controller function that I'm hitting to return the data so that I can use it in the data() section of my vue template.
I know the controller function returns what I need, but I'm not so sure how I need to handle the return/response in the axios call in order to start placing the data into the data() function in vue
Blade/Vue template
import moment from 'moment'
export default {
name: 'calendar',
data () {
return {
events: [
{
title: 'test',
allDay: true,
start: '2019-08-17',
},
],
config: {
defaultView: 'month',
eventRender: function(event, element) {
console.log(event)
}
},
}
},
created() {
this.fetchTasks();
},
methods: {
fetchTasks() {
axios.get('/landing/tasks' )
.then((response) => {
// handle success
this.assetOptions = response.data;
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
});
}
}
}
Route
Route::get('/landing/tasks', 'landingController#getTasks')
->name('landing/tasks');
Controller
public function getTasks()
{
$getTask = Task::getTaskForLanding();
$result = array();
foreach($getTask as $id => $task){
$result[$task->taskt_id][] = $task;
}
}
If you are certain that the Controller returns what you need, the only thing you are missing is declaration of assetOptions. To be able to assign response.data to assetOptions later on, you have to declare it in the data function first.
data() {
return {
...
assetOptions = []; // assuming you are expecting an array
...
};
}
Once that is done, you are all set.
I've inherited a project and the original author split their code into separate JS files. One of them contains all of the GET requests, such as Departments, Categories, Titles, etc.
I have some code for an autocomplete dropdown in a separate file, index.js, and I want to populate the dropdown with the Department results. I'm not that well-versed in JS and I'm not sure what I have to do to "connect" the files and reference the Departments GET request.
index.js:
import "jquery";
import "jquery-autocomplete/jquery.autocomplete.css";
import "jquery-autocomplete/jquery.autocomplete.js";
function loadCombobox() {
let depts = {
"t1": "1",
"t2": "2"
} // added this as a test
let deptsArray = $.map(depts, function(value, key) {
return {
value: value,
data: key
};
})
console.log(deptsArray);
$('#combobox').autocomplete({
source: deptsArray
}).on("click", function() {
console.log("combobox clicked") // works
});
}
loadCombobox();
data-request.js
import axios from "axios";
const baseUrl = siteAbsoluteUrl;
const deptList = "Department List";
async function makeGetRequest(url) {
const response = await axios.get(url, getRequestConfig);
const { results, __next } = response.data.d;
return { results, __next };
}
// gives back an array containing all departments, without duplicates
export async function makeDepartmentsRequest() {
const { results } = await makeGetRequest(
`${baseUrl}/getbytitle('${deptBPList}')/fields?$filter=EntityPropertyName eq 'Department'`
);
return results[0].Choices.results;
}
html:
<input type="text" id="combobox" placeholder="Browse departments">
console:
Something like this would be the end result (note that I don't know what makeDepartmentsRequest returns or what #combobox is so I can't test this code)
import { makeDepartmentsRequest } from "path-to/data-request";
function loadCombobox() {
makeDepartmentsRequest.then(choices => {
const source = $.map(depts, function(value, key) {
return {
value: value,
data: key
};
});
$("#combobox")
.autocomplete({
source
})
.on("click", function() {
console.log("combobox clicked"); // works
});
});
}
I'm stuck at a crossroads with a component I am working on.
I have the following component "RecentUpdates"
Within it I am passing props down to a few other components, as you can see from the top of the file.
My problem is when adding a new post, I can not figure out how to get the correct update object array back and i also can not figure out the correct 'Vue way' to update the data prop that is being passed down to the "PostList" component.
<template>
<div>
<PostFilter v-on:selectedCategory="getSelectedPosts" v-on:showAllPosts="showAllPosts" :user="user" :categories="categories"/>
<PostList v-if="recent_posts[0]" :categories="categories" :posts="recent_posts[0]" :user="user"/>
<Pagination v-on:getPreviousPage="getPreviousPage" v-on:getNextPage="getNextPage"/>
</div>
</template>
<script>
import PostList from './PostList';
import PostFilter from './PostFilter';
import Pagination from './Pagination';
import EventBus from '../event-bus';
export default {
name: 'RecentUpdates',
data: () => ({
errors: [],
recent_posts: [],
}),
props: ['categories', 'user'],
components: {
PostList,
PostFilter,
Pagination
},
created() {
if (this.user.meta.selected_categories[0] == 0) {
this.showAllPosts();
}
// do not call here, not working as expected
// is switching selected category to an incorrect one
// this.updateList();
this.getSelectedCategory();
},
watch: {
recent_posts: function(newValue) {
EventBus.$on('addPost', function(newPost) {
console.log(newPost);
this.$forceUpdate();
//this.recent_posts.push(newPost);
//this.$set(this.recent_posts, newPost, newPost);
// this.$nextTick(function () {
// this.recent_posts.push(newPost);
// });
});
console.log(this.recent_posts[0]);
// this.$nextTick(function () {
// console.log(this.recent_posts[0]) // => 'updated'
// });
// if (this.user.meta.selected_categories[0] == 0) {
// EventBus.$on('addPost', this.showAllPosts);
// } else {
// EventBus.$on('addPost', this.getSelectedCategory);
// }
//this.updateList();
}
},
methods: {
// updateList() {
// if (this.user.meta.selected_categories[0] == 0) {
// EventBus.$on('addPost', this.showAllPosts);
// //EventBus.$emit('newPost');
// } else {
// EventBus.$on('addPost', this.getSelectedCategory);
// //EventBus.$emit('newPost');
// }
// },
getSelectedCategory() {
let categoryId = this.user.meta.selected_categories[0];
this.getSelectedPosts(categoryId);
},
showAllPosts() {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]',
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = [];
//this.recent_posts = response.data;
//console.log(response.data);
this.recent_posts.push(response.data);
console.log(this.recent_posts[0]);
})
.catch(e => {
this.errors.push(e);
});
},
getSelectedPosts(categoryId) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&categories=' + categoryId,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = [];
//console.log(response.data);
this.recent_posts.push(response.data);
console.log(this.recent_posts[0]);
})
.catch(e => {
this.errors.push(e);
});
},
/**
* Pagination methods
*
*/
getPreviousPage(page) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&page=' + page,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = response.data;
})
.catch(e => {
this.errors.push(e);
});
},
getNextPage(page) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&page=' + page,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = response.data;
})
.catch(e => {
this.errors.push(e);
});
}
},
}
</script>
<style>
</style>
So there are a number of issues I see reading through your code.
You have a recent_posts data property, which is an array. When you make your ajax call to get the posts you push the response which is also an array into the recent_posts array. Why? Why not just set recent_posts = response.data? Then you won't have to be passing recent_posts[0] around.
You're setting up your EventBus handler inside a watcher. This is really unusual. Typically you would set up a handler inside created or mounted.
this inside the EventBus handler likely refers to the EventBus and not your Vue. Ideally, you would set the handler to be a method on the component, which is already bound to the Vue. Something like EventBus.$on("addPost", this.addPost).
Once you've done all that, adding a new post should be as simple as this.recent_posts.push(newPost).
Here is what I might recommend.
export default {
name: 'RecentUpdates',
data(){
return {
errors: [],
recent_posts: []
}
},
props: ['categories', 'user'],
components: {
PostList,
PostFilter,
Pagination
},
created() {
if (this.user.meta.selected_categories[0] == 0) {
this.showAllPosts();
}
this.getSelectedCategory();
EventBus.$on("addPost", this.addPost)
},
beforeDestroy(){
EventBus.$off("addPost", this.addPost)
},
methods: {
getPosts(url){
axios.get(url, {headers: {'X-WP-Nonce': portal.nonce}})
.then(response => this.recent_posts = response.data)
.catch(e => this.errors.push(e))
},
showAllPosts() {
const url = '/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]';
this.getPosts(url);
},
getSelectedPosts(categoryId) {
const url = '/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&categories=' + categoryId;
this.getPosts(url);
},
addPost(newPost){
this.recent_posts.push(newPost)
},
... //other methods
},
}
Try using kebab-case in your event listeners instead of camelCase:
Example: v-on:selectedCategory="getSelectedPosts" should be v-on:selected-category="getSelectedPosts".
Example: v-on:showAllPosts="showAllPosts" should be v-on:show-all-posts="showAllPosts" or even using the shortcut #show-all-posts="showAllPosts".
UPDATE: If you can provide the code of the other components so we can have a clearer vision of your problem, But you only want to track changes that happens on an object or an array in vue.js you need to deep watch them.
your watcher should be :
watch: {
recent_posts: {
deep: true,
handler: function( oldValue, newValue) {
console.log( "recent_posts has changed" );
// A post has been added, updated or even deleted
}
}
}