I've been having some trouble recently with the order of Vue components in Bootstrap. I'm trying to generate some Bootstrap collapsible content in Vue here's the code so far:
HTML
<div class="col-sm main-content" id="main-content">
<p>
<main-section-button v-for="item in sections"
v-bind:section="item"
v-bind:data-target="'#section-' + item.text"
v-bind:aria-controls="'section-' + item.text">
</main-section-button>
</p>
<main-section v-for="item in sections"
v-bind:id="'section-' + item.text">
</main-section>
</div>
VueJS
Vue.component("main-section-button", {
props: ["section"],
template: String.raw`<button class="btn btn-block btn-dark" type="button" data-toggle="collapse" aria-expanded="false">{{ section.text }}</button>`
});
Vue.component("main-section", {
props: ["section"],
template: String.raw`<div class="collapse"><div class="card card-body"><p>Hello, World!</p></div></div></div>`
});
let app = new Vue({
el: '#main-content',
data: {
sections: [
{ id: 0, text: "example1"},
{ id: 0, text: "example2"}
]
}
});
I have tried to make just one component for main-section and main-section-button, but that did not work because of the requirement to pass an id to the card (collapsible content), and a data-target to the button that collapses and expands the content.
Current Result
Required Result
Is it possible to create a component so that the section content is always below the section button.
Thank you in advance.
I guess you have two options to achieve this:
Create a new component that takes the items and displays both components as you wish.
Do not iterate over the components, instead use a <div> around both components or a non-rendered <template> like this:
<div class="col-sm main-content" id="main-content">
<template v-for="item in sections">
<p>
<main-section-button
v-bind:section="item"
v-bind:data-target="'#section-' + item.text"
v-bind:aria-controls="'section-' + item.text">
</main-section-button>
</p>
<main-section
v-bind:id="'section-' + item.text">
</main-section>
</template>
</div>
Related
I'm trying to use Clusterize.js component to display large amount of data in my vue app.
It's showing the dataset perfectly, however, while the clusterize element contains a button or something else which contains a click event, It's not working. I understand this component requires special click event fashion(described in docs FAQ), but can't find a way to use with dynamic data in vue app.
Here is the code sample
<div
id="scrollArea"
class="clusterize-scroll"
style="height: 300px; overflow: auto"
>
<div id="contentArea" class="clusterize-content">
<div v-for="item in dataSet" :key="item">
<button #click="onClick(item)" data-action="onClick">
button {{ item }}
</button>
</div>
</div>
</div>
<script>
mounted() {
new Clusterize({
scrollId: "scrollArea",
contentId: "contentArea",
});
},
</script>
Sandbox link: https://codesandbox.io/s/eloquent-tereshkova-wisf1?file=/src/App.vue
I'm trying to apply a class to a <p> tag using a VueJS condition but it doesn't work at all, despite it working on JSfiddle. It's been 3 days now that I'm stuck, please help me.
VueJS (in main.js)
new Vue({
el: "#test",
data: {
selected: "2"
}
});
HTML (App.vue)
<div id="app">
<div id="test">
{{ selected }}
<p :class="{ selection: 1 === selected }">
heyyy 1
</p>
<p :class="{ selection: 2 == selected }">
heyyy 2
</p>
<button #click="selected = 2">Switch</button>
</div>
</div>
I'll assume you're using Vue-CLI since you have a .vue file, but your main.js is using syntax from Vue CDN. Maybe you copied the code from the fiddle instead.
I'll assume too that you have a CSS class named .selection for this to work.
Either way, App.vue has its own component instance which is not the main.js mounting instance (it's one level deeper). Your code creates selected in the mounting root instead of App.vue.
To fix, create it in App.vue <script> section instead:
<template>
<div id="app">
<div id="test">
{{ selected }}
<p :class="{ selection: 1 === selected }">
heyyy 1
</p>
<p :class="{ selection: 2 == selected }">
heyyy 2
</p>
<button #click="selected = 2">Switch</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
selected: "2"
}
}
}
</script>
<style>
.selection {
/* CSS */
}
</style>
to apply class binding in VueJs you can use it as :
:class="{ 'selection(your class name here)': selected === 1}"
How can i toggle by btn specific div from list of items , which one i got from json and display using v-for ?
I am understand that i can`t just set v-if to div and add to vue data , but in this case it fill open and close all divs
<div class="about" v-if="list">
<div class="dat" v-for="data in item.items_situation" :key="data.a">
{{data.doc_valid_through}}
{{data.document_type}}
{{data.item_nr_full}}
{{data.item_text}}
</div>
<div class="situation-btn">
<input class="situatuin-btn" type="submit" value="Подбробнее" v-on:click="hide">
</div>
</div>
When i click the button i want to toggle one div not all
There are many ways to achieve this, I'll provide one for you.
Map your JSON array in data by adding visible property for every item.
Add button inside loop (so its attached for every item) and make visible = false by clicking it.
Display filtered results (visible === true only)
new Vue({
el: "#app",
data: {
items: [
{ text: "Learn JavaScript", id: 1 },
{ text: "Learn Vue", id: 2 },
{ text: "Play around in JSFiddle", id: 3 },
{ text: "Build something awesome", id: 4 }
].map(item => ({...item, visible: true})),
hiddenItems: []
},
computed: {
visibleItems() {
return this.items.filter(item => item.visible)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="dat" v-for="item in visibleItems" :key="item.id">
{{item.text}}
<button v-on:click.prevent="item.visible = false">X</button>
</div>
</div>
You can use v-bind:class to dynamically add class to elements. Code should be like this:
<div class="about" v-if="list">
<div v-for="data in items" v-bind:class="{hide: data.isHidden}" :key="data.key">
Something
</div>
<div class="situation-btn">
<input class="situatuin-btn" type="submit" value="Подбробнее" v-on:click="hide">
</div>
</div>
Then in hide
hide(){
this.items[i].isHidden = true
}
The data.isHidden property decides whether an element in list should have hide class.
See also Class and Style Bindings.
-------Edited-------
I have posted an example here
I have a Vue component in which I'm trying to do a v-for of an multi array json object passed by props, this object is dynamic and is populated by a parent method.
Here:
My problem is that in the component i'm seeing only the first object of the data:
but I have no error in console, so I don't understand what the problem is... must I do a watch in data?
Here is my code:
<lista-percorso :selezionati="il_tuo_percorso"></lista-percorso>
COMPONENT
Vue.component('lista-percorso', {
template:`
<div class="container" style="margin-top:30px;">
<template v-if="listaSelezionati.length>0">
<div class="row" v-for="(selezionato,index) in listaSelezionati">
<div>{{selezionato[index]}}</div>
</div>
</template>
<template v-else>
<h5>NON HAI SELEZIONATO NESSUN SERVIZIO</h5>
</template>
</div>
`,
props: ['selezionati'],
data: function(){
return{
listaSelezionati:this.selezionati
}
},
methods:{
}
});
Your data listaSelezionati is an Array of Arrays of Objects: [[{one:one}],[{two,two}]]
when you go this:
<div class="row" v-for="(selezionato,index) in listaSelezionati">
<div>{{selezionato[index]}}</div>
</div>
You are telling Vue to render the first item [{one:one}] and then the index in that item {one:one}. However, since they all appears to be arrays with length 1, you could do this:
<div class="row" v-for="(selezionato,index) in listaSelezionati">
<div>{{selezionato[0]}}</div>
</div>
I have 2 models Tour.php
public function Itinerary()
{
return $this->hasMany('App\Itinerary', 'tour_id');
}
and Itinerary.php
public function tour()
{
return $this->belongsTo('App\Tour', 'tour_id');
}
tours table:
id|title|content
itineraries table:
id|tour_id|day|itinerary
In tour-edit.blade.php view I have used vue js to create or add and remove input field for day and plan dynamically.
Code in tour-create.blade.php
<div class="row input-margin" id="repeat">
<div class="col-md-12">
<div class="row" v-for="row in rows">
<div class="row">
<div class="col-md-2">
<label >Day:</label>
<input type="text" name="day[]"
class="form-control">
</div>
<div class="col-md-8">
{{ Form::label('itinerary', " Tour itinerary:", ['class' => 'form-label-margin'])}}
{{ Form::textarea('itinerary[]',null, ['class' => 'form-control','id' => 'itinerary']) }}
</div>
<div class="col-md-2">
<button class="btn btn-danger" #click.prevent="deleteOption(row)">
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="row">
<button class="btn btn-primary add" #click.prevent="addNewOption" >
<i class="fa fa-plus"></i> Add Field</button>
</div>
</div>
</div>
I want to populate these fields with their respective data. But all data i.e itinerary belonging to a tour are being displayed in itinerary textbox in JSON format.
My vue js sript is:
<script>
var App = new Vue({
el: '#repeat',
data: {
day:1 ,
rows:[
#foreach ($tour->itinerary as $element)
{day: '{{$element->day}}', plan: '{{$element->plan}}'},
#endforeach
]
},
methods: {
addNewOption: function() {
var self = this;
self.rows.push({"day": "","itinerary":""});
},
deleteOption: function(row) {
var self = this;
self.rows.splice(row,1);
},
}
});
</script>
I would avoid mixing blade into JavaScript, instead the best option is to make an ajax call to an api route which returns your data in json, which can then be processed by Vue:
methods:{
getItinerary(){
axios.get('api/itinerary').then(response => {
this.itinerary = response.data;
})
}
}
However, with this approach you will likely need to use vue-router rather than laravel web routes, which puts us into SPA territory.
If that's not an option (i.e. you still want to use blade templates), you should take a look at this answer I gave the other day which shows you how to init data from your blade templates.
What you seem to be doing is using laravel's form model binding to populate your forms, not Vue, so your model data is not bound to the view. So, you will need to decide which one you want to use. If it's vue you just want to use a normal form and bind the underlying data to it using v-model:
Now any updates in the view will automatically be updated by Vue. I've put together a JSFiddle that assumes you will want to continue using Laravel web routes and blade templates to show you one approach to this problem: https://jsfiddle.net/w6qhLtnh/