How can i get properties from template - vue.js

Can i get properties from template before send request to server about the data?
Here's my template:
<box-component inline-template>
<div>
<div class="loading" v-if="Loading"> Loading ... </div>
<div v-if="Result">
<ul>
<li> {{ Result.FirstProp }} </li>
<li> {{ Result.FourthProp }} </li>
</ul>
</div>
</div>
And this is my component:
let BoxComponent = {
data() {
return {
Result: null,
Loading: true
};
},
created: function () {
this.Update();
},
methods: {
Update: function () {
this.Loading = true;
axios.post("#SERVER_URL#")
.then(response => {
this.Loading = false;
this.Result = response.data;
});
}
}
}
Vue.component('box-component', BoxComponent);
It's works fine! The problem is the server response contain much more data. Server response:
{
"FirstProp": "first",
"SecondProp": "second"
...
"HundredthProp": "hudredth"
}
I can't modify the response (other project use this too). But if i am able to send property list from the template (which are in this case FirstProp and FourthProp) the server side give me the filtered response which contains only these properties becouse the optimal response look like this:
{
"FirstProp": "first",
"FourthProp": "fourth"
}
So, the question is: how can i do that?
Can i find it in the Vue object?
I can load template as a string variable, but i want to avoid regex hack.
Update:
In this case, BoxTemplate call server side without "filters"
This is the optimal case, when js get the variables, that the template use, and call with them the server side. In this case, in the response there are only that variables, what template really use

I don't know how is set up your back-end but you can have additional property within your data:
data() {
return {
listOfProperty: [
'FirstProp',
'FourthProp',
],
...
And use list of it to send data to server.

Related

AlpineJS async call to render array of objects

Having the following snippet trying to fill an array of object with async alpine call but I can not get any result. Hier is what I try.
HTML:
<div x-init="experts.retrieveList" x-data="experts.list">
<ul>
<template x-for="item in experts.list" :key="item">
<li>
<div x-text="await item.address" ></div>
</li>
</template>
</ul>
</div>
external JS file
window.experts = {
apiUrl: 'http://bdb.local:8991/api/',
data: [],
list: [],
expertsForm: null,
retrieveList: () => {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`;
experts.apiCalls(membersUrl)
},
filterByParams: () => {
},
apiCalls: async (url) => {
let response = await fetch(url);
experts.list = await response.json()
return experts.list;
},
}
What is wrong in this case?
There are a few errors in this code:
You cannot use just a part of an Alpine.js component in the x-data="experts.list". If you don't define data variables directly in x-data, then it must be a real component that returns data and methods.
You cannot use an object as the key. It must be a string or number, like item.id or something like this.
The x-text="await item.address" seems incorrect. item.address should be a string, that has been already downloaded from the API.
In the component you need to use the this. prefix to access properties and methods.
Assuming your API returns the correct data format, something like this should work:
<div x-data="experts">
<ul>
<template x-for="item in list" :key="item.id">
<li>
<div x-text="item.address"></div>
</li>
</template>
</ul>
</div>
And the component in an external file:
const experts = {
apiUrl: 'http://bdb.local:8991/api/',
data: [],
list: [],
expertsForm: null,
init() {
const membersUrl = `${experts.apiUrl}members?include=user,association,affiliate`
this.apiCalls(membersUrl)
},
filterByParams() {
},
async apiCalls(url) {
let response = await fetch(url)
this.list = await response.json()
},
}
The init() method is executed automatically by Alpine.js.

How do I dynamically render an array from an api call in Vue?

I'm trying to make an autocomplete dropdown menu in vue and can't get the api response to render on the page. I'm making an api call on every keystroke in the input. You can see in the handleOnChange method that I'm trying to set the response to the results variable that is binding to the list above.
When I console log the results right after I make the api call AND set it to the data binding variable it logs as if everything is working fine. However, it does not render the actual data on the screen.
Here is my code
<template>
<div>
<input type="text" value="" v-model="address" placeholder="Start typing an address..." #input="methods.handleOnChange(address)"/>
<ul v-if="results.length !== 0">
<li v-for="result in results">
{{ result.streetLine || ""}}
</li>
</ul>
<p v-model="results">
{{results}}
</p>
</div>
</template>
<script>
const SmartyStreetsSDK = require("smartystreets-javascript-sdk");
const SmartyStreetsCore = SmartyStreetsSDK.core;
const Lookup = SmartyStreetsSDK.usAutocompletePro.Lookup;
const credentials = new SmartyStreetsCore.SharedCredentials([website-key]);
const clientBuilder = new SmartyStreetsCore.ClientBuilder(credentials).withLicenses(["us-autocomplete-pro-cloud"]).withBaseUrl("https://us-autocomplete-pro.api.smartystreets.me/lookup");
const client = clientBuilder.buildUsAutocompleteProClient();
export default {
name: 'Autocomplete',
data () {
return {
address: "",
results: [{streetLine: "testing"}],
methods: {
handleOnChange: function(address) {
//TODO: need to be able to access "results" from in here
console.log("this: ", this);
if (address) {
const lookup = new Lookup(address);
client.send(lookup).then((response) => {
console.log(response.result);
this.results = response.result;
console.log("databinding: ", this.results);
}).catch(console.log)
}
}
}
}
}
}
</script>
Vue.set()
As discussed in the comments, Vue.set was able to do it.
See documentation: https://v2.vuejs.org/v2/api/#Vue-set
Arguments are:
{Object | Array} target
{string | number} propertyName/index
{any} value
It replaces the value at target[propertyName/index] with value and forces reactivity on the value(s).
In your case it should be this instead of this.results = response.result;:
Vue.set(this, "results", response.result);

Why does my website return 2 space after retrieve in vuejs axios

Im new in vuejs, currently learning how to retrieve all data.
The image above shows that the return was empty but still have 2 row of spacing, I dont know why.
Below are the codes on my checker.vue
<template>
<div id="app">
<button #click="loadAll()">Load ALL</button>
<li v-for="(warranty, index) in warrantys" :key="index">
{{warranty.id}} -{{warranty.warranty_code}} - {{warranty.receipt}} - {{warranty.warranty_status}}
</li> </div>
</template>
<script>
export default {
data: function () {
return {
warrantys:[
]
}
},
methods: {
loadAll(){
this.axios({
method: 'get',
url: 'http://www.example.com/process.php?action=read',
}).then((response) => {
this.warrantys = response.data;
});
},
}
}
</script>
Also, I've tried to console log this.warranty. It returns the correct data. So I suspect that the problem was binding the data to it right?
Also I've to mentioned here is that im using CLI to create this project.
Pleaseee helppppp

How does vuejs react to component data updated asynchronously

I am very new with vuejs and recently started to try to replace some old jquery code that I have and make it reactive with vuejs. The thing is I have a component that gets information from a nodejs server via socket.io asynchronously.
When I get the data and update my component's data I see the changes when I console log it but it does not change the DOM the way I want it to do.
What is the proper way to grab data asynchronously and use it inside a component? I post some parts of my code so you can see it. I will appreciate any advice you can give me. Thanks in advance!
Vue.component('chat', {
data() {
return {
chat: null,
commands: [],
chatOpened: false,
}
},
props: [
'io',
'messages',
'channels',
'connectChat',
'roomChat',
'user',
'userId',
'soundRoute',
],
methods: {
openChat() {
this.chatOpened = true;
},
closeChat() {
this.chatOpened = false;
},
},
created() {
this.chat = this.$io.connect(this.connectChat);
this.commands.push('clear');
let self = this;
$.each(this.channels, function(index, value) {
self.chat.emit('join', {room: index, user: self.user, userId: self.userId}, function(err, cb) {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
self.channels[index].loaded = true;
}
//some more code
}
});
});
console.log(this.channels);
},
template: `
<div>
<div id="container-chat-open-button" #click="openChat" :class="{hide : chatOpened}">
<div>+90</div>
<i class="fas fa-comment-alt"></i>
</div>
<div id="container-chat" class="chat__container" :class="{open : chatOpened}">
<div id="container-chat-close-button" #click="closeChat">
<span>
<div>
<i class="fas fa-comment-alt"></i>
#{{ messages.chat_lobby_icon_title }}
</div>
<i class="icon-arrowdown"></i>
</span>
</div>
<div id="alert-chat" class="chat__container-notifications animated flash"></div>
<div class="row">
<ul>
<li v-for="channel in channels" v-show="channel.loaded === true">Channel loaded</li>
</ul>
</div>
</div>
</div>
`
});
I would expect to see the list of channels with messsages but instead I don't see the list even thought I see my channels with the loaded attribute set to true (by default they all have this attribute set to false).
My guess is that it's this part that is not working as expected.
if (messages.length > 0) {
self.channels[index].loaded = true;
}
The reactive way of doing this is by setting the full object again.
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
EDIT 1:
this.channels.forEach((channel) => {
this.chat.emit('join', {room: index, user: self.user, userId: self.userId}, (err, cb) => {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
}
//some more code
}
});
})
You'll need to add support for the rest-spread-operator using babel.

Vue filter in v-for

Hi I am pretty new to VueJS and have started working on a very simple API request. I have an object that looks like this:
posts: [
text: "String",
choices: {"1":"Yes","2":"No"}
]
Coming from angular, this seems very straightforward. I would just use a filter to convert choices to an object and loop over it. However, I ran into a problem. When I attempt to use the filter 'log' or 'json' in the v-for, they don't work.
<template>
<div>
<li v-for="(post,index) in posts | log">
<ul>
{{ post.text | log }}
{{ post.choices | json }}
<li v-for="(value,key) in post.choices | json">
{{value}} {{key}}
</li>
</ul>
</li>
</div>
</template>
<script>
import {HTTP} from './main';
export default {
filters: {
json: function (value) {
return JSON.parse(value)
},
log: function (value) {
console.log(value)
}
},
props: [
'apiKey'
],
data: () => ({
posts: [],
post: [],
errors: []
}),
created() {
HTTP.get('questions', { headers: { 'Api-Key': this.apiKey} })
.then(response => {
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
Then no data shows up, however they work fine in the mustache template. Any thoughts on how I can accomplish this?
tl;dr
works:
{{ post.choices | json }}
does not work:
<li v-for="(value,key) in post.choices | json">
Any work around? I can't use computed properties because this is a "sub-object" of an array and computed properties don't work that way, unless I am mistaken?
You cannot add a filter to a v-for directive.
Just parse the data before setting the posts data property:
HTTP.get('questions', { headers: { 'Api-Key': this.apiKey} })
.then(response => {
let posts = response.data;
post.forEach(p => p.choices = JSON.parse(p.choices));
this.posts = posts;
})
I don't see why you need to use JSON.parse, since the choices key refers to a valid JS object. Unless you have made a mistake, and that the choices key refers to a string.
Anyway, if you want to parse post.choice into a valid JS object, you can simply pass it into a custom function which returns the parsed JSON to v-for, for example:
<li v-for="(value,key) in postChoicesJson(post.choice)">
{{value}} {{key}}
</li>
And then declare a method for that:
postChoicesJson: function(obj) {
return JSON.parse(obj);
}
Note: Your DOM is invalid, because <li> elements must be a direct child of a <ul> or an <ol> element.