Well, this problem has me stumped... Having a bit of trouble getting nested for-loop data to show up:
<div v-if = "private.folders!=null" v-for="folder in private.folders">
{{folder.name}}
<div v-for="check in folder.checks">
{{check.name}}
</div>
</div>
And then the data that I'm trying to use looks like this:
folders [Array]
-object [this is a single 'folder']
--name
--checks [array] [each folder has this array]
---[object] [the actual 'check' object]
----[name]
The outer loop works just fine, and returns the data I expect. However, check.name doesn't return anything, and there are no errors in the console. Is it possible to do nested for-loops like this?
I tested you template, it's works.
new Vue({
el: '#sample',
data: {
private: {
folders : [{
name : 'folder1',
checks : [
{ name : 'check1.1' },
{ name : 'check1.2' }
]
},
{
name : 'folder2',
checks : [
{ name : 'check2.1' },
{ name : 'check2.2' }
]
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="sample">
<div v-if = "private.folders!=null" v-for="folder in private.folders">
{{folder.name}}
<div v-for="check in folder.checks">
{{check.name}}
</div>
</div>
</div>
This is how you might set it up with HTML table:
new Vue({
el: '#sample',
data: {
private: {
folders : [{
name : 'folder1',
checks : [
{ name : 'check1.1' },
{ name : 'check1.2' }
]
},
{
name : 'folder2',
checks : [
{ name : 'check2.1' },
{ name : 'check2.2' }
]
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="sample">
<table>
<tr>
<th>Folder</th>
<th>Checks</th>
<th>Checks</th>
</tr>
<tr v-if = "private.folders!=null" v-for="folder in private.folders">
<td><b>{{folder.name}}</b></td>
<td v-for="check in folder.checks">
{{check.name}}
</td>
</tr>
</table>
</div>
Related
I am trying to create a table in vuejs for a personal project (I dont want to use a existing table) and I am facing probably a newbie problem.
I am trying to insert on my last column some buttons, but I dont know why, the grid is rendering my element tag instead of the element himself.
May someone explain to me why this does not work ? And, how can I create this feature ?
Fiddle: https://jsfiddle.net/y7830cvd/1/
<div id="app">
<div>
<vue-grid :rows="gridData" :title="nTitle"></vue-grid>
</div>
</div>
Vue.component('vue-grid', {
props: ['rows', 'title'],
template: `<div>
<h2>{{title}}</h2>
<div class="table-wrapper">
<table class="fl-table">
<thead>
<tr>
<th v-for="col in columns" :key="col.id" v-on:click="sortTable(col)">{{col}}</th>
</tr>
</thead>
<tbody v-if="rows.length > 0">
<tr v-for="row in rows" :key="row.id">
<td v-for="col in columns" :key="col.id">{{row[col]}}</td>
</tr>
</tbody>
</table>
</div>
</div>`,
computed: {
columns: function columns() {
if (this.rows.length == 0) {
return []
}
return Object.keys(this.rows[0])
}
},
sortTable(col) {
this.rows.sort(function(a, b) {
if (a[col] > b[col]) {
return 1
} else if (a[col] < b[col]) {
return -1
}
return 0
})
},
methods: {
formatter(row, column) {
return row.address
},
filterTag(value, row) {
return row.tag === value
},
filterHandler(value, row, column) {
const property = column['property']
return row[property] === value
}
}
});
var app = new Vue({
el: '#app',
data(){
return {
gridData: [
{"id" : 1, "name": "firstValue", "something": "wha the fox say?","options" : "<button>Add</button>" },
{"id" : 1, "name": "firstValue", "something": "uauu uauu uauu?"},
{"id" : 1, "name": "firstValue", "something": "The cow goes mu?"}
],
nTitle: "Hello There!"
}},
})
Try v-html:
<td v-for="col in columns" :key="col.id">
<span v-if="col == 'options'" v-html="row[col]"></span>
<span v-else>{{row[col]}}</span>
</td>
Something you should consider (source documentation - link above):
Updates the element’s innerHTML. Note that the contents are inserted
as plain HTML - they will not be compiled as Vue templates. If you
find yourself trying to compose templates using v-html, try to rethink
the solution by using components instead.
What i want to do is also check the default option when a user checks one of the checkbox item.
i have created a snippet of the error i am encountering,
usually i thought its because of my nested components.
but i encountered the error
You may have an infinite update loop in watcher with expression "localChecked"
even on this simple code snippet.
vue js script
new Vue({
el: "#app",
data: {
application: [
{
app_name : 'Netflix',
app_default : 'videoshare_default',
options : [
{ text : 'Video Stream', value : 'video_streaming'},
{ text : 'Download Video' , value : 'video_download'},
{ text : 'Share Video' , value : 'videoshare_default'}
]
},
{
app_name : 'Messenger',
app_default : 'message',
options : [
{ text : 'Messaging', value : 'message'},
{ text : 'Voice Calls' , value : 'voice_calls'},
{ text : 'Video Calls' , value : 'video_calls'},
{ text : 'Media Sharing' , value : 'file_transfer'}
]
}
],
selected : []
},
methods: {
selectDefault: function(data,$event){
this.selected[data.app_name].push(data.videoshare_default)
}
}
})
HTML
<div id="app">
<b-col v-for="(data , index) in application" v-bind:key="index" class="p-2" cols="5">
<b-form-group :label="data.app_name" label-class="font-weight-bold">
<b-form-checkbox-group
#input="selectDefault(data,$event)"
v-model="selected[data.app_name]"
:options="data.options"
name="application[]"
stacked
></b-form-checkbox-group>
</b-form-group>
</b-col>
</div>
a FIDDLE:
https://jsfiddle.net/tomexsans/194m0jdq/1/
or is there any other way to do this than what i am doing.
Your selected property is an array, but you want to use key value pairs, which is why you need to make it an object instead, which will store an array of each application type.
To make sure that Vue stays reactive, you need to use the Vue.set or this.$set method to add a property to an object, if that property DOESN'T already exist in that object.
The $event on b-form-checkbox-group returns the entire array of selected values, which we don't want. That's why i use the .native modifier on the event, so i can access the clicked checkbox and it's value.
new Vue({
el: "#app",
data: {
application: [{
app_name: 'Netflix',
app_default: 'videoshare_default',
options: [{
text: 'Video Stream',
value: 'video_streaming'
},
{
text: 'Download Video',
value: 'video_download'
},
{
text: 'Share Video',
value: 'videoshare_default'
}
]
},
{
app_name: 'Messenger',
app_default: 'message',
options: [{
text: 'Messaging',
value: 'message'
},
{
text: 'Voice Calls',
value: 'voice_calls'
},
{
text: 'Video Calls',
value: 'video_calls'
},
{
text: 'Media Sharing',
value: 'file_transfer'
}
]
}
],
selected: {}
},
methods: {
selectDefault(data, event) {
/* Return if the checkbox was unchecked */
if (!event.target.checked) return;
/* Return if the selected value was the default */
if (data.app_default === event.target.value) return;
/* Init the array if it doesn't exist yet.*/
if (!this.selected[data.app_name]) {
this.$set(this.selected, data.app_name, []);
}
const nestedSelected = this.selected[data.app_name];
/* Push in the default value if it doesn't exist alreayd */
if (!nestedSelected.find(value => value === data.app_default)) {
this.selected[data.app_name].push(data.app_default)
}
}
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-col v-for="(data , index) in application" v-bind:key="index" class="p-2" cols="5">
<b-form-group :label="data.app_name" label-class="font-weight-bold">
<b-form-checkbox-group
v-model="selected[data.app_name]"
#input.native="selectDefault(data, $event)"
:options="data.options"
name="application[]"
stacked
></b-form-checkbox-group>
</b-form-group>
</b-col>
{{ selected }}
</div>
I'm trying to pre-process a Vue 2 template and get a list of all of the element bindings. So if I have a file like this:
<html>
<body>
<div id="app">
<p>Here's a message: {{message1}}</p>
<p>Here's an input: <input type="text" v-model="message2"></p>
</div>
<script type="application/javascript" src="vue.js"></script>
<script type="application/javascript">
new Vue({
el: "#app",
data: {
message1: "foo",
message2: "bar"
}
});
</script>
</body>
</html>
Then somewhere (beforeMount?) I could query Vue and it would tell me the bindings are ['message1', 'message2']. Is that possible?
I ended up solving this by getting the text of the render function (by calling vm.$options.render.toString() ) and then parsing the bindings from that.
For instance, the rendering function for a simple list view looks like this:
function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"table",
{ attrs: { border: "1", cellpadding: "5", cellspacing: "0" } },
[
_vm._m(0),
_vm._l(_vm.rows, function(row) {
return _c("tr", [
_c(
"td",
[
_c("router-link", { attrs: { to: "/detail/" + row.ID } }, [
_vm._v(_vm._s(_vm._f("truncate")(row.TITLE, 100)))
])
],
1
),
_c("td", [_vm._v(_vm._s(_vm._f("truncate")(row.DESCRIPTION, 200)))]),
_c("td", [_vm._v(_vm._s(row.TYPE))])
])
})
],
2
)
}
It looks like the bindings are always contained in an _s() element, and optionally a vm.f() instruction when using filters.
Any idea how to resolve this problem:
in this example, the author uses vue 2.3.2 which works perfect,
new Vue({
el: '#app',
data: {
users: [{
"id": "Shad",
"name": "Shad"
},
{
"id": "Duane",
"name": "Duane"
},
{
"id": "Myah",
"name": "Myah"
},
{
"id": "Kamron",
"name": "Kamron"
},
{
"id": "Brendon",
"name": "Brendon"
}
],
selected: [],
allSelected: false,
userIds: []
},
methods: {
selectAll: function() {
this.userIds = [];
if (this.allSelected) {
for (user in this.users) {
this.userIds.push(this.users[user].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<h4>User</h4>
<div>
<table>
<tr>
<th>Name</th>
<th>Select All<input type="checkbox" #click="selectAll" v-model="allSelected"></th>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td><input type="checkbox" v-model="userIds" #click="select" :value="user.id"></td>
</tr>
</table>
</div>
<span>Selected Ids: {{ userIds }}</span>
</div>
when I switch it to 2.5.16 ( <script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script> ) , the behavior is wierd:
When click the selectAll checkbox, only that checkbox checked, but when I toggle it to uncheck, all the checkboses below get checked
For consistent browser functionality, I can recommended to not use click/change on checkboxes. Instead, bind the checkbox to a value (which you've already done), and then use a watcher on the value. This way, the real value of the checkbox will always accurately represent it's state. So you'd have something like this:
<input type="checkbox" v-model="allSelected">
Vue.component({..., {
data: function() {
return {
allSelected: false,
}
}
},
watch: {
allSelected: function(val){
//Use your source of truth to trigger events!
this.doThingWithRealValue(val);
}
}
});
You're already using your component data value of allSelected as the source of truth, so you should use this source of truth as the real triggering element value, not a click. Whenever the value of allSelected changes, your code will get ran. This solves the problem without the rendering order weirdness.
As pointed out by rob in the comments and in his answer you cannot rely on #click / #input / #change to have the same behaviour in all browsers in regards to their execution order relative to the actual model change.
There is an issue at the VueJS repository with a bit more context: https://github.com/vuejs/vue/issues/6709
The better solution is to watch the model for changes and then react accordingly.
new Vue({
el: '#app',
data: {
users: [{
"id": "Shad",
"name": "Shad"
},
{
"id": "Duane",
"name": "Duane"
},
{
"id": "Myah",
"name": "Myah"
},
{
"id": "Kamron",
"name": "Kamron"
},
{
"id": "Brendon",
"name": "Brendon"
}
],
selected: [],
allSelected: false,
userIds: []
},
methods: {
selectAll: function() {
this.userIds = [];
if (this.allSelected) {
for (user in this.users) {
this.userIds.push(this.users[user].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
},
watch: {
allSelected: function () {
this.selectAll()
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h4>User</h4>
<div>
<table>
<tr>
<th>Name</th>
<th>Select All<input type="checkbox" v-model="allSelected"></th>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td><input type="checkbox" v-model="userIds" #click="select" :value="user.id"></td>
</tr>
</table>
</div>
<span>Selected Ids: {{ userIds }}</span>
</div>
I am working on simple vuejs example projects.
Basically, I am trying to display set of data in a loop. But I can't print the random name array from the nested object in vue 2.
It prints as a full array like { "name": "check1.1" } I want to print them separately like Name, Chcek1.1 ... etc
new Vue({
el: '#sample',
data: {
private: {
folders : [{
name : 'folder1',
checks : [
{ "name" : 'check1.1' },
{ "name2" : 'check1.2' },
{ "name4" : 'check1.1' },
{ "abcd" : 'check1.2' },
]
},
{
name : 'folder2',
checks : [
{ "name" : 'check1.1' },
{ "name2" : 'check1.2'},
{ "abcs" : 'check1.1' },
{ "abcd" : 'check1.2' },
]
}
]
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="sample">
<h1>current result: </h1>
<div v-if = "private.folders!=null" v-for="folder in private.folders">
{{folder.name}}
<div v-for="check in folder.checks">
{{check}}
</div>
</div>
</div>
<h1>expected result: </h1>
<ul>
<li>folder1
<ul>
<li>name</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name2</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name3</li>
<li>check1.1</li>
</ul>
</li>
<li>folder2
<ul>
<li>name</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name2</li>
<li>check1.1</li>
</ul>
---
<ul>
<li>name3</li>
<li>check1.1</li>
</ul>
</li>
</ul>
The outer loop works just fine but inside checks[] I can't get them as I expected.