Here's what I'm trying to accomplish:
<div v-for="columns in pageStructure"
//Print from here
<div v-for="htmlIWantPrinted in array">
...some content...
<button #click="printElementDiv()">Print</button>
</div>
//To here
</div>
I'm trying to print the specific content created in the for-loop. Button included.
Since there are multiple columns, I can't just put an id on it and I can't use ref either for the same reason, and using the element as a parameter for the method grabs the object instead of the html.
Simply add the value you passed to v-for loop and see the magic. This might not actually be what you want, but it should give a better understanding of what you're going to do. This is just enough.
var app = new Vue({
el: '#app',
data: {
pageStructure: ['Welcome', 'to', 'vue', 'world']
},
methods: {
printElementDiv(el) {
console.log(el)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="column in pageStructure">
<h1>Div: {{column}}</h1>
<button #click="printElementDiv(column)">Print</button>
</div>
</div>
I ended up using #RohìtJíndal' answer from the comments:
<div v-for="columns in pageStructure"
//Print from here
<div v-for="htmlIWantPrinted in array" :id="htmlIWantPrinted.id">
...some content...
<button #click="printElementDiv()">Print</button>
</div>
//To here
</div>
If you don't have a variable available, you can make one and increment it as part of the loop.
Related
I am trying to dynamically create a form (why? It's 200+ fields long and I am not permitted to modify it). The entire app is in the VueJs environment.
The problem I'm having is that each field requires different things (obviously). I'm trying to add attributes dynamically to each field, which would allow me to render the entire form dynamically, rather than hard coding a 200+ field form. So in my stupidity I'm taking more time trying to solve this problem than it would take to just hard code the form. Oh well...
Here's a specific (simplified) example of what I want to do...
data() {
return {
form: {
input1: {value: "", classIWantToDynamicallyAdd: "FieldSizeSmallAF"},
input2: {value: "", classIWantToDynamicallyAdd: "FieldSizeBigAF"},
//Repeat 200 times
}
}
}
Now ultimately I want to get the value of "classIWantToDynamicallyAdd" and :class="put it here"
The HTML looks like this:
<template>
<div>
<div v-for="(field, index)" in form" :key="index">
<div class="labelAndInput" :class="**I don't know what to do here**">
<label>index</label> // Successfully outputs: "input1", "input2", etc...
<input>
</div>
</div>
</div>
</template>
Hopefully that's somewhat clear. I expected form.index.classIWantToDynamicallyAdd to work, but it did not, i got the following error:
TypeError: "_vm.form.index is undefined".
Thanks in advance!
You could do :class="[field.classIWantToDynamicallyAdd]" :
<div v-for="(field, index)" in form" :key="index">
<div class="labelAndInput" :class="[field.classIWantToDynamicallyAdd]">
....
<input>
</div>
</div>
You could define those class names on data() and just bind it to :class
Example:
https://jsfiddle.net/pguti19/hL2vamnw/
More help:
https://michaelnthiessen.com/dynamically-add-class-name/
<div id="app">
<h1>
Forms:
</h1>
<div v-for="(field, index) in form" :key="index">
<span :class="field.class">
Using {{field.class}} class
</span>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
form: {
input1: {value: "", class: "red-theme"},
input2: {value: "", class: "blue-theme"},
input3: {value: "", class: "green-theme"}
},
theme1: 'blue-theme',
theme2: 'red-theme',
theme3: 'green-theme'
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
</script>
I have a Vue component which has a contenteditable div that lets users type in a message. When the user first attempts to create a message, I am using jQuery to wrap the text in a <p> tag. I cannot understand how this could be achieved using Vue.js alone...
Vue.js component
<template>
<div id="Message" contenteditable="true" #focus="formatMessage" #keydown="formatMessage" #keyup="formatMessage" #keypress="formatMessage">
</div>
</template>
<script>
import $ from 'jquery'
formatMessage: function(event) {
if ($("#Message > p").length === 0) { // if no <p> element when user interacts with div
$("#Message").contents().eq(0).wrap("<p />"); // then wrap a <p> tag around the first child content
}
}
Is it possible to do this using just Vue.js so I don't have to load the jQuery library for simple DOM manipulation (which may cause an issue with Vue's virtual DOM being out-of-sync with jQuery's changes)?
Before formatMessage():
<div id="Message" contenteditable="true">
I started typing here
</div>
After formatMessage():
<div id="Message" contenteditable="true">
<p>I started typing here</p>
</div>
Is it possible/better to try to do it using Vue's virtual DOM? Could I somehow use createElement to create a new p tag and then update its contents with what the user is typing? Maybe thats not the way the Virtual DOM works I'm not sure.
You can use v-if and duplicate the code a little if you want to achieve something similar
<template>
<div v-if="shouldWrap === false" contenteditable="true" #focus="formatMessage" #keydown="formatMessage" #keyup="formatMessage" #keypress="formatMessage">
</div>
<p v-else>
<div contenteditable="true" #focus="formatMessage" #keydown="formatMessage" #keyup="formatMessage" #keypress="formatMessage">
</div>
</p>
</template>
<script>
export default {
data () {
return {
shouldWrap: false
}
},
methods: {
formatMessage() {
this.shouldWrap = true
}
}
}
</script>
But probably trying to match the styling of a p should also work.
Do not use JQuery-like DOM manipulation in VUE, VUE is data driven framework, you need to store some data in component to trigger layout, for example
<template>
<div contenteditable="true" #focus="formatMessage" #keydown="formatMessage" #keyup="formatMessage" #keypress="formatMessage">
<!-- wrap 'p' tag, if 'shouldWrap'-->
<p v-if="shouldWrap">{{content}}</p>
<!-- without wrap-->
<template v-else>{{content}}</template>
</div>
</template>
<script>
export default {
data () {
return {
shouldWrap: false,
content:'' // text, you want to display inside div
}
},
methods: {
formatMessage() {
this.shouldWrap = true
}
}
}
</script>
I need to parse my website and save new version to variable.
For example i have code like this:
<body>
<div data-newname="test"></div>
<div data-newname="test2"></div>
</body>
so the new code should be:
<body>
{{Start:test}}{{End::test}}
{{Start:test2}}{{End::test2}}
</body>
I was want to use replacewiths method but this is not gonna help me :( maybe regular expression?
You are searching for replaceWith()
UPDATE: Reverse order replace the innermost first
$($('[data-newname]').get().reverse()).replaceWith(function() {
let data = $(this).data('newname')
return `{{Start:${data}}}${$(this).html()}{{End::${data}}}`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-newname="test1">
Test1
<div data-newname="test2">Test2</div>
</div>
<div data-newname="test3">Test3</div>
You could use jQuery replaceWith() to re-wrap the elements.
Because overwriting the HTML of the outer elements will drop the nested elements from the collection, I've wrapped it in a while to ensure that the nested ones get updated as well.
while ($("[data-newname]").length) {
$("[data-newname]").replaceWith(function() {
let data = $(this).data("newname");
return `{{Start:${data}}}${$(this).html()}{{End::${data}}}`
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-newname="test">
<div data-newname="nested">Nested</div>
</div>
<div data-newname="test2">Test2</div>
I wanted to access the current index of a loop inside another attribute of the same element that has the v-for directive.
The HTML content is :
<div id="app">
<div v-for="count in length" data-my-attribute="here" class="target">{{count}}</div>
</div>
And the JS code:
var app = new Vue({
el : '#app',
data: {
length: 9,
}
});
I know I can access the current loop index 'inside' the div with the class target.
The way it does with the {{ count }}
But is it possible to access the count inside the value of the attribute data-my-attribute ?
(I mean in the place of the word "here")
You could access that variable using the binding as follow :
<div id="app">
<div v-for="count in length" :data-my-attribute="count" class="target">{{count}} </div>
</div>
like the case when you want to define a dynamic ids
<div id="app">
<div v-for="count in length" :id="'divNum'+count" class="target">{{count}} </div>
</div>
I'm finishing up a memory game for school and I'd really like the cards to flip with a CSS animation, which on it's own is pretty straight forward. However I'm pretty new to JavaScript and JQuery which is leading to some trouble with achieving the proper container structure I need to make the cards flip when they are clicked.
Presently the game pieces generate within the board as follows:
const generate=(cards)=>{
cards.forEach(function(card, i) {
$(".gameBoard")
.append($("<div>").addClass("front")//
.append($("<div>").addClass("back").append($("
<img>").attr("src", cards[i]))));
});
};
OR:
<div class="gameBoard>
<div class="front"></div>
<div class="back"><img src="cards"></div>
</div>
But in order for the animation to function properly both the front and back divs need to exist in the same container like this:
<div class="gameBoard>
<div class="flip">
<div class="front></div>
<div class="back"><img src="cards></div>
</div>
</div>
How can I add the div I need (.flip) but have it contain the front and back divs, not just append on to the other divs being generated within the .gameboard container.
Thanks.
It's much simpler to create your DOM using template literals rather than jQuery methods. That way you just describe the HTML as you're accustomed to.
const generate=(cards)=>{
cards.forEach(function(card, i) {
$(".gameBoard").append(`
<div class=flip>
<div class=front></div>
<div class=back><img src="${cards[i]}"</div>
</div>
`);
});
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<div class=gameBoard></div>
You'll notice the ${cards[i]}, which lets you perform string interpolation by executing at runtime the code in the braces.
Here's a vanilla JS version.
const generate=(cards)=>{
var gb = document.querySelector(".gameBoard");
cards.forEach(card =>
gb.insertAdjacentHTML("beforeend", `
<div class=flip>
<div class=front></div>
<div class=back><img src="${card}"</div>
</div>
`)
);
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<div class=gameBoard></div>
It also uses card instead of cards[i], and an arrow function for the callback.
And this one performs a single append.
const generate=(cards)=>{
document.querySelector(".gameBoard")
.insertAdjacentHTML("beforeend", cards.map(card =>
` <div class=flip>
<div class=front></div>
<div class=back><img src="${card}"</div>
</div>`).join(""));
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<div class=gameBoard></div>