How to call a function with arguments for a button on click event in each block RactiveJS? - javascript

I'm using RactiveJS 1.4.1 and asp.net mvc core razor pages.
I put the html to a script-template in the page. And call it in RactiveJS to render. Rendering works fine, but button click action, the method "addToCard" is not working. I tried to move addToCard function under data section - not works. Also I tried to call addToCard function like this "ractive.on( 'addToCart' ..." It not worked too.
Here is the code;
#{
ViewData["Title"] = "Ractive Examples - ForEach";
}
<div class="card-body">
<script id="testTemplate" type="text/html">
<h1>Let's shop!</h1>
<ul>
{{#each items: i}}
<li>
<p>{{i+1}}: {{description}}</p>
<label><input value='{{qty}}'> Quantity</label>
<!-- when the user clicks this button, add {\{qty}} of this item -->
<button on-click="addToCart:{{this}},{{qty}}" type="button">Add to cart</button>
</li>
{{/each}}
</ul>
</script>
<div id="container"></div>
</div>
#section Scripts
{
<script>
// Predefined data, items to be added to cart.
const items_data = [
{ description: "Asset 1", qty: 1 },
{ description: "Asset 2", qty: 21 },
{ description: "Asset 3", qty: 35 },
{ description: "Asset 4", qty: 0 },
{ description: "Asset 5", qty: 5 }
];
const ractive = new Ractive({
el: '#container',
template: "#testTemplate",
data: {
items: items_data
},
on: {
addToCart: function ( event, item, qty ) {
// Do something with the item and qty
console.log( item );
console.log( qty );
}
}
});
</script>
}
I took this example directly from RactiveJS documentation; https://www.ractivejs.org/docs/latest/proxy-events.html - But this is not working too (button displays nothing)...
#{
ViewData["Title"] = "Ractive Examples - ForEach";
}
<div class="card-body">
<script id="testTemplate" type="text/html">
<h1>Let's shop!</h1>
<ul>
{{#each items: i}}
<li>
<p>{{i+1}}: {{description}}</p>
<label><input value='{{qty}}'> Quantity</label>
<!-- when the user clicks this button, add {\{qty}} of this item -->
<button on-click='addToCart:{{this}},{{qty}}'>Add to cart</button>
</li>
{{/each}}
</ul>
</script>
<div id="container"></div>
</div>
#section Scripts
{
<script>
// Predefined data, items to be added to cart.
const items_data = [
{ description: "Asset 1", qty: 1 },
{ description: "Asset 2", qty: 21 },
{ description: "Asset 3", qty: 35 },
{ description: "Asset 4", qty: 0 },
{ description: "Asset 5", qty: 5 }
];
const ractive = new Ractive({
el: '#container',
template: "#testTemplate",
data: {
items: items_data
}
});
ractive.on( 'addToCart', function ( event, item, qty ) {
console.log( 'Adding ' + qty + ' of ' + item.description + ' to cart');
});
</script>
}
What am I missing?
Update: I can use data- attribute, i know but I'm searching for better way to do it.*
const ractive = Ractive({
el: '#container',
template: `
<ul>
{{#each items: i}}
<li>
<p>{{i+1}}: {{description}}</p>
<label><input value='{{qty}}'> Quantity</label>
<button type="button" data-id="{{i}}" on-click="addToCart">Push me!</button>
</li>
{{/each}}
</ul>
`,
data: {
items: items_data
},
on: {
addToCart (event) {
const id = event.node.getAttribute('data-id');
console.log("Item of the id is: " + id);
}
}
});

Related

KnockoutJS - How to hide certain elements inside foreach using Observable Arrays?

I have a list of WebsiteOwners. I'm trying to build a UI which will display more information about the owners when I click on them.
this.toExpand = ko.observableArray(); //initialize an observable array
this.invertExpand = ko.observable("");
this.invertExpand = function (index) {
if (self.invertExpand[index] == false) {
self.invertExpand[index] = true;
alert(self.invertExpand[index]); //testing whether the value changed
}
else {
self.invertExpand[index] = false;
alert(self.invertExpand[index]); //testing whether the value changed
}
};
Here's the HTML code :
<div data-bind="foreach: WebsiteOwners">
<div>
<button data-bind="click: $root.invertExpand.bind(this,$index())" class="label label-default">>Click to Expand</button>
</div>
<div data-bind="visible: $root.toExpand()[$index]">
Primary Owner: <span data-bind="text:primaryOwner"></span>
Website Name : <span data-bind="text:websiteName"></span>
//...additional information
</div>
</div>
You can store one of your WebsiteOwner items directly in your observable. No need to use an index.
Don't forget you read an observable by calling it without arguments (e.g. self.invertExpand()) and you write to it by calling with a value (e.g. self.invertExpand(true))
I've included 3 examples in this answer:
One that allows only a single detail to be opened using knockout
One that allows all details to be opened and closed independently using knockout
One that does not use knockout but uses plain HTML instead 🙂
1. Accordion
Here's an example for a list that supports a single expanded element:
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
const selectedOwner = ko.observable(null);
const isSelected = owner => selectedOwner() === owner;
const toggleSelect = owner => {
selectedOwner(
isSelected(owner) ? null : owner
);
}
ko.applyBindings({ websiteOwners, isSelected, toggleSelect });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: { data: websiteOwners, as: 'owner' }">
<li>
<span data-bind="text: name"></span>
<button data-bind="
click: toggleSelect,
text: isSelected(owner) ? 'collapse' : 'expand'"></button>
<div data-bind="
visible: isSelected(owner),
text: role"></div>
</li>
</ul>
2. Independent
If you want each of them to be able to expand/collapse independently, I suggest adding that state to an owner viewmodel:
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
const OwnerVM = owner => ({
...owner,
isSelected: ko.observable(null),
toggleSelect: self => self.isSelected(!self.isSelected())
});
ko.applyBindings({ websiteOwners: websiteOwners.map(OwnerVM) });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: websiteOwners">
<li>
<span data-bind="text: name"></span>
<button data-bind="
click: toggleSelect,
text: isSelected() ? 'collapse' : 'expand'"></button>
<div data-bind="
visible: isSelected,
text: role"></div>
</li>
</ul>
3. Using <details>
This one leverages the power of the <details> element. It's probably more accessible and by far easier to implement!
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
ko.applyBindings({ websiteOwners });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: websiteOwners">
<li>
<details>
<summary data-bind="text: name"></summary>
<div data-bind="text: role"></div>
</details>
</li>
</ul>

How get a load more button for a li list vue js

I'm trying to implement a load more button to my code. I would be able to this in javascript but I can't find a similar way in vue.
This is my vue code. I've tried asking the element with the company id but it's not reactive so I can't just change the style.
<main>
<ul>
<li v-for="company in companiesdb" :key="company.id" v-bind:id="company.id" ref="{{company.id}}" style="display: none">
{{company.name}}<br>
{{company.email}}
</li>
</ul>
</main>
this is my failed atempt of doing it in javascript but as I've mentioned before ref is not reactive so I can't do it this way
limitView: function (){
const step = 3;
do{
this.numberCompaniesVis ++;
let li = this.$refs[this.numberCompaniesVis];
li.style = "display: block";
}while (this.numberCompaniesVis % 3 != step)
}
I think the way you are approaching this problem is a little complex. Instead, you can create a computed variable that will change the number of lists shown.
Here's the code
<template>
<div id="app">
<ul>
<li v-for="(company, index) in companiesLoaded" :key="index">
{{ company }}
</li>
</ul>
<button #click="loadMore">Load</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
companiesdb: [3, 4, 1, 4, 1, 2, 4, 4, 1],
length: 5,
};
},
methods: {
loadMore() {
if (this.length > this.companiesdb.length) return;
this.length = this.length + 3;
},
},
computed: {
companiesLoaded() {
return this.companiesdb.slice(0, this.length);
},
},
};
</script>
So instead of loading the list from companiesdb, create a computed function which will return the new array based on companiesdb variable. Then there's the loadMore function which will be executed every time user clicks the button. This function will increase the initial length, so more lists will be shown.
Here's the live example
Just use computed property to create subset of main array...
const vm = new Vue({
el: '#app',
data() {
return {
companies: [
{ id: 1, name: "Company A" },
{ id: 2, name: "Company B" },
{ id: 3, name: "Company C" },
{ id: 4, name: "Company D" },
{ id: 5, name: "Company E" },
{ id: 6, name: "Company F" },
{ id: 7, name: "Company G" },
{ id: 8, name: "Company H" },
{ id: 9, name: "Company I" },
{ id: 10, name: "Company J" },
],
companiesVisible: 3,
step: 3,
}
},
computed: {
visibleCompanies() {
return this.companies.slice(0, this.companiesVisible)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="company in visibleCompanies" :key="company.id" :id="company.id">
{{company.name}}
</li>
</ul>
<button #click="companiesVisible += step" v-if="companiesVisible < companies.length">Load more...</button>
</div>

Combine the same attribute from different arrays in href (vue.js)

I’m new to vue.js. I’m building a wizard, but one step I can’t figure out.
I’ve got a checkbox list that outputs the list a user has choosen. So far so good.
In the same array there is an urlAnchor that needs to be combined in the final url.
So for instance, if the user selects extra1 and extra2, the list will be:
List view
Product: Extra 1
Price: 129
URL Anchor: /iii
Product: Extra 2
Price: 49
URL Anchor: /jjj
URL
URL needs to be google.com/iii/jjj
But I don’t know how to combine the 2 url anchors in 1 url.
Can someone help me with this please?
My code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<!-- wizard -->
<div id="app">
<div v-for="(extraItem, index) in extra" v-bind:id="'extraItem-'+extraItem.id" :class="'w-1/2 overflow-hidden my-1 px-1 item-'+index" >
<input type="checkbox" class="extraCheckboxes" :id="extraItem.name" :value="extraItem.name" v-model="checkedExtras" #click="saveExtra(index)">
<label class="form-check-label" :for="extraItem.id">{{extraItem.name}}</label>
</div>
<h1>Output:</h1>
<h2> List view </h2>
<div v-for="extra in extraOutput">
<strong>Product:</strong> {{extra.name}} <br />
<strong>Price:</strong> <span class="items" :data-value="extra.price">{{extra.price}}</span><br />
URL Anchor: {{extra.urlAnchor}}
<p> </p>
</div>
<h2> URL </h2>
<button><a v-for="extra in extraOutput" :href="'https://google.be'+extra.urlAnchor">Button text</a></button>
</div>
<!-- scripts -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js'></script>
<script type="text/javascript">
function extras() {
return [
{ id: 1, name: "Extra 1", price: 129, urlAnchor:"/iii" , selected: false },
{ id: 2, name: "Extra 2", price: 49, urlAnchor:"/jjj" , selected: false },
{ id: 3, name: "Extra 3", price: 59, urlAnchor:"/ggg" , selected: false },
{ id: 4, name: "Extra 4", price: 69, urlAnchor:"/hhh" , selected: false }];
}
new Vue({
el: "#app",
data() {
return {
extra: extras(),
checkedExtras: [],
data: [],
};
},
methods: {
saveExtra: function (index) {
this.extra[index].selected = !this.extra[index].selected;
}
},
computed: {
extraOutput: function () {
let extra = this.extra.filter(function (item) {
return item.selected === true;
});
return extra;
}
}
});
</script>
</body>
</html>
You've already done most of the work with by writing the extraOutput computed.
computed: {
extraOutput() {
const anchorsOfSelectedExtras = this.extra
.filter(extra => extra.selected) //get array of only selected
.map(extra => extra.urlAnchor); //turn array of objects into array of strings
return anchorsOfSelectedExtras.join(''); //turn array of strings into one joined string
}
}
You can also go even shorter:
computed: {
extraOutput() {
return this.extra.reduce((accumulator, extra) => extra.selected ? [...accumulator, extra.urlAnchor] : accumulator, []).join('');
}
}

Vue Survey not indexing questions

I have to build a quiz/survey app in vue.js, I'm pretty new to vue and still trying to learn it. I have a quiz/survey that asks different questions depending on what the user answers in the initial question.
so if the user picks yes it will display question 2 if the user picks no it will display question 3 etc.
I'm not sure what the best way of going around it but so far I have this.
Is there anyway i can use the value of my answer as the questionIndex after a person clicks next?
JS file:
"use strict";
let quiz = {
title: "Asbestos Quiz",
questions: [
{
text: 'Do you need help with an Asbestos Survey?',
answers: [
{
text: "Yes",
value: '2'`enter code here`
},
{
text: "No",
value: '3'
},
]
},
{
text: 'Was your property constructed pre 2000',
answers: [
{
text: "Yes",
value: '4'
},
{
text: "No",
value: '5'
},
]
},
{
text: 'Do you need an Asbestos Management plan?',
answers: [
{
text: "Yes",
value: '6'
},
{
text: "No",
value: '7'
},
]
}
]
};
var app = new Vue({
el: "#app",
data: {
quiz: quiz,
questionIndex: 0,
responses: [],
errors: [],
error: ''
},
methods: {
prev: function() {
this.questionIndex--;
},
next: function() {
if (this.responses[this.questionIndex] === undefined) {
this.errors[this.questionIndex] = 1;
this.error = 'Please select your answer';
}
else {
this.errors[this.questionIndex] = 0;
this.questionIndex++;
}
},
score: function() {
},
playAgain: function() {
this.questionIndex = 0;
}
}
});
HTML:
<html lang="en">
<head>
<title>Vue quiz/survey</title>
<meta name="viewport" content="width=device-width"/>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- <link rel="stylesheet" href="index.css"> -->
</head>
<body>
<div id="app">
<div class="container">
<div class="jumbotron mt-3">
<h1 class="mb-5">{{ quiz.title }}</h1>
<hr>
<p v-if="errors[questionIndex]" class="alert alert-danger">
{{ error }}
</p>
<div v-for="(question, index) in quiz.questions">
<div v-show="index === questionIndex">
<h4 class="mt-5 mb-3">{{ question.text }}</h4>
<div v-for="answer in question.answers" class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio"
:value="answer.value"
:name="index"
v-model="responses[index]">
{{answer.text}}
</label>
</div>
<div class="mt-5">
<button
class="btn btn-primary"
v-if="questionIndex > 0"
#click="prev">
prev
</button>
<button class="btn btn-secondary" #click="next">
next
</button>
</div>
</div>
</div>
<div v-show="questionIndex === quiz.questions.length">
<h3>Your Results</h3>
<p>
You are: {{ score() }}
</p>
<button class="btn btn-success" #click="playAgain">
Play Again!
</button>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
I thought that this sounded like a potentially interesting exercise, so I spent some time creating an implementation in a Vue CLI sandbox app that I built and use for trying out various ideas.
I learned a few things, and hopefully you will get something out of it. I left the 'Previous' functionality as a TODO if you decide you like it and want implement that yourself.
QuizQuestions.vue
<template>
<div class="quiz-questions">
<div class="jumbotron mt-3">
<h1 class="mb-5">{{ quiz.title }}</h1>
<hr>
<question v-if="!showResults" :question="currentQuestion" #answer-selected="processAnswer" />
<div class="mt-5">
<button class="btn btn-primary" v-if="currentQuestionId > 1 && !showResults" #click="getPreviousQuestion">
prev
</button>
<button v-if="!showResults" class="btn btn-secondary" #click="getNextQuestion">
{{ nextButtonLabel }}
</button>
</div>
<div v-if="showResults">
<h3>Your Results</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>QUESTION</th>
<th>ANSWER</th>
</tr>
</thead>
<tbody>
<tr v-for="(response, index) in responses" :key="index">
<td>{{ getQuestionText(response.questionId) }}</td>
<td>{{ getAnswerText(response.answerId) }}</td>
</tr>
</tbody>
</table>
<button class="btn btn-success" #click="playAgain">
Play Again!
</button>
</div>
</div>
</div>
</template>
<script>
import quiz from './quiz.js';
import Question from '#/components/stackoverflow/Question'
export default {
components: {
Question
},
data() {
return {
quiz: quiz,
currentQuestionId: 1,
currentAnswerId: 1,
previousQuestionId: 0,
responses: [],
showResults: false,
errors: [],
error: ''
}
},
computed: {
currentQuestion() {
return this.quiz.questions.find( question => {
return question.id === this.currentQuestionId;
})
},
nextQuestionId() {
let retVal = 0;
if (this.currentAnswerId > 0) {
let tempAnswer = this.currentQuestion.answers.find( answer => {
return answer.id === this.currentAnswerId;
});
retVal = tempAnswer.nextQuestionId;
}
return retVal;
},
lastQuestion() {
return this.currentQuestion.answers[0].nextQuestionId === 0;
},
nextButtonLabel() {
return this.lastQuestion ? 'Finish' : 'Next';
}
},
methods: {
getPreviousQuestion() {
this.currentQuestionId = this.previousQuestionId;
},
getNextQuestion() {
// TODO: Look for existing response for this question in case the 'Previous' button was pressed
// If found, update answer
// Store current question id and answer id in responses
let response = { questionId: this.currentQuestionId, answerId: this.currentAnswerId };
this.responses.push(response);
if (this.lastQuestion) {
this.showResults = true;
return;
}
this.previousQuestionId = this.currentQuestionId;
this.currentQuestionId = this.nextQuestionId;
//console.log(this.responses);
},
getQuestionText(id) {
let result = this.quiz.questions.find( question => {
return question.id === id;
});
return result.text;
},
getAnswerText(id) {
// NOTE: Since answers are currently limited to '1 = Yes' and '2 = No',
// this method does not need to involve any look up
return id === 1 ? 'Yes' : 'No';
},
processAnswer(selectedAnswerId) {
this.currentAnswerId = selectedAnswerId;
},
score() {
return 'TODO'
},
playAgain() {
this.currentQuestionId = 1;
this.showResults = false;
this.responses = [];
}
}
}
</script>
Question.vue
<template>
<div class="question">
<h4 class="mt-5 mb-3">{{ question.text }}</h4>
<div class="form-check" v-for="(answer, idx) in question.answers" :key="idx">
<input class="form-check-input" type="radio"
:value="answer.id" v-model="answerId" #change="answerSelected">
<label class="form-check-label">
{{answer.text}}
</label>
</div>
</div>
</template>
<script>
export default {
props: {
question: {
type: Object,
required: true
}
},
data() {
return {
answerId: 1
}
},
watch:{
question() {
// Reset on new question
this.answerId = 1;
}
},
methods: {
answerSelected() {
this.$emit('answer-selected', this.answerId);
}
}
}
</script>
I also modified your test data by adding various ID properties to help with tracking, as well as created a few more placeholder questions.
quiz.js
const quiz = {
title: "Asbestos Quiz",
questions: [
{
id: 1,
text: 'Do you need help with an Asbestos Survey?',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 2
},
{
id: 2,
text: "No",
nextQuestionId: 3
},
]
},
{
id: 2,
text: 'Was your property constructed pre 2000',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 4
},
{
id: 2,
text: "No",
nextQuestionId: 5
},
]
},
{
id: 3,
text: 'Do you need an Asbestos Management plan?',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 6
},
{
id: 2,
text: "No",
nextQuestionId: 7
},
]
},
{
id: 4,
text: 'Question 4',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 5,
text: 'Question 5',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 6,
text: 'Question 6',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 7,
text: 'Question 7',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
}
]
};
export default quiz;

Generate a list with a head based on object property in array

In Vue, i'm trying to generate a list based on a property in an object.
I have an array coming from the vuex store that looks like this:
const array = [
{
name: "British title string"
nationality: "British"
},
{
name: "Another title"
nationality: "American"
},
{
name: "Name"
nationality: "Dutch"
},
{
name: "Another american item"
nationality: "American"
},
];
What i want to have is the output like this using v-for:
<h2>British</h2>
<ul>
<li>British title string</li>
</ul>
<h2>American</h2>
<ul>
<li>Another title</li>
<li>Another american item</li>
</ul>
<h2>Dutch</h2>
<ul>
<li>Name</li>
</ul>
I have already sorted the array by the nationality property using lodash _.sortBy and it's given me an array sorted by nationality but i want to add a H2 element with the value of nationality.
If you have multiple items with same nationality and you want to group them up, use _.groupBy() before _.sortBy():
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
template: '#appTemplate',
data: () => ({
rawData: [{
name: "British title string",
nationality: "British"
},
{
name: "Another title",
nationality: "American"
},
{
name: "Name",
nationality: "Dutch"
},
{
name: "Another american item",
nationality: "American"
}
]
}),
computed: {
groupedItems() {
return _.sortBy(
_.map(
_.groupBy(this.rawData, 'nationality'),
items => ({
items,
nationality: items[0].nationality
})
),
['nationality']
);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script type="text/template" id="appTemplate">
<div>
<template v-for="group in groupedItems">
<h2 v-text="group.nationality" />
<ul>
<li v-for="(item, index) in group.items" :key="index" v-text="item.name" />
</ul>
</template>
</div>
</script>
<div id="app"></div>
For convenience and data readability, I mapped first item's nationality as the group's nationality (and named the grouped items items), but you could have used item 0's nationality in the template directly, instead.
To demonstrate, here's how that would have looked like:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
template: '#appTemplate',
data: () => ({
rawData: [{
name: "British title string",
nationality: "British"
},
{
name: "Another title",
nationality: "American"
},
{
name: "Name",
nationality: "Dutch"
},
{
name: "Another american item",
nationality: "American"
}
]
}),
computed: {
groupedItems() {
return _.sortBy(
_.groupBy(this.rawData, 'nationality'),
['0.nationality']
);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script type="text/template" id="appTemplate">
<div>
<template v-for="grouped in groupedItems">
<h2 v-text="grouped[0].nationality" />
<ul>
<li v-for="(item, index) in grouped" :key="index" v-text="item.name" />
</ul>
</template>
</div>
</script>
<div id="app"></div>
Note both of the above examples output the required markup. If you want each item to have its own wrapper, replace <template> with an html tag and add keys. i.e:
<article v-for="(grouped, key) in groupedItems" :key="key">
You can do this, considering that the array is ordered:
const array = [
{
name: "Another title",
nationality: "American"
},
{
name: "Another american item",
nationality: "American"
},
{
name: "British title string",
nationality: "British"
},
{
name: "Name",
nationality: "Dutch"
},
];
const arrayAux = [];
var j = 0;
for (var i = 0; i < array.length;) {
console.log(array[i].name);
arrayAux.push({
nationality: array[i].nationality,
names: [array[i].name]
});
i++;
while ( i < array.length && array[i].nationality === array[i - 1].nationality) {
arrayAux[j].names.push(array[i].name);
i++;
}
j++;
}
console.log(arrayAux);
In the html:
<div v-for="item in arrayAux">
<h2>{{ item.nationality }}</h2>
<ul>
<li v-for="n in item.names">
{{ n.name }}
</li>
</ul>
</div>

Categories

Resources