Vue.js method change event doesn't trigger - javascript

I am trying to trigger a change event, where some statement is set from true to false and the other way around, whenever I check the "checkbox", but it doesn't work.
It's actually from this tutorial where I tried to experiment by adding this change event.
<div v-for="task in tasks" :key="task.id" v-if="task.completed">
<ul>
<li v-text="task.description"></li>
<input type="checkbox" #change="changeComplete">
</ul>
</div>
var app = new Vue ({
el: '#root',
data: {
tasks: [
{description: 'clean room', completed: true, id: 1},
{description: 'do homework', completed: true, id: 2},
{description: 'go to sleep', completed: false, id: 3}
]
},
methods: {
changeComplete() {
this.task.completed = !this.task.completed;
}
}
})
I would now expect whenever I check the box for a certain task that the value completed is changing, but the change event doesn't trigger and the completed value remains unchanged.

There is no task property in data object here so you need to change in tasks array, And to do this you need to pass index to identify which task need to change.
var app = new Vue ({
el: '#root',
data: {
tasks: [
{description: 'clean room', completed: true, id: 1},
{description: 'do homework', completed: true, id: 2},
{description: 'go to sleep', completed: false, id: 3}
]
},
methods: {
changeComplete(index) {
this.tasks[index].completed = !this.tasks[index].completed;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<div id="root">
<div>
<div v-for="task,index in tasks" :key="task.id" v-if="task.completed">
<ul>
<li v-text="task.description"></li>
<input type="checkbox" #change="changeComplete(index)">
</ul>
</div>
</div>
</div>
Or better you can change just class to revert or toggle task status
var app = new Vue ({
el: '#root',
data: {
tasks: [
{description: 'clean room', completed: true, id: 1},
{description: 'do homework', completed: true, id: 2},
{description: 'go to sleep', completed: false, id: 3}
]
},
methods: {
changeComplete(index) {
this.tasks[index].completed = !this.tasks[index].completed;
}
}
})
.completed{
color: green;
text-decoration: line-through;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<div id="root">
<div>
<div :class='{completed:task.completed}' v-for="task,index in tasks" :key="task.id">
<ul>
<li v-text="task.description"></li>
<input type="checkbox" #change="changeComplete(index)">
</ul>
</div>
</div>
</div>

You need to refer to the task you want to complete inside the callback:
#change="changeComplete(task)"
and then:
changeComplete(task) {
task.completed = !task.completed;
}
And your v-if shows the completed tasks, I suppose you want to show incomplete ones:
v-if="task.completed == false"
Here's a working copy:
Vue.config.productionTip = false;
Vue.config.devtools = false;
var app = new Vue({
el: '#root',
template: '<div> \
<div v-for="task in tasks" :key="task.id" v-if="task.completed == false"> \
<ul> \
<li> \
<span v-text="task.description"></span> \
<input type="checkbox" #change="changeComplete(task)"> \
</li> \
</ul> \
</div></div>',
data: {
tasks: [{
description: 'clean room',
completed: false,
id: 1
},
{
description: 'do homework',
completed: false,
id: 2
},
{
description: 'go to sleep',
completed: false,
id: 3
}
]
},
methods: {
changeComplete(task) {
task.completed = !task.completed;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="root"></div>

Related

How to pass data from component file to view file in vue js

I'm trying to pass data from vue component to vue view file. I tried creating props but it didn't work. I have one component file on path src/components/list.vue and another file on path src/views/select.vue
The flow goes like this: User lands on select.vue page, here on click of input box a pop-up appears which have have list of elements in <ul> <li> tag from list.vue (component file).
What I want to achieve:
Whenever user select any option from list.vue file, modal pop-up should close and selected element should be displayed in input box of select.vue file.
Below is code:
src/views/select.vue
<label class="primary-label mb-2">First Question</label>
<div class="form-block">
<label class="secondary-label mb-1">Question</label>
<b-form-input placeholder="Select Question" v-model="questions.first" class="form-control l-input" #click="onOptionChanged"></b-form-input>
</div>
<div class="form-block">
<label class="secondary-label mb-1">Answer</label>
<b-form-input v-model="answers.first" placeholder="Enter answer" class="form-control l-input"></b-form-input>
</div>
<script>
export default {
data() {
return {
questions: {
first: null,
},
answers: {
first: null,
},
options: [
{ value: null, text: 'Select Question', disabled:true },
{ value: 1, text: 'In what city were you born?' },
{ value: 2, text: 'What is the name of your favorite pet?' },
{ value: 3, text: 'What is your mother\'s maiden name?' },
{ value: 4, text: 'What high school did you attend?' },
{ value: 5, text: 'What is the name of your first school?' },
{ value: 6, text: 'What was the make of your first car?' },
{ value: 7, text: 'What was your favorite food as a child?' },
{ value: 8, text: 'Where did you meet your spouse?' },
],
}
},
methods: {
onOptionChanged() {
var modal_ref = 'myModalRef';
this.$refs[modal_ref].show();
},
},
components: {
SecurityQuestionsList,
},
}
src/components/list.vue
<template>
<main>
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="search-block">
<span class="s-icon fa fa-search"></span>
</div>
<ul class="l-group" v-if="filteredQuestions.length > 0">
<li class="d-flex align-items-center" :key="item.id" v-for="item in filteredQuestions" #click="onOptionSelect(item.question)"
:class="selected === item.id ? 'my-selected-item-class':null">
{{item.question}}
</li>
</ul>
</div>
</div>
</div>
</main>
</template>
<script>
export default {
data() {
return {
search: '',
selected: null,
questions: [
{ id: 1, question: 'In what city were you born?' },
{ id: 2, question: 'What is the name of your favorite pet?' },
{ id: 3, question: 'What is your mother\'s maiden name?' },
{ id: 4, question: 'What high school did you attend?' },
{ id: 5, question: 'What is the name of your first school?' },
{ id: 6, question: 'What was the make of your first car?' },
{ id: 7, question: 'What was your favorite food as a child?' },
{ id: 8, question: 'Where did you meet your spouse?' },
],
}
},
computed: {
filteredQuestions() {
return this.questions.filter(item => {
return item.question.toLowerCase().includes(this.search.toLowerCase());
});
}
},
methods: {
onOptionSelect(selectedId) {
this.selected = selectedId;
console.log(this.selected);
this.$emit('questions-selected', this.selected);
},
}
}
</script>
I am getting selected value in console but not sure how to catch it in search.vue file.
Thanks!
You need to connect your components and pass props, try like following snippet:
Vue.component('list', {
template: `
<main>
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="search-block">
<span class="s-icon fa fa-search"></span>
</div>
<ul class="l-group" v-if="filteredQuestions.length > 0">
<li class="d-flex align-items-center" :key="item.id" v-for="item in filteredQuestions" #click="onOptionSelect(item.question)"
:class="selected === item.id ? 'my-selected-item-class':null">
{{item.question}}
</li>
</ul>
</div>
</div>
</div>
</main>
`,
props: ['search'],
data() {
return {
selected: null,
questions: [
{ id: 1, question: 'In what city were you born?' },
{ id: 2, question: 'What is the name of your favorite pet?' },
{ id: 3, question: 'What is your mother\'s maiden name?' },
{ id: 4, question: 'What high school did you attend?' },
{ id: 5, question: 'What is the name of your first school?' },
{ id: 6, question: 'What was the make of your first car?' },
{ id: 7, question: 'What was your favorite food as a child?' },
{ id: 8, question: 'Where did you meet your spouse?' },
],
}
},
computed: {
filteredQuestions() {
if (this.search) {
return this.questions.filter(item => {
return item.question.toLowerCase().includes(this.search.toLowerCase());
});
} return this.questions
}
},
methods: {
onOptionSelect(selectedId) {
this.selected = selectedId;
this.$emit('questions-selected', selectedId);
},
}
})
new Vue({
el: "#demo",
name: 'select',
data() {
return {
questions: {
first: null,
},
answers: {
first: null,
},
selected: false
}
},
methods: {
onOptionChanged() {
this.selected = true
},
onSelected(val) {
this.questions.first = val
this.selected = false
},
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div id="demo">
<div>
<label class="primary-label mb-2">First Question</label>
<div class="form-block">
<label class="secondary-label mb-1">Question</label>
<b-form-input placeholder="Select Question" v-model="questions.first" class="form-control l-input" #click="onOptionChanged()"></b-form-input>
</div>
<div class="form-block">
<label class="secondary-label mb-1">Answer</label>
<b-form-input v-model="answers.first" placeholder="Enter answer" class="form-control l-input"></b-form-input>
<list :search="questions.first" #questions-selected="onSelected" v-if="selected"></list>
</div>
</div>
</div>

filtering items with multiple filters using vue js

I'm learning Vue JS and trying to use multiple filters to filter multiple products.
So, I have an example of 6 filters, product 1 to product 6. And I also have 6 products with different categories. I've coded as below, but when I try to click button filter, to filter the product, it doesn't work. I've tried various ways still not working.
Can anyone help me explain it and deal with this problem?
new Vue({
el: '#app',
data () {
return {
categories:[
{
category_title: 'Product 1',
category_name: "product-1"
},
{
category_title: 'Product 2',
category_name: "product-2"
},
{
category_title: 'Product 3',
category_name: "product-3"
},
{
category_title: 'Product 4',
category_name: "product-4"
},
{
category_title: 'Product 5',
category_name: "product-5"
},
{
category_title: 'Product 6',
category_name: "product-6"
},
],
productItems: [
{
name: 'Pro1',
category: 'product-1'
},
{
name: 'Pro2',
category: 'product-2'
},
{
name: 'Pro3',
category: 'product-3'
},
{
name: 'Pro4',
category: 'product-4'
},
{
name: 'Pro5',
category: 'product-5'
},
{
name: 'Pro6',
category: 'product-6'
},
],
}
},
computed: {
productFilter(){
return this.productItems;
},
filterProduct(){
return this.productItems.filter((product) => product.category == this.categories.category_name)
}
}
})
.product-items span{
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div id="app">
<div class="product-filter">
<button #click="filterProduct" v-for="cat in categories" :key="cat.id">
{{ cat.category_title }}
</button>
</div>
<div class="product-items">
<span v-for="product in productFilter" :key="product.id">{{ product.name }}</span>
</div>
</div>
Move the filterProduct from the computed properties to methods. Generally actions are to be placed inside methods and the computed properties holds the data which changes with respect to the dependent values used inside the defenition of that particular computed property.
Here I have created a data property called activeCategory which is being set when the user clicks any one of the category button. The productFilter computed property makes use of activeCategory data property. So when ever the data property activeCategory gets updated from the function filterProduct, the productFilter computed property automatically retrns new set of products.
new Vue({
el: '#app',
data() {
return {
categories: [
{
category_title: 'Product 1',
category_name: "product-1"
},
{
category_title: 'Product 2',
category_name: "product-2"
},
{
category_title: 'Product 3',
category_name: "product-3"
},
{
category_title: 'Product 4',
category_name: "product-4"
},
{
category_title: 'Product 5',
category_name: "product-5"
},
{
category_title: 'Product 6',
category_name: "product-6"
},
],
productItems: [
{
name: 'Pro1',
category: 'product-1'
},
{
name: 'Pro2',
category: 'product-2'
},
{
name: 'Pro3',
category: 'product-3'
},
{
name: 'Pro4',
category: 'product-4'
},
{
name: 'Pro5',
category: 'product-5'
},
{
name: 'Pro6',
category: 'product-6'
},
],
activeCategory: null,
}
},
methods: {
filterProduct(category) {
this.activeCategory = category;
}
},
computed: {
productFilter() {
return this.activeCategory ?
this.productItems.filter((product) => product.category == this.activeCategory.category_name) :
this.productItems;
},
}
})
.product-items span {
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<div id="app">
<div class="product-filter">
<button #click="filterProduct(cat)" v-for="cat in categories" :key="cat.id">
{{ cat.category_title }}
</button>
</div>
<div class="product-items">
<span v-for="product in productFilter" :key="product.id">{{ product.name }}</span>
</div>
</div>
The method filterProduct should be defined inside methods option and add filtered property that will take the filtered products when you click the button, this click button runs the method that takes the current category as parameter :
new Vue({
el: '#app',
data() {
return {
categories: [{
category_title: 'Product 1',
category_name: "product-1"
},
{
category_title: 'Product 2',
category_name: "product-2"
},
{
category_title: 'Product 3',
category_name: "product-3"
},
{
category_title: 'Product 4',
category_name: "product-4"
},
{
category_title: 'Product 5',
category_name: "product-5"
},
{
category_title: 'Product 6',
category_name: "product-6"
},
],
productItems: [{
name: 'Pro1',
category: 'product-1'
},
{
name: 'Pro2',
category: 'product-2'
},
{
name: 'Pro3',
category: 'product-3'
},
{
name: 'Pro4',
category: 'product-4'
},
{
name: 'Pro5',
category: 'product-5'
},
{
name: 'Pro6',
category: 'product-6'
},
],
filtered: []
}
},
computed: {
productFilter() {
return this.productItems;
},
},
methods: {
filterProduct(cat) {
this.filtered = this.productItems.filter((product) => product.category == cat.category_name)
}
}
})
.product-items span {
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div id="app">
<div class="product-filter">
<button #click="filterProduct(cat)" v-for="cat in categories" :key="cat.id">
{{ cat.category_title }}
</button>
</div>
<div class="product-items">
<span v-for="product in filtered" :key="product.id">{{ product.name }}</span>
</div>
<pre>
<pre>
{{filtered}}
</pre>
</div>

how make ng-model work separately inside array of objects?

I'm stuck with some concept while realizing checkbox system.
I've got an array of objects, it looks like this:
this.columnsGroups = [
{
id: 1,
title: 'General requests',
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined
},
{
title: 'ID',
filterType: 'number',
visible: undefined
}
]
},
{
id: 2,
title: 'Technical Requests',
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined
},
{
title: 'ID',
filterType: 'number',
visible: undefined
}
]
},
{
id: 3,
title: 'Popular requests',
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined
},
{
title: 'ID',
filterType: 'number',
visible: undefined
}
]
},
{
id: 4,
title: 'Misc requests',
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined
},
{
title: 'ID',
filterType: 'number',
visible: undefined
}
]
}
]
What I'm trying to do is to parse this array into html template with checkboxes.
The structure of the template consists of: parent checkbox
<div class="ln-select-columns-content" ng-if="currentTab.isAdvancedSearch" ng-repeat="group in ctrl.columnsGroups" ng-style="{'width':'25%'}">
<div class="ln-select-column-row">
<label>
<span class="ln-checkbox">
<input type="checkbox" ng-model="allChecked" ng-checked="allSelected" ng-click="ctrl.toggleSelectAllAttributes()"/>
<span class="ln-bg"></span>
</span>
<div class="ln-select-column-name"><b>{{group.title}}:</b></div>
</label>
<div ng-repeat="column in group.props" class="ln-select-column-row">
<label>
<span class="ln-checkbox">
<input type="checkbox" ng-model="column.visible" />
<span class="ln-bg"></span>
</span>
<span class="ln-select-column-name">{{column.title}}</span>
</label>
</div>
</div>
</div>
Literally, the template view looks like this:
view screenshot
Now, the problem is: when I'm writing a realisation for checkboxing all the child checkboxes with parent checkbox for each column, ng-model=allChecked checks all the child checkboxes from any parent checkbox in the container.
What would be the best way to do parent checkboxing, isolated from the sibling parent checkboxes?
Or maybe the array of objects is not correctly structured?
You have to modify your JSON object a little bit by adding a selected property for tracking ng-model of each checkbox. Then you can write methods in your $scope to trigger on ng-change of each checkbox and toggle checkboxes by modifying JSON object.
For complete solution please see below snippet.
angular.module('app', []).controller('HomeCtrl', function($scope) {
$scope.toggleGroup = function(group) {
if (group.selected) {
group.props.forEach(function (prop) {
prop.selected = true
})
} else {
group.props.forEach(function (prop) {
prop.selected = false
})
}
}
$scope.selectProp = function(group) {
var allSelected = true;
group.props.forEach(function (prop) {
allSelected = allSelected && prop.selected;
})
group.selected = allSelected;
}
$scope.columnsGroups = [
{
id: 1,
title: 'General requests',
selected: false,
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined,
selected: false
},
{
title: 'ID',
filterType: 'number',
visible: undefined,
selected: false
}
]
},
{
id: 2,
title: 'Technical Requests',
selected: false,
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined,
selected: false
},
{
title: 'ID',
filterType: 'number',
visible: undefined,
selected: false
}
]
},
{
id: 3,
title: 'Popular requests',
selected: false,
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined,
selected: false
},
{
title: 'ID',
filterType: 'number',
visible: undefined,
selected: false
}
]
},
{
id: 4,
title: 'Misc requests',
selected: false,
props: [
{
title: 'Search level',
filterType: 'entry',
visible: undefined,
selected: false
},
{
title: 'ID',
filterType: 'number',
visible: undefined,
selected: false
}
]
}
]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="HomeCtrl">
<div ng-repeat="group in columnsGroups">
<input type="checkbox" ng-model="group.selected" ng-change="toggleGroup(group)"/> {{group.title}}
<ul>
<li ng-repeat="prop in group.props">
<input ng-change="selectProp(group)" ng-model="prop.selected" type="checkbox"/> {{prop.title}}
</li>
</ul>
</div>
<pre>
{{columnsGroups | json}}
</pre>
</div>
<script src="script.js"></script>
</body>

Vue toggle between marking tasks not working

I am asking this again because I don't understand what I am doing wrong. This is a tutorial I did and I did exactly as the tutorial went. I am trying to toggle between tasks being marked done and not done. When I run the code it does not toggle and there are no errors. I read the documentation but I don't fully understand. I am new to programming.
let bus = new Vue();
let Task = {
props: ['task'],
template: `
<div class="task" :class="{ 'task--done' : task.done , 'task-notdone' : task.done === false }">
{{ task.body }}
Mark me as {{ task.done ? 'not done' : 'done' }}
</div>
`,
methods: {
toggleDone(taskId) {
bus.$emit('task:toggleDone', taskId);
}
}
};
let Tasks = {
components:{
'task': Task
},
data() {
return {
tasks: [
{id: 1, body: 'Task One', done: false },
{id: 2, body: 'Task Two', done: true },
{id: 3, body: 'Task Three', done: true }
],
}
},
template: `
<div>
<template v-if="tasks.length">
<task v-for="task in tasks" :key="task.id" :task="task"></task>
</template>
<span v-else>No tasks</span>
<form action="">
form
</form>
</div>
`,
methods: {
toggleDone(taskId){
let task = this.tasks.find(function (task) {
return task.id === taskId;
});
console.log(task);
}
},
mounted () {
bus.$on('task:toggleDone', (taskId) => {
this.toggleDone(taskId);
})
},
};
let app = new Vue({
el:'#app',
components: {
'tasks': Tasks,
},
});
I'm not sure why the tutorial was leading you to using a bus; it's just not needed here. There is a list of tasks that are javascript objects and each task object is being passed to the task component. Since it is a javascript object, and not a primitive value, you can update the done property in the task component.
console.clear()
let Task = {
props: ['task'],
template: `
<div class="task" >
<span :class="{ 'task--done' : task.done , 'task-notdone' : !task.done}">{{ task.body }}</span>
Mark me as {{ task.done ? 'not done' : 'done' }}
</div>
`
};
let Tasks = {
components:{
'task': Task
},
data() {
return {
tasks: [
{id: 1, body: 'Task One', done: false },
{id: 2, body: 'Task Two', done: true },
{id: 3, body: 'Task Three', done: true }
],
}
},
template: `
<div>
<template v-if="tasks.length">
<task v-for="task in tasks" :key="task.id" :task="task"></task>
</template>
<span v-else>No tasks</span>
</div>
`,
};
let app = new Vue({
el:'#app',
components: {
'tasks': Tasks,
},
});
.task--done{
text-decoration: line-through
}
<script src="https://unpkg.com/vue#2.5.2/dist/vue.js"></script>
<div id="app">
<tasks></tasks>
</div>
Additionally, if you don't want to mutate the object in the component, you can instead emit an event that lets the parent mutate it.
console.clear()
let Task = {
props: ['task'],
template: `
<div class="task" >
<span :class="{ 'task--done' : task.done , 'task-notdone' : !task.done}">{{ task.body }}</span>
Mark me as {{ task.done ? 'not done' : 'done' }}
</div>
`
};
let Tasks = {
components:{
'task': Task
},
data() {
return {
tasks: [
{id: 1, body: 'Task One', done: false },
{id: 2, body: 'Task Two', done: true },
{id: 3, body: 'Task Three', done: true }
],
}
},
template: `
<div>
<template v-if="tasks.length">
<task v-for="task in tasks" :key="task.id" :task="task" #toggle-task="toggleTask"></task>
</template>
<span v-else>No tasks</span>
</div>
`,
methods:{
toggleTask(task){
task.done = !task.done
}
}
};
let app = new Vue({
el:'#app',
components: {
'tasks': Tasks,
},
});
.task--done{
text-decoration: line-through
}
<script src="https://unpkg.com/vue#2.5.2/dist/vue.js"></script>
<div id="app">
<tasks></tasks>
</div>

NG-Repeat to produce a list with dynamic classes

I'm trying to print as an li any item that matches the index of the first loop.
For example: first loop makes li's classed as cat1_li, 2nd one as cat2_li, 3rd one as cat3_li
However the closest I can get prints them all out in separate lists.
Having real trouble getting my head around using ng-repeats inside other ng-repeats and can't seem to find where the issue is. I can see why it is printing multiple lists, but not how to remedy this behaviour.
Would anyone be willing to take a look?
HTML:
<div>
<ul class="tasks" ng-repeat="cat in taskCategories.categories">
{{ cat }}
<li ng-repeat="tasks in tasklist.tasks | orderBy:'category' | filter: {category: cat}" class="cat{{ index + 1 }}_li">
<span>
<span class="panel_title">
{{ tasks.title }}
</span>
</span>
</li>
</ul>
</div
Object:
app.controller('MainController', ['$scope', function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning',
'lifting'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}, {
title: '400 lbs',
category: 'lifting'
}
]
};
}]);
You can use $parent.$index if you're trying to use the Category's index inside the nested ng-repeat:
var app = angular.module('app', []);
app.controller('MainController', ['$scope',
function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning',
'lifting'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}, {
title: '400 lbs',
category: 'lifting'
}]
};
}
]);
.cat1_li {
background-color: yellow;
}
.cat2_li {
background-color: cyan;
}
.cat3_li {
background-color: pink;
}
.cat4_li {
background-color: lime;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController">
<ul class="tasks" ng-repeat="cat in taskCategories.categories">
{{ cat }}
<li ng-repeat="tasks in tasklist.tasks | orderBy:'category' | filter: {category: cat}" class="cat{{ $parent.$index + 1 }}_li">
<span>
<span class="panel_title">
{{ tasks.title }}
</span>
</span>
</li>
</ul>
</div>
UPDATE
After reading your comment in the question, you don't need two ng-repeat at all:
var app = angular.module('app', []);
app.controller('MainController', ['$scope',
function($scope) {
$scope.taskCategories = {
categories: [
'work',
'chores',
'learning',
'lifting'
]
};
$scope.tasklist = {
tasks: [{
title: 'Email Gregory',
category: 'work'
}, {
title: 'Clean the Kitchen',
category: 'chores'
}, {
title: 'AngularJS',
category: 'learning'
}, {
title: 'Hose Car',
category: 'chores'
}, {
title: 'Email Jethro',
category: 'work'
}, {
title: '400 lbs',
category: 'lifting'
}]
};
$scope.mappedTasks = $scope.tasklist.tasks.map(function(task) {
task.category = $scope.taskCategories.categories.indexOf(task.category);
return task;
}).sort(function(a, b) {
return a.category > b.category;
});;
console.log($scope.mappedTasks);
}
]);
.cat1_li {
background-color: yellow;
}
.cat2_li {
background-color: cyan;
}
.cat3_li {
background-color: pink;
}
.cat4_li {
background-color: lime;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController">
<ul class="tasks">
<li ng-repeat="task in mappedTasks" class="cat{{ task.category + 1 }}_li">
<span>
<span class="panel_title">
{{ task.title }}
</span>
</span>
</li>
</ul>
</div>
Each ng-repeat creates a child scope with the passed data, and also adds an additional $index variable in that scope.
So what you need to do is reach up to the parent scope, and use that $index.
HTML :
<li ng-repeat="tasks in tasklist.tasks | orderBy:'category' | filter: {category: cat}" class="cat{{ $parent.$index + 1 }}_li">

Categories

Resources