I m using vis.js timeline and i want display date from firestore. It works when I type manually (look --> this.items), but does not work with firestore (look --> this.users).
I m using Vue framework.
<script>
export default {
data() {
return {
users: [],
items: [
{
id: '1',
content: 'London',
group: 'Mike',
start: '2021-12-20',
end: '2022-06-19',
},
],
}
},
async fetch() {
await this.loadPlaces()
},
methods: {
async loadPlaces() {
const querySnapshot = await getDocs(collection(db, 'places'))
querySnapshot.forEach((doc) => {
this.users.push({ id: doc.id, ...doc.data() })
})
this.$store.commit('places/setPlaces', this.users)
},
},
computed: {
places() {
return this.$store.state.places.places
},
},
mounted() {
let container = document.getElementById('visualization')
let options = {
moveable: true,
}
let timeline = new vis.Timeline(container)
timeline.setOptions(options)
timeline.setGroups(this.groups)
timeline.setItems(this.items)
},
}
</script>
I found a solution.
I just moved all code from mounted() to method loadPlaces (under this.$store.commit)
Save yourself trouble and use the vis datasets instead.
my pinia store in vue 3 looks like this.
import { defineStore } from 'pinia'
import { DataSet } from 'vis-data/esnext'
export const useVisData = defineStore('visData', {
state: () => ({
items: new DataSet([]),
groups: new DataSet([]),
selectedItems: [],
serializedGroupsAndItems: []
}),
actions: {
//Group actions
showAllGroups() {
this.groups.forEach(group => {
this.groups.updateOnly({ id: group.id, visible: true })
});
},
addGroup(group) {
this.groups.add(group)
},
hideGroup(group) {
this.groups.updateOnly({ id: group, visible: false })
},
//Item actions
addItem(item) {
this.items.add(item)
},
removeItem(item) {
this.items.remove(item)
},
setSelectedItems(items) {
this.selectedItems = items
},
//data add/remove
serializeData() {
this.serializedGroupsAndItems.push({
groups: JSON.stringify(this.groups.get()),
items: JSON.stringify(this.items.get())
})
},
loadSerializedData() {
this.clearGroupsAndItems()
this.serializedGroupsAndItems.forEach(data => {
this.addGroup(JSON.parse([data.groups]))
this.addItem(JSON.parse([data.items]))
})
},
//misc
clearGroupsAndItems() {
this.groups.clear()
this.items.clear()
}
},
getters: {
getHiddenGroups(state) {
return state.groups.get({
filter: (item) => {
return item.visible === false
}
})
}
}
})
Also remember to watch for changes in your options.
Might be better to wrap it in a vue component too. something like this.
this is what i did.
let timeline;
const visref = ref(null);
onMounted(async () => {
timeline = new Timeline(visref.value, props.items, props.groups, {...props.options, ...timelineOptions});
props.events.forEach(event => {
on(event, (properties) => {
// console.log(event, properties)
emits(`vis${event}`, properties);
});
});
})
<template>
<div ref="visref"></div>
</template>
then you can use it like so:
const timelineref = ref();
<Timeline
ref="timelineref"
:items="visStore.items"
:groups="visStore.groups"
:options="options"
/>
remember to expose the instance in your timeline component then you can call the functions using a ref like this.
timelineref.value.timeline.zoomOut(0.5)
Related
I have some data in a firestore and want to display it in my vue app.
Firestore:
Test.vue
<template>
<p>{{ items }}</p>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: mapState(["items"]),
methods: {
getItems() {
this.$store.dispatch("bindItems");
},
},
mounted() {
this.getItems();
},
};
</script>
index.js
import { createStore } from "vuex";
import { firestoreAction } from "vuexfire";
import { vuexfireMutations } from "vuexfire";
import { db } from "./db";
const store = createStore({
state() {
return {
items: [],
};
},
actions: {
bindItems: firestoreAction(({ bindFirestoreRef }) => {
return bindFirestoreRef("items", db.collection("items"));
}),
},
mutations: {
...vuexfireMutations,
},
getters: {
items(state) {
return state.items;
},
},
});
store.subscribe((state) => console.log(state));
export default store;
To check my store, I added this line in index.js: store.subscribe((state) => console.log(state));. As it turns out, the data from firestore actually makes it to my store:
Why is it not rendered in the frontend? What do I have to change to make it appear?
Edit.
When I hardcode some data in my store and remove this.$store.dispatch("bisndItems"); from the monuted hook, the data gets rendered:
state() {
return {
items: {"name": "peter"},
};
},
I am writing a chat app with the following data model:
using the following approach:
store/index.js
actions: {
bindMessages: firestoreAction(({ state, bindFirestoreRef }) => {
// return the promise returned by `bindFirestoreRef`
return bindFirestoreRef(
'messages',
db.collection('groupchats')
);
}),
},
then I access the messages store.state from my vue components like this:
computed: {
Messages() {
return this.$store.state.messages;
},
},
according to the vuexfire docs.
I was able to get the data of the whole collection (reactively) but I want the **messages array ** of a specific document assuming I know the document id. How do I do so?
The following should do the trick:
state: {
// ...
doc: null
},
mutations: vuexfireMutations,
getters: {
// ...
},
actions: {
bindDoc: firestoreAction(({ bindFirestoreRef }) => {
return bindFirestoreRef('doc', db.collection('groupchats').doc('MI3...'))
})
}
You can make it dynamic as follows:
Component:
// ...
<script>
export default {
created() {
this.$store.dispatch('bindDoc', { id: '........' }); // Pass the desired docId, e.g. MI3...
},
};
</script>
// ...
Vuex store:
state: {
// ...
doc: null
},
mutations: vuexfireMutations,
getters: {
// ...
},
actions: {
bindDoc: firestoreAction(({ bindFirestoreRef }, payload) => {
return bindFirestoreRef('doc', db.collection('groupchats').doc(payload.id));
}),
}
I use react with redux.
Action:
export const updateClicked = (id, section) => {
return {
type: actionTypes.UPDATE_CLICKED,
id,
section
};
};
Please advise the best way to immutable update property in nested array:
Reducer:
const initialState = {
updates: {
html: {
id: 'html',
label: 'HTML',
count: 0,
items: [
{
id: 1,
label: 'Header',
price: 10,
bought: false
},
{
id: 2,
label: 'Sidebar',
price: 50,
bought: false
}
]
}
}
};
My action:
action = {
id: 1,
bought: true
}
I want to update bought property inside items array. I.e.:
const updateClicked= (state, action) => {
const updateSections = state.updates[action.section].items;
const updatedItems = updateSections.map(el => {
if (el.id === action.id && !el.bought) {
el.bought = true;
}
return el;
});
//How to update state???
return {}
};
Will be glad if you explain 2 ways to do this:
With es6 spread operator
With some library (like immutability-helper)
Thanks!
With es6 spread operator:
export default (state = initialState, action) => {
if (action.type !== actionTypes.UPDATE_CLICKED) return state;
return {
...state,
updates: {
...state.updates,
html: {
...state.updates.html,
items: state.updates.html.items.map((item, idx) => idx === action.id
? {...item, bought: item.bought}
: item
)
}
}
}
};
I'm using Meteor React and trying to get react-autosuggest working (from the basic usage example https://github.com/moroshko/react-autosuggest
The error I get is Missing class properties transform - it's happening at onChange (right after this.state.
this.state = {
value: '',
suggestions: getSuggestions('')
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsUpdateRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
I've looked around to see if I could find a solution, but no luck.
Hopefully someone can shed some light on what's going on.
Using class Example extends React.Component {...} doesn't work with meteor. Try this approach instead:
import React from 'react';
import Autosuggest from 'react-autosuggest';
import AutosuggestHighlight from 'autosuggest-highlight';
Example = React.createClass({
getInitialState() {
return {
value: '',
suggestions: [],
};
},
onChange(event, { newValue }) {
this.setState({
value: newValue,
});
},
onSuggestionsFetchRequested({ value }) {
this.setState({
suggestions: this.getSuggestions(value),
});
},
onSuggestionsClearRequested() {
this.setState({
suggestions: [],
});
},
getSuggestionValue(suggestion) {
return suggestion.name;
},
getSuggestions(value) {
const languages = [
{
name: 'C',
year: 1973,
},
{
name: 'C#',
year: 2001,
},
{
name: 'C++',
year: 1984,
},
{
name: 'Clojure',
year: 2008,
},
{
name: 'Elm',
year: 2013,
},
{
name: 'Go',
year: 2010,
},
];
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
if (inputLength === 0) {
return [];
}
return languages.filter(language =>
language.name.toLowerCase().slice(0, inputLength) === inputValue
);
},
renderSuggestion(suggestion, { query }) {
const suggestionText = `${suggestion.name} (${suggestion.year})`;
const matches = AutosuggestHighlight.match(suggestionText, query);
const parts = AutosuggestHighlight.parse(suggestionText, matches);
return (
<span className="suggestion-content">
<span>
{
parts.map((part, index) => {
const className = part.highlight ? 'highlight' : null;
return (
<span className={className} key={index}>{part.text}</span>
);
})
}
</span>
</span>
);
},
render() {
const { value, suggestions } = this.state;
const inputProps = {
value,
placeholder: 'Enter programming language...',
onChange: this.onChange,
};
return (
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={this.getSuggestionValue}
renderSuggestion={this.renderSuggestion}
inputProps={inputProps} />
</div>
);
},
});
Then simply use <Example /> to render the autosuggest field.
This was tested with Meteor 1.4.1.1
I am working with Vuex and Firebase Auth system.
I just want to store with Vuex the user object that i get from:
firebase.auth().getCurrentUser
so that every time it changes, it updates the views.
But i ve troubles with this.
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: {
loggedIn: false,
data: null
}
},
getters: {
user(state){
return state.user
}
},
mutations: {
SET_LOGGED_IN(state, value) {
state.user.loggedIn = value;
},
SET_USER(state, data) {
state.user.data = data;
}
},
actions: {
fetchUser({ commit }, user) {
commit("SET_LOGGED_IN", user !== null);
if (user) {
commit("SET_USER", user);
} else {
commit("SET_USER", null);
}
}
}
});
Account.vue
<template>
<ion-item>
<ion-label #click="openModal()" position="stacked">Your name</ion-label>
{{user.data.displayName}}
</ion-item>
</template>
computed: {
// map `this.user` to `this.$store.getters.user`
...mapGetters({
user: "user"
})
},
methods: {
openModal() {
let us = this.$store.getters.user;
return this.$ionic.modalController
.create({
component: Modal,
componentProps: {
data: {
content: 'New Content',
},
propsData: {
pro: us.data.displayName
},
},
})
.then(m => m.present())
},
.
.
.
</script>
Modal.vue
<template>
<ion-app>
<h1>MODAL</h1>
<ion-input :value="prop" #input="prop = $event.target.value"></ion-input>
<ion-button #click="clos()">Save</ion-button>
</ion-app>
</template>
<script>
import firebase from "firebase";
export default {
props:['pro'],
data(){
return{
prop: this.pro
}
},
methods:{
clos(){
let vm = this;
let user = firebase.auth().currentUser;
window.console.log("updating",vm.prop)
user.updateProfile({
displayName: vm.prop
}).then(function(){
user = firebase.auth().currentUser;
vm.$store.dispatch("fetchUser",user);
}).catch(function(err){
window.console.log("err",err);
})
this.$ionic.modalController.dismiss();
}
}
}
</script>
I can see using Vue Dev Tools that when I dispatch the new user in Modal.vue
vm.$store.dispatch("fetchUser",user);
that the Vuex state is correctly updated, but the view in Account.vue is not.
But if I press the button 'commit this mutation' in the dev tools the view updates!
How can I fix this behavior?
try this solution:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: {
loggedIn: false,
data: null
}
},
getters: {
user(state){
return state.user;
},
userData(state){
return state.user.data;
}
},
mutations: {
SET_LOGGED_IN(state, value) {
state.user.loggedIn = value;
},
SET_USER(state, data) {
state.user.data = data;
}
},
actions: {
fetchUser({ commit }, user) {
commit("SET_LOGGED_IN", user !== null);
if (user) {
commit("SET_USER", user);
} else {
commit("SET_USER", null);
}
}
}
});
Account.vue
<template>
<ion-item>
<ion-label #click="openModal()" position="stacked">Your name</ion-label>
{{user.displayName}}
</ion-item>
</template>
computed: {
// map `this.user` to `this.$store.getters.user`
...mapGetters({
user: "userData"
})
},
methods: {
openModal() {
let us = this.$store.getters.userData;
return this.$ionic.modalController
.create({
component: Modal,
componentProps: {
data: {
content: 'New Content',
},
propsData: {
pro: us.displayName
},
},
})
.then(m => m.present())
},
.
.
.
</script>
You can try this:
SET_USER(state, data) {
Vue.$set(state.user, 'data', data)
}