I'm using the Vuetify Autocomplete component to search through all stock symbols and company names (117230 in total). When I enter a search term, the browser becomes very laggy for a few seconds. When I tried it with few records (6000), there were no problems. The API is hosted locally for now.
I am assuming this job should be done by the backend? But I'm not sure how. What are my options?
Javascript code in the search component:
import Chart from './GChart.vue'
export default {
name: "Search",
components: {
Chart,
},
data: () => ({
symbolsExchangesNames: [],
isLoading: false,
model: null,
search: null
}),
computed: {
items () {
return this.symbolsExchangesNames
}
},
watch: {
search (val) {
console.log(val)
if (this.items.length > 0) return
if (this.isLoading) return
this.isLoading = true
fetch('http://localhost/API/allSymbolsExchangesNames')
.then(res => res.json())
.then(res => {
for(let i of res){
this.symbolsExchangesNames.push({
Code: i.Symbol,
Exchange: i.Exchange,
Name: i.Name,
CodeAndName: `${i.Symbol} ${i.Name}`
})
}
})
.catch(err => {
console.log(err)
})
.finally(() => (this.isLoading = false))
}
}
}
This is how the data looks like:
[{"Symbol": "A", "Exchange": "US", "Name": "Agilent Technologies, Inc"}, {"Symbol": "AA", "Exchange": "US", "Name": "Alcoa Corporation"},...]
117k records takes sometime to be rendered in HTML.
I'd suggest you to use some debounce function (in the example below I used underscore), to only query your backend when the user stops typing or you could use some real type of input, like pressing enter in the input or submitting the form.
And you should prevent the backend from returning so many records, so yeah, you should filter results in your backend, it's usually used query string on GET requests for this (https://www.google.com/search?q=text%20to%20search)
new Vue({
el: "#app",
data() {
return {
query: "",
data: []
}
},
methods: {
// with debounce, 300ms after user stops typing, the callback will be executed
search: _.debounce(function() {
// here you should query your backend, something like
// http://localhost/API/allSymbolsExchangesNames?query=#{this.query}
this.data = [1,2,3]
}, 300)
}
})
<script src="https://underscorejs.org/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="query" #keypress="search" />
{{data}}
</div>
Related
8.4 of react-admin. I've been trying to implement a custom action that connects with the custom reducer but so far nothing has worked.
I've Implemented this part of the guide in the official documentation for the action side https://marmelab.com/react-admin/doc/3.8/Actions.html#querying-the-api-with-fetch and this for the reducer https://marmelab.com/react-admin/doc/3.8/Admin.html#customreducers. The problem stems from that I can only use useUpdate method which sends update request, instead of a get without connecting to the reducer and there is no clear explanation of how I can chain those two things together. I also tried using an older way of dispatching actions, but still didn't work. Please help I've been trying this for 2 weeks now. Nothing gets updates and the redux store stays the same.
component
const { data, loading, error } = useQueryWithStore({
type: 'getList',
resource: 'goals',
action: "GET_USER_GOALS",
payload: { pagination: { page: 1, perPage: 10 }, sort: { field: "a-z", order: "ABC" }, filter: {} }
});
reducer
export default (previousState = 0, { type, payload }) => {
console.log(type)
if (type === 'GET_USER_GOALS') {
return payload.rate;
}
return previousState;
}
I even wrote a custom action
but it says that "Cannot read property 'update' of undefined" which isn't supported in the newer version I guess.
import { UPDATE } from 'react-admin';
export const UPDATE_PAGE = 'GET_USER_GOALS';
export const setGoals = (id, data) => {
return {
type: UPDATE_PAGE,
payload: { id, data: { ...data, is_updated: true } },
meta: { fetch: UPDATE, resource: 'goals' },
}
};
admin
<Admin
locale="en"
customReducers={{ userGoals: userGaolsReducer }}
loginPage={LoginPage}
authProvider={authProvider}
dataProvider={testProvider}
i18nProvider={i18nProvider}
history={history}
dashboard={Dashboard}
customSagas={[userGoalsSaga]}
>
I had to include it in the store.js as well
const reducer = combineReducers({
admin: adminReducer,
router: connectRouter(history),
userDashboardSettings: userGaolsReducer
});
I am fetching data from API inside the created method and i want to use these data in the page.
Here is my code.
created(){
let id = this.$route.params.id
let videos;
this.$axios.get(this.$axios.defaults.apiURL + 'v1.0.0/tips/' +id,).then((response) => {
this.videos = response.data.data;
}, (error) => {
toast.$toast.error('Something went wrong! Please try again', {
position: 'top'
})
});
},
data(){
let videos = this.videos;
return {
video: {
sources: [{
src: videos.video_url,
type: 'video/mp4'
}],
options: {
autoplay: true,
volume: 0.6,
poster: videos.thumbnail
}
}
}
}
I am getting error that thumbnail and video_url is not defined. This 2 values are coming from API response. How can i solve this? Thanks
I can see two obvious issues with your code (without seeing it in action):
created is a synchronous hook, but your axios request is returning a promise. Instead of waiting for the promise, you are immediately trying to show the result, hence the issue you are encountering - the data just hasn't arrived yet.
Your use of this seems a bit chaotic (i.e. let videos = this.videos - where would this.videos come from? The only other 'videos' is declared inside of a different function with let)
There are multiple ways to solve this, depending on what you want to show while you are fetching the data and what type of component this is - if you want to show a spinner while you are waiting for the request to be answered, or if you just want to show some progress bar on the previous page and only enter this one once it's loaded.
In-component loading
In the first case, I would suggest setting a variable or using a loader management solution like vue-wait. Your code could look like this then:
data() {
return {
loading: true,
videos: null,
}
},
computed: {
video() {
return this.videos ? {
sources: [{
src: this.videos.video_url,
type: 'video/mp4'
}],
options: {
autoplay: true,
volume: 0.6,
poster: this.videos.thumbnail
}
} : null
}
},
methods: {
fetch() {
let id = this.$route.params.id
this.$axios.get(this.$axios.defaults.apiURL + 'v1.0.0/tips/' + id, ).then((response) => {
this.videos = response.data.data;
}, (error) => {
toast.$toast.error('Something went wrong! Please try again', {
position: 'top'
})
}).finally(() => (this.loading = false));
},
},
created() {
this.fetch()
},
In your template, you would add somewhere v-if=!loading to make sure that the request has finished before you try to show something
Data-fetching before entering page
If this is a page though, you could request the data in beforeRouteEnter - there's a whole article that explains the principle on the vue site
im really going through hard times trying to figure out how to get my API data through Vuex, is there some body whom has accurate bibliography of how to do this step by step, or even better help me with this code?
Formerly without using Vuex , but Vue all request worked perfectly, but now i dont understand clearly what i should do, here sharing part of my code:
data() {
return {
testArray: []
};
methods: {
getJsonData() {
fetch(
"https://app.ticketmaster.com/discovery/v2/events.json?countryCode=" +
this.countriesDrop +
"&apikey=xxxxxxxxxxxxxxxxxxxxxxxx",
{
method: "GET"
}
)
.then(response => {
return response.json();
})
.then(test => {console.log(this.testArray)
this.testArray = test._embedded.events;
})
.catch(err => {
console.log(err);
});
},
watch: {
countriesDrop: function(val) {
this.getJsonData();
}
},
As you can see in the request also is included an external element which make it changes attuning with the watcher and the value the user might asign.
I already got set Vuex and all else pluggins...just dont know how to act like , thus would appreciate an accurate link or tutorial either help with this basic problem resolved on detail step by step, .....thanks!
In your code there's nothing with Vuex. I guessed you want to set the state so that the getJsonData() method is called according to what's in the store.
Here's a snippet as an example of handling async in a Vuex environment.
const store = new Vuex.Store({
state: {
testArray: []
},
mutations: {
setTestArray(state, data) {
state.testArray = data
}
},
actions: {
getJsonData({
commit
}, countriesDrop) {
if (countriesDrop && countriesDrop !== '') {
fetch(`https://jsonplaceholder.typicode.com/${countriesDrop}`, {
method: "GET"
})
.then(response => {
return response.json();
})
.then(json => {
commit('setTestArray', json)
})
.catch(err => {
console.log(err);
});
}
}
}
})
new Vue({
el: "#app",
store,
computed: {
getDataFromStore() {
return this.$store.state.testArray
}
},
methods: {
getData(countriesDrop) {
this.$store.dispatch('getJsonData', countriesDrop)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/es6-promise#4/dist/es6-promise.auto.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="getData('todos')">GET TODOS</button>
<button #click="getData('albums')">GET ALBUMS</button>
<ol>
<li v-for="data in getDataFromStore">{{data.title}}</li>
</ol>
</div>
The point is that Vuex is a central element in a Vue-Vuex application. You can store app state, handle async and sync functions (actions, mutations) with it, and all your Vue components can rely on the state - that should be the "single source of truth".
So, you get your input from a component (the Vue instance in this snippet), and dispatch an action that is available in the Vuex store. If the action needs to modify the state, then you call a mutation to do that. With this flow you keep reactivity for all your components that use that state.
I used a computed to get data from the Vuex store, but getters can be set also.
This way you don't "pollute" your components with functions and data that should be in the store.
I am trying to add a Google Sign-In button to my Vue.js application and I found the vue-google-oauth2 plugin. I installed it and followed exactly the sample.html code to integrate it in my application, this way:
<template>
<div>
<h1>Test</h1>
<button #click="handleClickSignIn" :disabled="!isLoaded">signIn</button>
</div>
</template>
<script>
/**
* You should first need to place these 2 lines of code in your APP ENTRY file, e.g. src/main.js
*
* import GAuth from 'vue-google-oauth2'
* Vue.use(GAuth, {clientId: '4584XXXXXXXX-2gqknkvdjfkdfkvb8uja2k65sldsms7qo9.apps.googleusercontent.com'})
*
*/
export default {
name: 'test',
props: [],
components: {
},
data () {
return {
isLoaded: false
}
},
computed: {
},
methods: {
handleClickSignIn(){
this.$gAuth.signIn(function (user) {
//on success do something
console.log('user', user)
}, function (error) {
//on fail do something
})
}
},
mounted(){
let that = this
let checkGauthLoad = setInterval(function(){
that.isLoaded = that.$gAuth.isLoaded()
console.log('checked', that.isLoaded)
if(that.isLoaded) clearInterval(checkGauthLoad)
}, 1000);
}
}
</script>
The problem is that the isLoaded() method never returns true, with the Google Chrome console telling me every time I press on the button that the google api is not ready, that is the plugin console message printed when the GoogleAuthInstance is false. Could anyone help me?
Use isInit instead of isLoaded as the latter will be/is deprecated.
Add to main.js
import GAuth from 'vue-google-oauth2'
Vue.use(GAuth, {
clientId: '....apps.googleusercontent.com',
scope: 'email',
prompt: 'consent',
fetch_basic_profile: true
})
new Vue({
...
render: (h) => h(App),
}).$mount("#app");
I am trying to implement a vote button with vue js, when user click "Vote" will send axios request to server and store the data, then return json back. Same with unvote.
I also check if the user is voted, the button should change to Unvote like facebook.
So far the vote and unvote button is work correctly.
But i found a problems which is the voted features is not working. If user voted, after refresh page it will change back to "Vote", but it should be Unvote. But if the button was clicked, in database will showing the vote was deleted. Mean it should be problems of computed. But i am struggle on it since i not really know vue js.
This is my vue components.
<template>
<a href="#" v-if="isLiked" #click.prevent="unlike(comment)">
<span>UnVote</span>
</a>
<a href="#" v-else #click.prevent="like(comment)">
<span>Vote</span>
</a>
<script>
export default{
props: ['comment','liked'],
data: function() {
return {
isLiked: '',
}
},
mounted() {
axios.get('/comment/'+ this.comment.id +'/check', {})
.then((response) => {
this.liked = response.data;//here will get json "true" or "false"
});
this.isLiked = this.liked ? true : false;
},
computed: {
isLike() {
return this.liked;
},
},
methods: {
like(comment) {
axios.post('/comment/'+ comment.id +'/like')
.then(response => this.isLiked = true)
.catch(response => console.log(response.data));
},
unlike(comment) {
axios.post('/comment/'+ comment.id +'/unlike')
.then(response => this.isLiked = false)
.catch(response => console.log(response.data));
},
}
}
Your component instance does not have a liked data property and you should not attempt to set prop values (see https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow)
Also, you are attempting to set the isLiked value outside of the asynchronous operation which will not work how you think.
Just set the isLiked property...
mounted() {
axios.get('/comment/'+ this.comment.id +'/check', {})
.then((response) => {
this.isLiked = response.data; //here will get json "true" or "false"
});
},
Your isLike computed property is also never used.