So I'm fairly new to Vue and I'm trying to make a customer list search work with Fuse.js.
I do get the array of customers back and it's being assigned to customer_search. my keys are populated properly and the only issue is that results doesn't return anything. I'm wondering if I need to structure my customer array differently or am I missing something else altogether?
Any help would be appreciated.
Here is my code:
<template>
<div>
<div class="container">
<h1>Search</h1>
<input type="text" class="input-search" value="" v-model="query">
<p v-html="results"></p>
<p v-for="info in data" >{{info}}</p>
</div>
</div>
</template>
<script>
import Fuse from 'fuse.js'
import $ from 'jquery'
import PageService from '../../common/services/PageService'
const Search = {
data(){
return {
data: {},
fuse: {},
results: {},
query: '',
options: {
keys: [
'id',
'name',
'company',
],
minMatchCharLength: 3,
shouldSort: true,
threshold: 0.5
},
}
},
methods:{
runQuery(query){
if(query.length >= 3)
this.results = this.fuse.search(query)
},
},
computed:{
customers: function(){
return this.data
},
customer_search: function(){
return Object.values(this.data)
},
},
watch: {
query: function(){
this.runQuery(this.query)
}
},
created(){
this.fuse = new Fuse(this.customer_search, this.options)
if(this.$store.state.search != ''){
this.query = this.$store.state.search
}
PageService.getSearchObject().then((response)=>{
this.data = response.data
}).catch((err)=>{
console.log('Error')
});
},
}
export default Search
</script>
I think your runQuery method is created before your this.fuse get created so the this.fuse inside your runQuery method is not up-to-date.
Maybe try:
methods:{
runQuery(query){
if(query.length >= 3)
this.results = new Fuse(this.customer_search, this.options).search(query)
},
},
Related
On top of How to watch only after the initial load from API in VueJS?, I wanted to detect any changes in values of the properties in the json object.
Initially the user object is
user: {
userId: 0,
id: 0,
title: "",
completed: false,
},
I have two input fields,
<input type="text" v-model="user.userId" /> <br />
<input type="text" v-model="user.title" /> <br />
and a button <button :disabled="isLoaded">Update</button>
If none of the input values changed, the button should be still disabled. Example, if the userId is changed to 1, the button should be enabled but if the value is changed back to 0, the button should be disabled. I referred Vue js compare 2 object and remove differences in watcher and I tried following but failed.
<template>
<div id="app">
<div v-if="!isFetching">
<input type="text" v-model="user.userId" /> <br />
<br />
<input type="text" v-model="user.title" /> <br />
<br />
<button :disabled="isLoaded">Update</button>
</div>
<div v-else>Loading...</div>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
user: {
userId: 0,
id: 0,
title: "",
completed: false,
},
isFetching: false,
isLoaded: true,
};
},
watch: {
user: {
handler(oldVal, newVal) {
this.checkObject(oldVal, newVal);
},
deep: true,
},
},
methods: {
checkObject: (obj1, obj2) => {
const isEqual = (...objects) =>
objects.every(
(obj) => JSON.stringify(obj) === JSON.stringify(objects[0])
);
console.log(obj1, obj2);
console.log(isEqual(obj1, obj2));
},
},
created() {
this.isFetching = true;
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => {
this.user = json;
this.isLoaded = true;
})
.finally(() => (this.isFetching = false));
},
};
</script>
Here's a live demo: https://codesandbox.io/embed/friendly-hopper-ynhxc?fontsize=14&hidenavigation=1&theme=dark
Here is one way you could solve this. So below I'm storing two user objects, one is my base line comparison compareUser, and the other is the user that is under edit. When something changes which the deep watch on user will notify me about, I use a utility function like isEqual from lodash to perform a semantic comparison of the base line object and the current user object, and see if there are any differences.
If I want to update my base line that I'm comparing to, then I update the compareUser from the current user by cloning it.
You can of course replace things like isEqual and cloneDeep by rolling your own to avoid the extra library if that's an issue.
<script>
import { isEqual, cloneDeep } from "lodash";
const createDefault = function () {
return {
userId: 0,
id: 0,
title: "",
completed: false,
};
};
export default {
name: "App",
data() {
return {
compareUser: createDefault(),
user: createDefault(),
isLoaded: false,
isDifferent: false,
};
},
watch: {
user: {
handler() {
this.isDifferent = !isEqual(this.user, this.compareUser);
},
deep: true,
},
},
methods: {
setCompareUser(user) {
this.compareUser = cloneDeep(user);
this.isDifferent = false;
},
},
async created() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const user = await response.json();
this.user = user;
this.setCompareUser(user);
this.isLoaded = true;
},
};
</script>
Demo:
https://codesandbox.io/s/modern-tdd-yg6c1
I have issue to get some values from methods and want to parse to provide.
How I can solve the problem?
methods: {
onClickCategory: (value) => {
return (this.catId = value);
},
},
provide() {
return {
categoryId: this.value,
};
},
I get always categoryId:undefined
I found solution:
methods: {
onClickCategory(value) {
this.categoryId.value = value;
},
},
provide() {
this.catID = this.categoryId;
return {
catId: this.catID,
};
},
As Vue Guide highlights,
Note: the provide and inject bindings are NOT reactive. This is
intentional. However, if you pass down an observed object, properties
on that object do remain reactive.
So one solution is wrap your value into one observed object, like test2.value in below example:
Vue.config.productionTip = false
Vue.component('v-parent', {template: `
<div>
<h4>Example</h4>
<p>Not Working: <input v-model="test1"></p>
<p>Working: <input v-model="test2.value"></p>
<v-child></v-child>
</div>
`,
data () {
return {
test1: 'blabla1',
test2: {value: 'blabla2'}
}
},
provide () {
return {parent1: this.test1, parent2: this.test2}
}
}),
Vue.component('v-child', {
template: `<div><pre>{{parent1}}</pre><pre>{{parent2.value}}</pre></div>`,
inject: ['parent1', 'parent2']
})
new Vue({
el: '#app',
data() {
return {
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<v-parent/>
</div>
</div>
I'm using Bootstrap vue table with contentful's API and could use some help with my code. I'm attempting to use a for loop to iterate over an array and get the property values. The console.info(episodes); call prints out each iteration for the var episodes, but now how do I bind this to my variable episodes. Using return only returns one result even outside of the for each loop. Any help or suggestions on another implementation is greatly appreciated. Full Template below.
<template>
<div>
<h1>Bootstrap Table</h1>
<b-table striped responsive hover :items="episodes" :fields="fields"></b-table>
</div>
</template>
<style>
</style>
<script>
import axios from "axios";
// Config
import config from "config";
// Vuex
import store from "store";
import { mapGetters, mapActions } from "vuex";
// Services
import { formatEntry } from "services/contentful";
// Models
import { entryTypes } from "models/contentful";
// UI
import UiEntry from "ui/Entry";
import UiLatestEntries from "ui/LatestEntries";
const contentful = require("contentful");
const client = contentful.createClient({
space: "xxxx",
environment: "staging", // defaults to 'master' if not set
accessToken: "xxxx"
});
export default {
name: "contentful-table",
data() {
return {
fields: [
{
key: "category",
sortable: true
},
{
key: "episode_name",
sortable: true
},
{
key: "episode_acronym",
sortable: true
},
{
key: "version",
sortable: true
}
],
episodes: []
};
},
mounted() {
return Promise.all([
// fetch the owner of the blog
client.getEntries({
content_type: "entryWebinar",
select: "fields.title,fields.description,fields.body,fields.splash"
})
])
.then(response => {
// console.info(response[0].items);
return response[0].items;
})
.then(response => {
this.episodes = function() {
var arrayLength = response.length;
var episodes = [];
for (let i = 0; i < arrayLength; i++) {
// console.info(response[i].fields.title + response[i].fields.splash + response[i].fields.description + response[i].fields.body );
var episodes = [
{
category: response[i].fields.title,
episode_name: response[i].fields.splash,
episode_acronym: response[i].fields.description,
version: response[i].fields.body
}
];
// episodes.forEach(category => episodes.push(category));
console.info(episodes);
}
return episodes;
};
})
.catch(console.error);
}
};
</script>
You can use the map method on the response array to return all the elements.
In your current example you keep re-setting the episodes variable, instead of the push() you actually want to do. The map method is still a more elegant way to solve your problem.
this.episodes = response.map((item) => {
return {
category: item.fields.title,
episode_name: items.fields.splash,
episode_acronym: item.fields.description,
version: item.fields.body
}
})
You can update the last then to match the last then below
]).then(response => {
return response[0].items;
})
.then((response) => {
this.episodes = response.map((item) => {
return {
category: item.fields.title,
episode_name: items.fields.splash,
episode_acronym: item.fields.description,
version: item.fields.body
};
});
})
.catch(console.error)
You do have an unnecessary second then, but I left it there so that you could see what I am replacing.
I have the following code (below) that lets a user search data in an array. I want to replace the data property with data from an api and I don't know the proper way to structure a vue.js app so the methods have access to ajax data that is called on page load.
I know I use the axios library to call the data.
Vue.axios.get('https://jsonplaceholder.typicode.com/posts/1').then((response) => {
console.log(response.data)
})
MY CODE
https://jsfiddle.net/bny191f7/1/
Vue.js code
new Vue({
el: '#app',
data: {
searchString: "",
users: [{ //____________I want to replace this data with api data
"name": "Bob"
},
{
"name": "Angel"
},
{
"name": "Whatever"
}
]
},
computed: {
filterUsers: function() { //___________And insure this has access to it
//___________so the app continues to work
var users_array = this.users,
searchString = this.searchString;
if (!searchString) {
return users_array;
}
searchString = searchString.trim().toLowerCase();
users_array = users_array.filter(function(item) {
if (item.name.toLowerCase().indexOf(searchString) !== -1) {
return item;
}
})
return users_array;;
}
}
});
HTML
<form id="app" v-cloak>
<input type="text" v-model="searchString" placeholder="Enter your search terms" />
<ul>
<li v-for="user in filterUsers">
<p>{{user.name}}</p>
</li>
</ul>
I figured it out.
VUE
new Vue({
el: '#app',
data: {
searchString: "",
users: undefined
},
mounted: function () {
Vue.axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
console.log(response);
this.users = response.data;
console.log(this.users);
})
.catch(function (error) {
console.log(error);
});
},
computed: {
filterUsers: function () {
var users_array = this.users,
searchString = this.searchString;
if(!searchString){
return users_array;
}
searchString = searchString.trim().toLowerCase();
users_array = users_array.filter(function(item){
if(item.title.toLowerCase().indexOf(searchString) !== -1){
return item;
}
})
return users_array;;
}
}
});
HTML
<form id="app" v-cloak>
<input type="text" v-model="searchString" placeholder="Enter your search terms" />
<ul>
<li v-for="user in filterUsers">
<p>{{user.title}}</p>
</li>
</ul>
</form>
I am trying to create a filtering for a Stapes object in RactiveJS, but it seem the two way binding is not responding correctly to the update.
I can't work out what is going wrong where as I thought it should just work without any issue, I have created a code example here:
https://jsbin.com/sajeje/4/edit?html,output
Here's the pattern I use for using a computed property with a filter adapted for your use case. I couldn't get the Stapes object to directly be usable as the result, but there is a getAllAsArray() that seems to work:
computed: {
filtered: function () {
var query = this.get( 'query').trim(),
users = this.get( 'users' );
return query ? users.search(query) : users.getAllAsArray();
}
},
data: function () {
return {
users: usersModel,
query: ''
}
}
Works with the sub-component as pure reactive programming without the need for an event:
<Search query="{{query}}" placeholder="Search users" />
Updated bin here: https://jsbin.com/bomumajagu/edit?html,output
Push works against Stapes model:
this.get( 'users' ).push( { name: newUser } );
Keep your users array. Your filtered result is derived from your search query and the users array. Use computed properties for this. Essentially, the search result is another array.
Here's an example using POJO, not Stapes, but same idea: http://jsfiddle.net/561fj33a/
HTML:
<script type="template/ractive" id="template-users-list">
<div>
<input type="text" value="{{ query }}">
</div>
<ul>
{{# usersList }}
<li>{{ name }}</li>
{{/}}
</ul>
</script>
JS:
var UsersList = Ractive.extend({
template: '#template-users-list',
data: {
query: '',
users: []
},
computed: {
usersList: function () {
var query = this.get('query');
var users = this.get('users');
return !query ? users : users.filter(function (user) {
return ~user.name.toLowerCase().indexOf(query);
});
}
}
});
var usersList = new UsersList({
el: 'body',
append: true
});
setTimeout(function () {
usersList.set('users', [{
name: 'James'
}, {
name: 'John'
}, {
name: 'Jenny'
}, {
name: 'George'
}, {
name: 'Harry'
}]);
}, 2000);