VueJS v-for in index array first element - vuejs2

Iam new in vuejs. So I have this array in my data and when I output the data in console the result is correct. I need to put the categories in an array because it can have multiple categories.
const app = new Vue({
el:"#app",
data:{
market:{
categories:[]
}
},
methods: {
init: function(){
var app = this;
var url = '/api/categories/0';
axios.get(url).then(function(response){
app.market.categories[0] = response.data;
console.log(app.market.categories[0]);
});
}
}
});
But when use v-for in the ui it doesn't show. Can you help? Thanks`
<select class="form-control form-control-no-border" id="s-category1">
<option value="">Please select a category</option>
<option v-for="category in market.categories[0]" :value="category.external_code">#{{ category.name }}</option>
</select>

Vue cannot react to changes to array elements when you access them with square brackets. When you modify array elements, you need to access them with array methods, or use Vue.set, like this...
Vue.set( app.market.categories, 0, response.data );

Related

Iterating over an object inside an axios.get request in vue js

I'm having issues looping over an object (a json response from a get request).
Is it not possible to use for ... in ... in a vue js method? or inside an axios method?
Even if I replace the loop to a dummy loop like so, I get no output to my console.
fetchData(id){
this.$Progress.start()
axios.get(base_path+'/admin_api/testDetailByUser/'+id)
.then(response => {
ob ={"a":1,"b":2,"c":3}
for (i in ob){
console.log(ob[i]);
}
this.$Progress.finish()
}).catch(error=>{
this.$Progress.fail()
});
},
The actual code looks something more like this:
export default {
data(){
return {
form : new Form({
id :'',
examiner_id :[],
}),
}
},
methods:{
fetchData(id){
this.$Progress.start()
axios.get(base_path+'/admin_api/testDetailByUser/'+id)
.then(response => {
this.myData = response.data;
$('#detailDiv').modal('show');
this.form.examiner_id = [];
for (property in this.myData){
this.form.examiner_id.push(this.myData[property].id);
}
this.$Progress.finish()
}).catch(error=>{
this.$Progress.fail()
});
},
with the following html excerpt
<form>
<tr v-for="(mailPdf,i) in mailPdfs" :key="mailPdf.id">
<select v-model="form.examiner_id[i]" name="examiner_id[i]" id="examiner_id" :value="mailPdf.examiner.id">
<option value="" disabled>choose examiner</option>
<option :value="mailPdf.examiner.id">{{mailPdf.examiner.name}} - current examiner</option>
<option v-for="testExaminer in filteredTestExaminer" :value="testExaminer.examiner.id">{{ testExaminer.examiner.name }}</option>
</select>
</tr>
</form>
the idea is to set the default value for the <select> input, since selected="selected" doesn't work on vuejs, but I can't feed it into data(){ because it has a new value for each individual get request.
Thanks in advance!
you can do that with Object. values()
your code should be like this
...
for (property in Object.values(this.myData)){
this.form.examiner_id.push(this.myData[property].id);
}
...
or just use forEach()
...
Object.values(this.myData).forEach(item => this.form.examiner_id.push(item.id))
...

VueJS Using #click on <option> elements and #change on <select> element

I have a simple function that manipulates a data between true and false. I use it to make my div hidden and visible like a toggle. I also have a with three element. I found out that I cannot use #click for <option> elements, I need to use #change for my <select>.
But in this way, whenever an is selected, the function is being triggered and my data toggles between true and false. Here is my <select> element;
<select #change="isDisabled">
<option>Please select a security type</option>
<option>No Security</option>
<option>Personal</option>
<option>Enterprise</option>
</select>
IsDisabled function takes a variable and change its values between true and false so my div becomes hidden and visible as follows;
<div v-if="noSecurity">something</div>
But here is the thing, I only want to trigger the function when the user select the "No Security" option. Now it's being triggered whenever I select an option, so it turned out to be some kind of a toggle. But I want to hide the div when I select the "No Security" option and show the div if something different is selected. What should I do?
I've made a CodeSandbox where you could see the result :
https://codesandbox.io/s/magical-meitner-63eno?file=/src/App.vue
But here is the explanation:
<template>
<section>
<select #change="isDisabled">
<option>Please select a security type</option>
<option>No Security</option>
<option>Personal</option>
<option>Enterprise</option>
</select>
<div v-if="noSecurity">You Choose no security, that's dangerous !</div>
</section>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
data() {
return {
noSecurity: false,
};
},
methods: {
isDisabled(e) {
console.log("e", e.target.value);
if (e.target.value === "No Security") {
// do your change
return (this.noSecurity = !this.noSecurity);
}
// to allow reset if another option is selected
if (this.noSecurity) {
return this.noSecurity = false;
}
},
},
};
</script>
Basically when you use the #change handler, your function will receive an event, in this event you can catch the target value with event.target.value.
Doing so, you do a condition if the value is equal to No Security (so the selected item), you change your state, if it's not No Security, you do nothing, or you do something else you would like to do.
Appart from that, I advice you to change your method name isDisabled to a global convention name like handleChange, or onChange.
Pass id values in your option so when you get the select event you're clear that No security or whatver the name you would like to change will be the same.
Because if one day you change No security to another name, you have to update all your conditions in your app. Try to avoid conditions with strings values like this if you can.
<option value="1">No Security</option> // :value="securityType.Id" for example if coming from your database
<option value="2">Personal</option>
<option value="3">Enterprise</option>
then in your function it will be
if (e.target.value === noSecurityId) {
// do your change
this.noSecurity = !this.noSecurity;
}
//...
There's no need for the additional noSecurity variable. Create your select with v-model to track the selected value. Give each option a value attribute.
<select v-model="selected">
<option value="">Please select a security type</option>
<option value="none">No Security</option>
<option value="personal">Personal</option>
<option value="enterprise">Enterprise</option>
</select>
Check that value:
<div v-if="selected === 'none'">something</div>
You can still use the noSecurity check if you prefer by creating a computed:
computed: {
noSecurity() {
return this.selected === 'none';
}
}
Here's a demo showing both:
new Vue({
el: "#app",
data() {
return {
selected: ''
}
},
computed: {
noSecurity() {
return this.selected === 'none';
}
},
methods: {},
created() {}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="selected">
<option value="">Please select a security type</option>
<option value="none">No Security</option>
<option value="personal">Personal</option>
<option value="enterprise">Enterprise</option>
</select>
<div v-if="selected === 'none'">something</div>
<div v-if="noSecurity">something</div>
</div>
Is using v-model instead of using a method is option for you? If it is, please try the following:
HTML:
<div id="hello-vue" class="demo">
<select v-model="security">
<option>Please select a security type</option>
<option>No Security</option>
<option>Personal</option>
<option>Enterprise</option>
</select>
<div v-if="security=='No Security'">something</div>
</div>
JS:
const HelloVueApp = {
data() {
return {
security: undefined
}
}
}

Clickable option with action in select

I'm working on a Vue file and have a select
<select v-if="show">
<option v-on:click="test()" value=""> someData</option>
<option>otherdata</option>
</select>
[...]
var instance = new Vue({
el: "#General",
[...]
methods:{
test: function(){
alert('youre goddam right');
}
[...]
}
[...]
}
So What i'm trying to do is to display my datas someData and otherdata. That works great but what i want it's when the user click on firstData, the action "test" append, and an alert comes up but that doesn't work here. I tried with the v-on:click but nothing happend.
Thanks for the help
Yes so it appears that the click event is not triggered for an <option> element. Here is one of the many fix available: using the input event on the select instead, and filter the value.
var instance = new Vue({
el: "#General",
methods:{
test: function(value) {
if (value === "choice1") {
alert('youre goddam right');
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<div id="General">
<select v-on:input="test($event.target.value)">
<option value="choice1">someData</option>
<option>otherdata</option>
</select>
</div>

Wrap element using v-if, otherwise just have content itself

I have a set of elements being generated in a v-for directive that, if the object has a url property, get wrapped in an <a> tag - otherwise, I need it to just emit the element itself.
For example,
var data = {
events: [
{name: "Foo"},
{name: "Bar", url: "google.com"}
]
};
and the corresponding HTML:
<div v-for="event in events">
<span>{{event.name}}</span>
</div>
What I need is to wrap the <span> in an <a v-bind:href="url"> only if there is a url property present.
I understand I could use a v-if and use two spans, such as:
<span v-if="!event.url">{{event.name}}</span>
<a v-bind:href="event.url" v-if="event.url">
<span>{{event.name}}</span>
</a>
However, in my use case the <span> element here could be massive and I don't want to repeat myself just to wrap the element.
Is there a way to achieve a conditional wrap such as the above?
You can use v-html for example and render your logic inside a function:
function wrapSpan(el, link) { // link wrapper function
return `${el}`;
}
new Vue({
el: '#app',
data: {
events: [
{name: "Foo"},
{name: "Bar", url: "google.com"}
]
},
methods: {
element: function(i) {
const name = this.events[i].name;
const url = this.events[i].url || null;
const span = `<span style="color: green">${name}</span>`; // long span
return (url) ? wrapSpan(span, url) : span;
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div v-for="(event, index) in events">
<span v-html="element(index)"></span>
</div>
</div>

Retrieving text of select element

When binding a <select> element using vue.js's v-model, how would you get the selected option text as opposed to the selected option value?
In HTML:
<select v-model="selected" options="myOptions"></select>
In JS:
myOptions: [{ text: 'Blue', value: '1' }, { text: 'Green', value: '2' }]
What I would want to retrieve is both the text 'Blue' as well as the value '1' by doing something like {{ selected.text }} or {{ selected.value }}. However, you can only do {{ selected }} which returns the selected value by default.
Ref: Vue.js guide for Dynamic Select Options
You can just use a filter, like this:
html:
<div id='vm'>
Formatted value:<b> {{city | cityFormatter}} </b><br/>
<br/>
<select v-model="city" options="cities"></select>
</div>
js:
var vm = new Vue({
el: '#vm',
data: {
city: 'city1',
cities: [{text: 'Toronto', value: 'city1'},
{text: 'Orleans', value: 'city2'}]
},
filters: {
cityFormatter: function(val) {
var newVal = '';
this.cities.map(function(el){
if (val == el.value){
newVal = el.value + ' ' + el.text;
}
});
return newVal;
}
}
});
Working example:
http://jsfiddle.net/qfy6s9Lj/9/
Actually, you could try combine jquery or just native js code
The solution with jQuery
html:
<div id='example'>
<select v-model="selected" options="myOptions"></select>
</div>
js:
var vm = new Vue({
el: '#example',
data: {
...
},
computed: {
selectedtext: {
cache: false,
//get selectedtext by jquery
get: function(){ return $(this.$el).find(":selected").text();}
}
},
});
The solution without jquery
html:
<div id='example'>
<select ref="ddl" v-model="selected" options="myOptions"></select>
</div>
js:
var vm = new Vue({
el: '#example',
data: {
...
},
computed: {
selectedtext: {
cache: false,
//get selectedtext directly
get: function(){
var ddl = this.$refs.ddl;
return ddl.options[ddl.selectedIndex].text;
}
}
},
});
Moreover, you could create a component for reusing logic and achieve the purpose of accessing the selected value by {{ selected.text }} or {{ selected.value }}.
An answer for Vue 2+
I was interested to come across this question as I'm currently evaluating Vue and React, and researching the comparative ease of getting current selection (not just dropdowns, but the UI generally).
What I found was that things have changed a lot since these posts in May-July 2015, when the latest version of Vue was 0.12. The JSFiddle in #swift's answer still works today because it pulls in Vue 0.12.
Using today's Vue, currently version 2.6, I found a solution that's relevant to people facing the same question today. It's interesting that using 2.6, the markup under discussion won't even get as far as initializing the options:
<select v-model="selected" options="myOptions"></select>
After digging into this, I found that although options is a valid HTML DOM property of the select element, and hence is accessible from JavaScript, it seems Vue no longer supports initializing it in markup like this. Instead, we add traditional HTML option elements. Per https://v2.vuejs.org/v2/guide/forms.html:
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
On first sight, this change seems a backward step. But in fact, remembering that we can use v-for and v-bind, I think it makes things more flexible. To demonstrate why I think that, I will first show the example given at the same linked page:
HTML
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
JS
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
It can be seen in the HTML that this binds the selected value of the dropdown to the Vue instance's selected property (using v-model="selected"), binds the individual option values to the value of each option (using v-bind:value="option.value"), and finally binds the individual option texts to the text that will be displayed (using {{ option.text }}).
It's only a small step further to bind selected to a different option property, be it text, id or whatever properties your option object may have, or--and here's the thing--to the option object itself. Being able to access the option itself as the selected value means we can access all of its properties, rather than only the property we chose to bind to:
HTML
<div id='vm'>
<select id="ddl1" v-model="ddl1selecteditem">
<option v-for="option in options1" v-bind:value="option">
{{ option.txt }}
</option>
</select>
<span>selected item: text='{{ ddl1selecteditem.txt }}', id={{ ddl1selecteditem.id }}</span>
</div>
JS
var vm = new Vue({
el: '#vm',
data: {
options1: [
{ txt: 'One', id: 1 },
{ txt: 'Two', id: 2 },
{ txt: 'Three', id: 3}
],
ddl1selecteditem: {}
}
});
vm.ddl1selecteditem = vm.options1[0];