I have this code in VueJs , simple task list, completed and incompleted ones, when I check or uncheck the box the task should move to the proper list.
var app = new Vue({
el: '#vueapp',
data: {
tasks: [{
id: 1,
description: 'Do some Stuff',
completed: false
},
{
id: 2,
description: 'Go to pharmacy',
completed: false
},
{
id: 3,
description: 'Go to doctor',
completed: true
},
{
id: 4,
description: 'Do some Slask',
completed: false
},
]
},
methods: {
toggleTask(key) {
this.tasks[key].completed = !this.tasks[key].completed;
}
},
computed: {
incompleteTasks() {
return this.tasks.filter(task => !task.completed);
},
completedTasks() {
return this.tasks.filter(task => task.completed);
},
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="vueapp">
<h2>Completed Tasks</h2>
<ul>
<li v-for="(task, key) in completedTasks">{{ task.description }}<input type="checkbox" v-model="task.completed"></li>
</ul>
<h2>Incomplete Tasks</h2>
<ul>
<li v-for="(task, key) in incompleteTasks">{{ task.description }}<input type="checkbox" v-model="task.completed"></li>
</ul>
</div>
tested in Chrome. Try check the first incomplete task , it moves on the upper list succesfully, but the next incomplete task gets checked too.!!!!
You need to add a key to your loops :key="task.id".
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. An ideal value for key would be the unique id of each item.
var app = new Vue({
el: '#vueapp',
data: {
tasks: [{
id: 1,
description: 'Do some Stuff',
completed: false
},
{
id: 2,
description: 'Go to pharmacy',
completed: false
},
{
id: 3,
description: 'Go to doctor',
completed: true
},
{
id: 4,
description: 'Do some Slask',
completed: false
},
]
},
methods: {
toggleTask(key) {
this.tasks[key].completed = !this.tasks[key].completed;
}
},
computed: {
incompleteTasks() {
return this.tasks.filter(task => !task.completed);
},
completedTasks() {
return this.tasks.filter(task => task.completed);
},
}
});
.as-console-wrapper { display: none !important; }
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="vueapp">
<h2>Completed Tasks</h2>
<ul>
<li v-for="(task, key) in completedTasks" :key="task.id">{{ task.description }}<input type="checkbox" v-model="task.completed"></li>
</ul>
<h2>Incomplete Tasks</h2>
<ul>
<li v-for="(task, key) in incompleteTasks" :key="task.id">{{ task.description }}<input type="checkbox" v-model="task.completed"></li>
</ul>
</div>
Related
<div id="app">
<div v-if="isLoaded">
<select v-model="selectNum" name="text">
<option value="" selected="selected">Select status</option>
<option value="ok">ok</option>
<option value="notok">notok</option>
</select>
</div>
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<ul>
<li v-for="user in userList"></li>
<li v-for="manage in manageList"></li>
</ul>
</div>
const app = new Vue ({
el: '#app',
data: {
search: '',
itemsList: [],
isLoaded: false,
selectNum: '',
userList: [
{
id: 1,
name: "Prem",
status:"ok"
},
{
id: 2,
name: "Chandu",
status:"notok"
},
{
id: 3,
name: "Shravya",
status:"ok"
},
{
id: 4,
name: "kirt",
status:"notok"
}
],
manageList: [
{
id: 1,
name: "cc",
status:"ok"
},
{
id: 2,
name: "aa",
status:"notok"
},
{
id: 3,
name: "a",
status:"ok"
},
{
id: 4,
name: "vv",
status:"notok"
}
]
},
created(){
this.isLoaded = true;
},
computed: {
filteredAndSorted(){
function compare(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
const res = this.userList.filter(user => {
return user.name.toLowerCase().includes(this.search.toLowerCase())
}).sort(compare)
if (this.selectNum) {
return res.filter(user => user.status === this.selectNum )
}
return res
}
}
})
From multiple v-for, I want to display data initially, later I have two filters where one is for filtering array and second one is for, selecting particular array from dropdown, for all of them I have written some logic, But I not sure how to combine my looping logic in filters, so that it work accordingly?
This is my code link :- https://codepen.io/dhanunjayt/pen/vYeeorm
You don't need to create another array to store search results. Vuejs provided computed property for that.
HTML
<div id="app">
<div v-if="isLoaded">
<select v-model="selectNum" name="text"> <!-- HERE -->
<option value="" selected="selected">Select status</option>
<option value="ok">ok</option>
<option value="notok">notok</option>
</select>
</div>
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<span>Search Results</span>
<ul>
<li v-for="user in search_results">Name:{{user.name}} and Status:{{user.status}}</li>
</ul>
<span>All Users</span>
<ul>
<li v-for="user in userList">Name:{{user.name}} and Status:{{user.status}}</li>
</ul>
</div>
JS
const app = new Vue({
el: "#app",
data: {
search: "",
isLoaded: false,
selectNum: "",
userList: [
{
id: 1,
name: "Prem",
status: "ok"
},
{
id: 2,
name: "Chandu",
status: "notok"
},
{
id: 3,
name: "Shravya",
status: "ok"
},
{
id: 4,
name: "kirt",
status: "notok"
}
],
manageList: [
{
id: 1,
name: "cc",
status: "ok"
},
{
id: 2,
name: "aa",
status: "notok"
},
{
id: 3,
name: "a",
status: "ok"
},
{
id: 4,
name: "vv",
status: "notok"
}
]
},
created() {
this.isLoaded = true;
},
computed: {
search_results: function () {
let that = this;
if (that.search.length == 0 && that.selectNum.length == 0) return;
return this.userList.filter(
(item) =>
item.name
.toLocaleLowerCase()
.indexOf(that.search.toLocaleLowerCase()) >= 0 &&
item.status.toLocaleLowerCase() == that.selectNum.toLocaleLowerCase()
);
}
}
});
If i understand your question correcttly.Try this:
search_results: function () {
let filterList = ['userList','manageList']
let resultList = []
let that = this;
filterList.forEach(filter => {
const result = that[filter].filter((item) =>
item.name
.toLocaleLowerCase()
.indexOf(that.search.toLocaleLowerCase()) >= 0 &&
item.status.toLocaleLowerCase() == that.selectNum.toLocaleLowerCase())
resultList = [...resultList,...result]
})
return resultList
}
I'm using the react-sortablejs library.
When trying to move cards within the list. I get the error:
Cannot read property 'map' of undefined
I have a dense structure and it gets lost here. How to handle onChange so that I can see in the console that the order of the notes within the list has changed.
Demo here
import Sortable from 'react-sortablejs';
// Functional Component
const SortableList = ({ items, onChange }) => {
return (
<div>
<Sortable
tag="ul"
onChange={(order, sortable, evt) => {
console.log(order)
onChange(order);
}}
>
{items.listItems.map(val => {
return <li key={uniqueId()} data-id={val}>List Item: {val.title}</li>})
}
</Sortable>
</div>
);
};
class App extends React.Component {
state = {
item: {
id: "abc123",
name: "AAA",
lists: [
{
id: "def456",
list_id: "654wer",
title: 'List1',
desc: "description",
listItems: [
{
id: "ghj678",
title: "ListItems1",
listItemsId: "88abf1"
},
{
id: "poi098",
title: "ListItems2",
listItemsId: "2a49f25"
},
{
id: "1oiwewedf098",
title: "ListItems3",
listItemsId: "1a49f25dsd8"
}
]
},
{
id: "1ef456",
list_id: "654wer",
title: 'List 2',
desc: "description",
listItems: [
{
id: "1hj678",
title: "ListItems4",
listItemsId: "18abf1"
},
{
id: "1oi098",
title: "ListItems5",
listItemsId: "1a49f25"
},
{
id: "1oiwewe098",
title: "ListItems6",
listItemsId: "1a49f25dsd"
}
]
},
{
id: "2ef456",
title: 'List 3',
list_id: "254wer",
desc: "description",
listItems: [
{
id: "2hj678",
title: "ListItems7",
listItemsId: "28abf1"
},
{
id: "2oi098",
title: "ListItems8",
listItemsId: "234a49f25"
},
{
id: "df098",
title: "ListItems9",
listItemsId: "1asd8"
}
]
}
]
}
};
render() {
const c = this.state.item['lists'].map(item => { return item.listItems});
return (
this.state.item['lists'].map(item => {
return (<div>
{item.title}
<SortableList
key={uniqueId()}
items={item}
onChange={(item) => {
console.log(item)
this.setState({item});
}}
>
</SortableList>
</div>)
})
)
}
};
Thanks in advance.
You have to update few changes in your code.
Update the SortableList function as below.
First pass data-id={val.id} in li and after that in onChange method you will receive the order with id. So based on that we are sorting the records.
const SortableList = ({ items, onChange }) => {
return (
<div>
<Sortable
tag="ul"
onChange={(order, sortable, evt) => {
items.listItems.sort(function(a, b){
return order.indexOf(a.id) - order.indexOf(b.id);
});
onChange(items);
}}
>
{items.listItems.map(val => {
return <li key={uniqueId()} data-id={val.id}>List Item: {val.title}</li>})
}
</Sortable>
</div>
);
};
Update the onChange event of App component.
onChange={(item) => {
let itemObj = {...this.state.item};
itemObj.lists.map(x=>{
if(x.id === item.id) x = item;
});
this.setState({itemObj});
}}
That's it!
Here is the working demo for you
https://stackblitz.com/edit/react-sortablejs-blzxwd
When remove the onChange event in the Sortable list, Its works.
const SortableList = ({ items, onChange }) => {
return (
<div>
<Sortable
tag="ul"
>
{items.listItems.map(val => {
return <li key={uniqueId()} data-id={val}>List Item: {val.title}</li>})
}
</Sortable>
</div>
);
};
I have this fiddle:
https://jsfiddle.net/pnqzspoe/12014/
I want to modify it a bit and want to display each node as a text area containing the corresponding text. Further, I want to give an option to 'reply' to it. This would mean insertion of a new text area into which we can enter text.
Here is the code:
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{ model.name }}
<span v-if="isFolder">[{{ open ? '-' : '+' }}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="(model, index) in model.children"
:key="index"
:model="model">
</item>
<li class="add" #click="addChild">+</li>
</ul>
</li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>
var data = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
el: '#demo',
data: {
treeData: data
}
})
What would be the template for this use-case?
If I don't understand your question wrongly...
Replace
{{model.name}}
with
<textarea v-model="model.name"></textarea>
should work?
I am trying to create a Bootstrap tabs component in Vuejs. The tabs component consists of two parts. First, the parent tabs-list component which contains multiple tab-list-item component. Here is the code for both of these-
//Vue component template for tabs list.
Vue.component('tabs-list', {
template: `<ul class="nav nav-tabs nav-justified" role="tablist">
<tab-list-item v-for="concept in concepts" :key="concept.id" :concept="concept" :selected="concept.active">{{ concept.title }}</tab-list-item>
</ul>`,
data() {
return {
activeTab: 1,
concepts: [ { title: 'Tab A', id:1, active: true},
{ title: 'Tab B', id:2, active: false},
{ title: 'Tab C', id:3, active: false},
{ title: 'Tab D', id:4, active: false},
{ title: 'Tab E', id:5, active: false},
{ title: 'Tab F', id:6, active: false},
{ title: 'Tab G', id:7, active: false},
{ title: 'Tab H', id:8, active: false}]
}
},
methods: {
tabItemClicked(concept) {
console.log(concept);
this.activeTab = concept.id;
this.concepts.forEach(tab=> {
tab.active = (tab.id === concept.id);
});
}
}
});
//Vue component template for tab list item.
Vue.component('tab-list-item', {
props: ['concept', 'selected'],
template: `<li role="presentation" :class="{active:concept.active}">
<a :href='computedHref' :aria-controls="ariaControls" role="tab" data-toggle="tab" #click="tabClicked">
<img :src="aquaImage" class="image-responsive concept-image img-active">
<slot></slot>
</a>
</li>`,
computed: {
computedHref: function() {
return "#concept"+this.concept.title
},
ariaControls: function() {
return "concept"+this.concept.title
},
aquaImage: function() {
return "/images/"+this.concept.title+"-aqua.png"
}
},
data() {
return {
isActive: false
}
},
mounted() {
this.isActive = this.selected;
},
methods: {
tabClicked: function() {
this.$emit('tabItemClicked', [this.concept]);
}
}
});
So, here my tab-list-item should emit an event tabItemClicked when any of the tabs is clicked. However, I am not getting anything logged in the console. When I take a look at the Vue developer console, I do see the event getting emitted. But why is it not getting captured by the parent tabs-list method? Any help will be greatly appreciated!
You have to explicitly listen to the event in the parent component template
Vue.component('tabs-list', {
template: `<ul class="nav nav-tabs nav-justified" role="tablist">
<tab-list-item v-on:tabItemClicked="tabItemClicked" v-for="concept in concepts" :key="concept.id" :concept="concept" :selected="concept.active">{{ concept.title }}</tab-list-item>
</ul>`,
//....,
methods: {
tabItemClicked(concept) {
console.log(concept);
this.activeTab = concept.id;
this.concepts.forEach(tab=> {
tab.active = (tab.id === concept.id);
});
}
}
}
camelCased custom events do not invoke trigger in parent. Change this.$emit('tabItemClicked', [this.concept]); tothis.$emit('tab_item_clicked', [this.concept]); See https://github.com/vuejs/vue/issues/4044
Hello I have this code in my symfony 3 project :
TWIG TEMPLATE:
<div id="fileManagerContainer" class="AppContent">
{% verbatim %}
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{model.name}}
<span v-if="isFolder">{{open ? '-' : '+'}}</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="model in model.children"
:model="model">
</item>
<li class="add" #click="addChild">+</li>
</ul>
</li>
</script>
{% endverbatim %}
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul id="demo">
<item
class="item"
:model="treeData">
</item>
</ul>
</div>
VUE FILE :
// demo data
var data = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
delimiters: ['{{', '}}'],
el: '#demo',
data: {
treeData: data
}
})
ant it works on jsfiddle, but doesnt do a thing in real project. All scripts are loaded perfectly, Vue.js works but just this piece of code does not. Any ideas ?