VueJS 3 - substr / truncation in template / v-for? - javascript

I'm pretty new to VueJS and have an understanding problem i couldn't find any help for.
Its pretty simple: I'm getting a JSON through an API with axios. This item contains a description that I want to output on the page.
My code looks something like this:
<template>
<div v-for="item in listitems" :key="item.id">
{{ item.description }}
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import axios from 'axios'
export default defineComponent({
name: 'AllCoupons',
components: {
},
data: function() {
return {
listitems :[]
}
},
mounted: function() {
axios.get('https://api.com/endpoint',
{
headers: {
'Accept': 'application/json'
}
}).then((response) => {
console.log(response);
this.listitems = response.data.data
}).catch(error => {
console.log("ERRR:: ", error.response.data)
});
}
});
</script>
It works fine so far. The problem is that the item.description has too many characters, which I'd like to limit with something like substr. What is the right / best way to do something like this in vue?
I thought about adding a custom function in methods which will be run after the api fetched the data to iterate trough the data and make the modifications then, before passing it back to this.listitems. - But is there a way to do something like this in the template: ?
{{ item.description.substring(1, 4); }}
I knew something like this was possible with Vue 2 and filters if I'm right... But how can I do something like this in Vue 3?
Thanks a lot!!

As suggested in migration guide, you could use a computed property like :
data: function() {
return {
listitems :[]
}
},
computed:{
customItems(){
return this.listitems.map(item=>{
return {...item, description:item.description.substring(1, 4)}
}
}
}
then render that computed property :
<div v-for="item in customItems" :key="item.id">
{{ item.description }}
</div>

Related

Algolia vue instantsearch dynamically set search query

I have a search bar on the root component (App.vue). I want to enter a query and on #keyup.enter, it should redirect to the Search component view with the v-text-field input value. Redirection is used by using $router.replace because users might search for a different keyword from within the same route.
The code below work but only ONCE. If I enter a new search term, the URL changed but the results stay the same.
App.vue
<template>
<div>
<header>
<v-text-field #keyup.enter="goToSearchPage($event)"></v-text-field>
</header>
<v-main>
<router-view></router-view>
</v-main>
</div>
</template>
<script>
export default {
methods: {
goToSearchPage($event) {
this.$router.replace({
name: "Search",
query: { q: $event.target.value }
});
}
}
};
</script>
views/Search.vue
<template>
<div>
<ais-instant-search
index-name="dev_brunjar_products"
:search-client="searchClient"
:search-function="searchFunction"
>
<ais-hits>
<ul slot-scope="{ items }">
<li v-for="item in items" :key="item.objectID">
{{ item.name }}
</li>
</ul>
</ais-hits>
</ais-instant-search>
</div>
</template>
<script>
import algoliasearch from "algoliasearch/lite";
export default {
data() {
return {
searchClient: algoliasearch(
process.env.VUE_APP_ALGOLIA_APP_ID,
process.env.VUE_APP_ALGOLIA_SEARCH_KEY
)
};
},
methods: {
// According to Algolia's doc, this should be inside data instead of methods
// https://www.algolia.com/doc/api-reference/widgets/instantsearch/vue/#widget-param-search-function
// But doing so, I wouldn't be able to get this.$route.query.q
searchFunction(helper) {
var query = this.$route.query.q;
if (query) {
helper.setQuery(query).search();
}
}
}
};
</script>
What I've tried
Did a hack-ish way (Test 1) to solve it but didn't work (which I'm glad, because it doesn't feel right). Below was the non-working code addition to the Search component. Created computed & watch property of query which get its data from this.$route.query.q and algoliaHelper data assigned with AlgoliaSearchHelper when the searchFunction first load.
When I typed a new search term, the watcher works and the query indeed changed. Despite that, calling the helper and setting its query with the new term within the watcher did not change the results from Algolia.
Then I used Routing URLs (Test 2) to the ais-instant-search and it still didn't solve the issue. Maybe I'm implementing it wrong? I really tried to understand Algolia's doc and it's just too hard to digest.
views/Search.vue - Test 1 (Failed)
<template>
<div>
<ais-instant-search
index-name="dev_brunjar_products"
:search-client="searchClient"
:search-function="searchFunction"
>
<ais-hits>
<ul slot-scope="{ items }">
<li v-for="item in items" :key="item.objectID">
{{ item.name }}
</li>
</ul>
</ais-hits>
</ais-instant-search>
</div>
</template>
<script>
import algoliasearch from "algoliasearch/lite";
export default {
data() {
return {
searchClient: algoliasearch(
process.env.VUE_APP_ALGOLIA_APP_ID,
process.env.VUE_APP_ALGOLIA_SEARCH_KEY
),
algoliaHelper: null
};
},
computed: {
query() {
return this.$route.query.q;
}
},
watch: {
query(newQuery) {
this.algoliaHelper.setQuery(newQuery).search();
}
},
methods: {
searchFunction(helper) {
if (!this.algoliaHelper) {
this.algoliaHelper = helper;
}
if (this.query) {
helper.setQuery(this.query).search();
}
}
}
};
</script>
views/Search.vue - Test 2 (Failed)
<template>
<div>
<ais-instant-search
index-name="dev_brunjar_products"
:search-client="searchClient"
:search-function="searchFunction"
:routing="routing"
>
<ais-hits>
<ul slot-scope="{ items }">
<li v-for="item in items" :key="item.objectID">
{{ item.name }}
</li>
</ul>
</ais-hits>
</ais-instant-search>
</div>
</template>
<script>
import { history as historyRouter } from "instantsearch.js/es/lib/routers";
import { singleIndex as singleIndexMapping } from "instantsearch.js/es/lib/stateMappings";
import algoliasearch from "algoliasearch/lite";
export default {
data() {
return {
searchClient: algoliasearch(
process.env.VUE_APP_ALGOLIA_APP_ID,
process.env.VUE_APP_ALGOLIA_SEARCH_KEY
),
routing: {
router: historyRouter(),
stateMapping: singleIndexMapping("instant_search")
}
};
},
methods: {
searchFunction(helper) {
if (this.query) {
helper.setQuery(this.query).search();
}
}
}
};
</script>
I would appreciate it if you guys know how to solve this issue.
https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/routing-vue-router?file=/src/views/Home.vue
This is an example using vue router. I guess this might be what you're looking for.
Please let us know if it works for you.
Hope you where able to solve this since it's been a long time since you asked. But, in order to make it work you have to put searchFunction(helper) inside data() as shown in the docs: https://www.algolia.com/doc/api-reference/widgets/instantsearch/vue/#widget-param-search-function

Vuex store value not set in mounted

I have had problems where I am getting a null value from my store... sometimes, and only on some values. If anyone could point me in the right direction and explain why it is wrong... I would be really grateful. So here is the deal my store.getters.getApiKey is sometimes "" and sometimes not.
So... in the component vue below, it is not null on the first reference
{{this.$store.getters.getApiKey}}
and then within the mounted section, store.getters.getHostUrl is set, but store.getters.getApiKey keeps returning "".
Here are the details:
the Component.vue
<template>
<div class="countryCodes">
<p>ApiKey : {{this.$store.getters.getApiKey}}</p>
<p>CountryCode Data is {{ page }}</p>
<div class="CountryCodes">
<tr v-for="ccdata in content_list" v-bind:key="ccdata.guid">
<td>{{ ccdata.guid }}</td>
<td>{{ ccdata.name }}</td>
<td>{{ ccdata.code }}</td>
</tr>
</div>
</div>
</template>
import axios from "axios";
import store from "#/store";
export default {
name: 'CountryCodes',
data () {
return {
page: "",
content_list: []
}
},
mounted () {
axios({ method: "GET", "url": store.getters.getHostUrl + "
"/api/"+store.getters.getApiKey+"/countryCodes" }).then(result => {
this.page = result.data.page;
this.content_list = result.data.contents;
}, error => {
console.error(error);
});
},
methods: {
}
}
</script>
Then my store (store.js) looks like this...
import Vuex from "vuex";
import Vue from 'vue';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
apiKey: "",
hostUrl:""
},
mutations: {
SET_APIKEY(state, value) { state.apiKey = value; },
SET_HOST_URL(state, value) {state.hostUrl = value; }
},
getters: {
getApiKey(state) { return state.apiKey; },
getHostUrl(state) { return state.hostUrl; }
}
})
finally in my main.js I commit the data to the store... :
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
import store from './store.js'
new Vue({
el: '#app',
render: h => h(App),
router,
store,
mounted: function() {
console.log(this.$route.query)
store.commit('SET_APIKEY', this.$route.query.api)
store.commit("SET_HOST_URL", location.origin.toString())
}
})
I have the same problem when trying to build a http service, where the store is null for the apikey. What magic am I missing?
Usually the mounted hook of child component is called before the mounted of parent component.
From Vue Parent and Child lifecycle hooks
If you try to console.log on both mounted hook you will see the order of execution (but I'm still not sure why your store.getters.getHostUrl is set).
So you need a watcher to run your code after your store has value.
Example code:
...
computed: { // or use another getters
url () {
if (!store.getters.getHostUrl || !store.getters.getApiKey) return
return `${store.getters.getHostUrl}/api/${store.getters.getApiKey}/countryCodes`
}
},
watch: {
url (value) {
...
}
}
...
CodeSandbox
So.... there were two ways of solving this..... thank you to both comments.
Switch my mounted in the main.js to created- the diagram above explains why... as well as the nice article.
add "await store.getters.getApiKey"

VUE.JS after parsing trough the JSON getting wrong output

I did [GET] method using Axios. Everything is working fine, when I want to output i get this kind of thing:
http://prntscr.com/mpey70
This is my JS with HTML and VUE code on how I am trying to output it:
HTML, VUE:
<div class="col-lg-6">
<p>Casuals</p>
<ul>
<div v-bind:key="realsub.id+1" v-for="realsub in subnavreal">
<div v-if="nav.linkTitle == 'Male'">
<li><router-link :to="{ path: whiteSpace(realsub.male.casual) }">{{JSON.realsub.male.casual}}</router-link></li>
</div>
<div v-if="nav.linkTitle == 'Female'"></div>
<li><router-link :to="{ path: whiteSpace(realsub.female.casual) }">{{realsub.female.casual}}</router-link></li>
</div>
</ul>
</div>
And this is Related JS code:
import axios from 'axios';
import uuid from 'uuid';
export default {
name: 'navigation',
data(){
return{
subnavreal: []
}
},
props: ["navigation"],
methods:{
whiteSpace(a){
console.log(a);
}
},
async created(){
axios.get('/products.json')
.then(res => this.subnavreal = res.data)
.catch(err => console.log(err));
}
}
</script>
What I want to display is only the name of that object, for example: "Hoodies"
Any solutions? :)
I think you are printing the entire Response. You can use the Object.keys() to print the keys.
let user = {
name: "tom",
age: 20
}
If you want to print the keys [name, age] use Object.keys(user)

VueJS method not returning response from Axios

Trying to figure this out for awhile now and I know it is something simple but can not seem to figure out the issue. My output is {} when I am trying to return a Axios response. When I do not return the entire axios.post my output is nothing. But when I console.log the data it shows up fine in the console. So I know I am getting the data correctly. Below is my test code. Not sure what I am doing wrong, if anyone has a idea it would be greatly appreciated.
<template>
<div>
{{ fetch_name('my_name') }}
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
}
},
methods: {
fetch_name(name) {
return axios.post('example.com/api/names', {custom: 'SELECT name FROM `names` WHERE name = "'+name+'"'}).then(response => {
console.log(response.data[0].name)
return response.data[0].name
})
}
}
}
</script>
Solution for your case:
<template>
<div>
{{ fetch_name('my_name') && result }}
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
result: 'Loading...',
}
},
methods: {
fetch_name(name) {
return axios.post('example.com/api/names', {custom: 'SELECT name FROM `names` WHERE name = "'+name+'"'}).then(response => {
console.log(response.data[0].name)
this.result = response.data[0].name;
return response.data[0].name
})
}
}
}
</script>
But I think better modify logic something like this:
Solution for with control name in UI:
<template>
<div>
<input v-model="name">
{{ result }}
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
name: null,
result: 'Wait for typing',
}
},
watch: {
name(name) {
this.result = 'Loading...';
axios.post('example.com/api/names', {custom: 'SELECT name FROM `names` WHERE name = "'+name+'"'})
.then(response => {
this.result = response.data[0].name;
return response.data[0].name
})
}
},
}
</script>
return response.data[0].name
isn't returning from fetch_name fetch name returns as soon as it does the post....
what you need to do is put the response into your data, then bind to your data, not the method. When the response to post comes in, it will then trigger the UI update.
Also, sending SQL from the client side seems super dangerous.

Json Array object access

I want some help in getting data form json array file is in the link
Html
<div>
<div v-for="data in myJson.id " >{{ data }}</div>
</div>
js
import json from '.././json/data.json'
export default {
components: {
MainLayout,
},
data: function(){
return {
myJson:json
}
},
method:{
getjson:function(){
this.json = JSON.parse(myJson);
}
}
}
i want to access only the data with some specific id and i cannot access it using the syntax i am using
edit
Json file
Apparently, you do not even need JSON.parse. It seems to work without it... Put your JSON file in the same directory as your component and try this:
import data from './data.json'
export default {
created () {
for (const item of data[0]['file']) {
console.log(`
Name: ${item.name}
Type: ${item.type}
Size: ${item.filesize}
Dimensions: ${item.dimension[0].width}x${item.dimension[0].height}
`)
}
}
}
You should see information from your JSON file in your console when the page loads.
<script>
import MainLayout from '../layouts/Main.vue'
import json from '.././json/data.json'
export default {
components: {
MainLayout,
},
data: function(){
return {
myJson: json[0].file
}
},
method:{
}
}
</script>
html
<div v-for="data in myJson">
{{ data.name }}
{{ data.filesize}}
{{ data.dimension[0].width}}x{{data.dimension[0].height}}
</div>
using the above code i utilized and used to implemented according to my needs and it worked

Categories