VueJS - How can I bind multiple class from object that created by v-for? - class

I wanted to make it like this:
<ul>
<li class="aaa active">text-aaa</li>
<li class="bbb">text-bbb</li>
<li class="ccc">text-ccc</li>
</ul>
Here's the code. https://jsfiddle.net/qwvwsgb9/
I can bind value calculated by v-for by using non-object format :class="v.name"
<div id="app">
<ul>
<li v-for="v, i in listAry" :class="{v.name:true,active:active==i}">{{ v.text }}</li>
</ul>
</div>
script:
let vm = new Vue({
el: "#app",
data: {
active:0,
listAry: [{
name: 'aaa',
text: 'text-aaa'
}, {
name: 'bbb',
text: 'text-bbb'
}, {
name: 'ccc',
text: 'text-ccc'
}]
}
})
but as long as I tried to apply it in object format, the error occurs.
How can I do it?

Try something like this :class="[{ active: active === i }, v.name]"
<div id="app">
<ul>
<li v-for="v, i in listAry" :class="[{ active: active === i }, v.name]">
{{ v.text }}
</li>
</ul>
</div>

Related

$route.params.id dont want to give id data when clicking on drink. CocktailDB API, nuxt.js

Hello Im doing a simple cocktailapp to practice nuxt.js and axios with coktailDB API https://www.thecocktaildb.com/api.php. In drinks/index.vue I have listed all drinks with v-for. When clicking on a drink you get to drinks/_id/index.vue page where the full info about the drink will get showed by its ID. Im using $route.params.id and I have tried with this.$route.params.id. No drink info get showed. Its just shows undefined and brackets. How can I get the drink info to be showed by its ID after been clicking on a specific drink? Thanks in advance!
Drinks/index.vue;
<template>
<div>
<div>
<SearchDrink/>
</div>
<div>
<div v-for="drink in drinks" :key="drink.id">
<nuxt-link :to="'drinks/' + id">
<div class="drink">
<p> {{ drink.strDrink
}} </p>
<img :src="drink.strDrinkThumb" alt=""/>
<p>Instructions:</p>
<p> {{ drink.strInstructions }} </p>
<div class="ing"> Ingridients:
<ul>
<li>{{ drink.strIngredient1 }} </li>
<li>{{ drink.strIngredient2 }} </li>
<li>{{ drink.strIngredient3 }} </li>
<li>{{ drink.strIngredient4 }} </li>
<li>{{ drink.strIngredient5 }} </li>
</ul>
</div>
</div>
</nuxt-link>
</div>
</div>
</div>
</template>
<script>
import SearchDrink from '../../components/SearchDrink.vue'
import axios from 'axios'
export default {
components:{
SearchDrink,
},
data(){
return {
drinks: [],
}
},
methods: {
getAllDrinks(){
axios.get('https://thecocktaildb.com/api/json/v1/1/search.php?s=')
.then((response) => {
this.drinks = response.data.drinks
const myDrink = response.data.drinks
console.log(myDrink)
console.log(myDrink.strDrinkThumb)
})
.catch((error) =>{
console.log(error)
})
},
},
created(){
this.getAllDrinks()
},
// methods: {
// searchDrink(){
// if(!this.search){
// return this.drinks
// }else{
// return this.drinks.filter(drink =>
// drink.text.toLowerCase().includes(this.search.
// toLowerCase()))
// }
// }
// },
head(){
return {
title: 'Drinks App',
meta: [
{
hid: 'description',
name: 'description',
content: 'Best place to search a Drink'
}
]
}
}
}
</script>
Drinks/_id/index.vue;
<template>
<div>
<nuxt-link to="/drinks">
Go Back
</nuxt-link>
<h2> {{ drink }} </h2>
<hr>
<small>Drink ID: {{ this.$route.params.id }}</small>
</div>
</template>
<script>
import axios from 'axios'
export default {
data(){
return{
drink: {}
}
},
methods: {
getAllDrinks(){
axios.get(`www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${this.$route.params.id}`)
.then((response) => {
this.drinks = response.data.drinks
const myDrink = response.data.drinks
console.log(myDrink)
})
.catch((error) =>{
console.log(error)
})
},
},
created(){
this.getAllDrinks()
},
head(){
return {
title: this.drink,
meta: [
{
hid: 'description',
name: 'description',
content: 'Best place to search a Drink'
}
]
}
}
}
</script>
There are quite a few things that be improved here (and the API is a bit messy too).
Here is how I would do it with modern practices.
/pages/drinks/index.vue
<template>
<div>
<div v-for="drink in drinks" :key="drink.idDrink">
<nuxt-link :to="`/drinks/${drink.idDrink}`">
<div class="drink">
<p>{{ drink.strDrink }}</p>
<img width="100px" height="100px" :src="drink.strDrinkThumb" alt="" />
<p>Instructions:</p>
<p>{{ drink.strInstructions }}</p>
<div class="ing">
<p>Ingredients:</p>
<ul>
<li>{{ drink.strIngredient1 }}</li>
<li>{{ drink.strIngredient2 }}</li>
<li>{{ drink.strIngredient3 }}</li>
<li>{{ drink.strIngredient4 }}</li>
<li>{{ drink.strIngredient5 }}</li>
</ul>
</div>
</div>
</nuxt-link>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
drinks: [],
}
},
async fetch() {
await this.getAllDrinks()
},
methods: {
async getAllDrinks() {
try {
const { data } = await axios.get(
'https://thecocktaildb.com/api/json/v1/1/search.php?s='
)
this.drinks = data.drinks
} catch (error) {
console.log('error', error)
}
},
},
}
</script>
/pages/drinks/_id.vue
<template>
<div>
<nuxt-link to="/drinks"> Go Back </nuxt-link>
<h2>{{ drink.strGlass }}</h2>
<hr />
<small>Drink ID: {{ $route.params.id }}</small>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
drink: {},
}
},
async fetch() {
await this.getAllDrinks()
},
methods: {
async getAllDrinks() {
try {
const { data } = await axios.get(
`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${this.$route.params.id}`
)
this.drink = data.drinks[0]
} catch (error) {
console.log('error', error)
}
},
},
}
</script>
Few notes:
I skipped the parts I don't have access to (like your SearchDrink component) or that are not relevant with the current issue
you probably would need to use the axios module
you don't need to import your Nuxt components, it is done for you

why won't my code update when I add a new Item to the list

I am trying to allow an item to be pushed to a list of times that I looped through using Vue.js. I don't understand why when I click on the button the list item appears but the text doesn't.
HTML
<div id="root">
<input v-modle="newCat" v-on:keyup.enter="addKitty">
<button v-on:click="addKitty">
+add
</button>
<ul>
<li v-for="cat in cats">{{ cat.name }}</li>
</ul>
</div>
Vue.js
app = new Vue({
el: '#root',
data: {
cats:
[{name: 'kitkat'},
{ name: 'fish'},
{ name: 'henry'},
{ name: 'bosco'}],
//new data set
newCat: ''
},
methods: {
addKitty: function() {
this.cats.push({
name: this.newCat
})
this.newCat = ''
}
}
})
There is a typo in your code.
<input v-modle="newCat" v-on:keyup.enter="addKitty">
Should be:
<input v-model="newCat" v-on:keyup.enter="addKitty">
Notice the different spelling for v-model.

Nesting {{#each}} in Handlebars 304 error

My Nested {{#each}} does not work.
I am Scraping article titles to my page and I'm using MongoDB/Mongoose. For each article (that I named News) I want user to post comments and view previous comments.
My routes, console.log, and the database itself shows that my api routes are working, but I can't get the comments to display.
My handlebars has the articles rendering to screen, but when GETting the comments it returns a 304.
The Articles display as I intend, it only has a problem with the comments
{{#each News}} is each article
{{#each comment}} is nested inside each News.
{{#each News}}
<div class="panel-block is-active">
<ul>
<li>
<div class="level-left">
<h2 class="subtitle">
<a href="https://www.atptour.com{{this.url}}">
{{this.headline}}
</a>
</h2>
</div>
</li>
<li>
<img width="348" src="https://www.atptour.com{{this.thumbNail}}">
</li>
<li>
<p>{{category}}</p>
</li>
<li>
{{!-- on click remove class .is-hidden on #getComments --}}
<p id="showComments" data-id="{{this._id}}" >Comments</p>
</li>
<li>
{{#each comment}}
<article id="getComments" class="media is-hidden">
<div class="media-content">
<div class="content">
<p>
<small>{{this.time}}</small>
<br />
{{this.body}}
</p>
</div>
</div>
<div class="media-right">
<button class="delete"></button>
</div>
</article>
{{/each}}
{{!-- on click remove class .is-hidden on #postComments --}}
<a id="addComment" >Add Comment</a>
{{!-- TO POST A COMMENT --}}
<article id="postComment" class="media is-hidden">
<div class="media-content">
<div class="field">
<p class="control">
<textarea
class="textarea"
placeholder="Add a comment..."
></textarea>
</p>
</div>
<div class="level">
<div class="level-left">
<div class="level-item">
<a class="button is-info">Submit</a>
</div>
</div>
</div>
</div>
</article>
</li>
</ul>
</div>
{{/each}}
In the {{#each comment}} {{this.time}} and {{this.body}} is blank. But if I simply put {{this}} I get the _id if each comment. The context must be wrong but I don't know why. The comments are properly associated to the News because I can check the get route and it's all there correctly.
Here are the schemas if it helps
new articles
const NewsSchema = new Schema({
headline: {
type: String,
required: true,
},
thumbNail: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
// Comments will populate the News
comment: [
{
type: Schema.Types.ObjectId,
ref: "Comment",
},
],
});
const News = mongoose.model("News", NewsSchema);
comments
const CommentSchema = new Schema({
time: {
type: Date,
default: Date.now,
required: true,
},
body: {
type: String,
required: true,
},
});
const Comment = mongoose.model("Comment", CommentSchema);
It wasn't a handlebars problem at all!
It was a route problem.
I was doing this:
app.get("/", function(req, res) {
db.News.find()
.then(News => {
res.render("index", { News });
})
.catch(err => {
console.log(err);
});
});
When I should have been doing this
app.get("/", function(req, res) {
db.News.find()
.populate("comment")
.then(News => {
res.render("index", { News });
})
.catch(err => {
console.log(err);
});
});
I forgot to populate in the res.render. (I was populating the api route, but not this 1!)

Jquery - Sortable did not work when div.load is called

I have this issue that every time the div is loaded using div.load in the ajax success, the code for sortable will not work. Sortable will work again after the page is refreshed manually. What could be the possible solution for this?
$(document).on('click', '#add-song-tag', function() {
tag_id = $('#tags').val();
$.ajax({
url: base_url + '/songtags/add_song_tag',
type: 'POST',
data: {
song_info_id: song_info_id,
tag_id: tag_id
},
success: function() {
$('#category').load(window.location.href + ' #category');
$('#modal-categories').trigger('change');
},
error: function(xhr) {
console.log(xhr.responseText);
}
})
});
I have my code in the sortable.js like
$( function() {
$( "#sortable, #sortable1" ).sortable({
connectWith: ".draggable-group",
start: function(event, ui){
$(ui.item).width($('#sortable div').width());
}
// containment: "parent",
// tolerance: "pointer"
}).disableSelection();
} );
and in the html it looks something like:
<?php if($selected_tag_for_m['category_id'] == $tempo_id):?>
<div class="btn-group draggable-group">
<div>
<i class="fa fa-minus-circle fa-lg delete-a-tag" aria-hidden="true"></i>
</div>
<div type="button" class="btn btn-default btn-color"><i class="fa fa-circle-o custom-text-blue"></i> <?php echo $selected_tag_for_m['tag_name'];?> </div>
<div type="button" class="btn btn-default custom-bgcolor-blue dropdown-toggle" data-toggle="dropdown">
<span><i class="fa fa-pencil"></i></span>
<span class="sr-only">Toggle Dropdown</span>
</div>
<ul class="dropdown-menu" role="menu" id="tempo">
<?php foreach($tempos as $tempo):?>
<li data-id="<?php echo $selected_tag_for_m['info_tag_id']?>"><a href="" data-id="<?php echo $tempo['tag_id']?>" class="songtaglist" ><?php echo $tempo['tag_name'];?></a></li>
<?php endforeach;?>
</ul>
</div>
<?php endif;?>
I found solution to this by calling sortable.js script in body of the load function. The code looks like this:
$('#category').load(window.location.href + ' #category', function(){
$.getScript(base_url + '/assets/js/sortable.js');
});

Group and display data

Here is my data
data = [
{ category : "Cat1"},
{ category : "Cat2"},
{ category : "Cat3"},
{ category : "Cat4"},
{ category : "Cat5"},
{ category : "Cat6"}]
Let suppose i have it in a collection named myData
What i want is to group and display my data in group of 2.
Then i display it in a navbar (in a dropdown in fact) like this
<ul>
{{#each group}}
<li class="col-md-2">
<ul>
{{#each categories}}
<li>{{category}}</li>
{{/each}}
</ul>
{{/each}}
<ul>
What i am asking is how to group the data in my helpers or in mongodb so that i could get this result.
I'm not 100% clear what you mean by "group", but assuming you are using Boostrap navbar dropdowns, you could group them with separators:
{{#each categories}}
<li>{{category}}</li>
{{#if doSeparator #index}}
<li role="separator" class="divider"></li>
{{/if}
{{/each}}
and the doSeparator helper goes in your .js file:
doSeparator( index ) {
return (index % 2);
}
If on the other hand you want submenus for each group you will need to reorganize your array in two levels.
Another approach could be:
<ul>
{{#each groups}}
<li>
<ul>
{{#each this}}
<li>{{category}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
Then in your Template helper:
import { Template } from 'meteor/templating';
import chunk from 'lodash/chunk';
import { myData } from '/imports/api/mydata/collection';
import './main.html';
Template.someTemplate.helpers({
groups() {
return chunk(myData.find().fetch(), 2);
},
});
This uses lodash's chunk function to split the returned array into groupings of 2 items (so you'll want to meteor npm install --save lodash if you haven't already).
The above will give you output like:
<ul>
<li>
<ul>
<li>1</li>
<li>2</li>
</ul>
</li>
<li>
<ul>
<li>3</li>
<li>4</li>
</ul>
</li>
<li>
<ul>
<li>5</li>
<li>6</li>
</ul>
</li>
</ul>
You can break it up into groups of 2 inside a helper using underscore's map and compact
Template.hello.helpers({
groups() {
// var data = myData.find().fetch();
var data = [
{ category : "Cat1"},
{ category : "Cat2"},
{ category : "Cat3"},
{ category : "Cat4"},
{ category : "Cat5"},
{ category : "Cat6"}];
return _.chain(data).map(function(item, index){
return (index % 2) ? false : data.slice(index, index + 2);
}).compact().value();
},
});
Then, in your template you can use a nested #each in to loop through groups
<template name="hello">
<ul>
{{#each group in groups}}
<li class="col-md-2">
<ul>
{{#each category in group}}
<li>{{category.category}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</template>