I want to add some transition effect on newly created element on VueJS. Like in the following code if I create a new list element using the input field, I want it to appear using 'fade' or 'slide-in' effect. I couldn't figure out it from the official documentation. how can I do that?
var vm = new Vue({
el: "#vue-instance",
data: {
newelement: '',
list: []
},
methods: {
addelement: function() {
this.list.push(this.newelement);
this.newelement = '';
}
}
});
<div id="vue-instance">
<input type="text" v-model="newelement" #keyup.enter="addelement">
<ul v-for="element in list">
<li>{{ element }}</li>
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
According to official docs u need to wrap you loop with a transition element. And specify a name and a (optional) tag attributes.
Like so:
<transition-group name="list" tag="p">
<span v-for="item in list" class="list-item"></span>
</transition-group>
And add some css classes with name of transition:
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to /* .list-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateY(30px);
}
More info at the https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions
Related
I have a pill shape that say's either Production or Development. I want the background color to be different depending on if it is production or development. I've done this before with option api, but I'm having trouble in composition api.
Computed Method:
const reportStatus = computed(function() {
if (dashboard.status === 'Production') {return 'status--production'}
return 'status--qa'
});
Styling:
.status--production {
background-color: blue;
color: white;
}
.status--qa {
background-color: green;
color: white;
}
Template code:
<div>
<span class="float-left text-center w-24 inline-block flex-shrink-0 rounded-full px-2 py-0.5 text-xs font-medium text-white inset-x-0 top-0" :class="reportStatus">
{{ dashboards.status }}</span>
</div>
Script Dashboard code:
const dashboard = [
{
report: "Ad Hoc",
description: "",
status: "Production"
},
Use computed properties for things like changing reactive variable output. For example, some different date format.
The best way to your issue is:
<template>
<span class="fload ..." :class="[ dashboard.status === 'Production' ? 'status-production' : 'status-qa']">{{ dashboard.status }}</span>
</template>
Make sure dashboard is a reactive object/value like ref() or reactive() second one fit best for objects. Objects are tricky to use every time you have to assign a full new object instead of just change a value in it.
Computed property:
<template>
<div>
<span class="float ..." :class="addClass">{{ dashboard.status }}</span>
<button #click="dashboard = { ...dashboard, status: 'lol' }">Change</button>
<button #click="dashboard = { ...dashboard, status: 'Production' }">Production</button>
</div>
</template>
<script setup>
const dashboard = ref({ status: 'Production' })
const addClass = computed(() => dashboard.value.status === 'Production' ? 'status-production' : 'status-qa')
</script>
If you use ref() and change dashboard like "dashboard.value.status = 'some value'" reactivity won't work same as "dashboard.status = 'some value'" in template. You will always need to assign a new object to trigger reactivity.
reactive() don't have that problem because all items inside of it are reactive.
When you have two class attributes, the second one gets ignored. Combine your two class attributes into one;
<div>
<span :class="`float-left text-center w-24 inline-block flex-shrink-0 rounded-full px-2 py-0.5 text-xs font-medium text-white inset-x-0 top-0 ${reportStatus}`">
{{ dashboards.status }}
</span>
</div>
Your code should work fine, Just for a demo purpose I am using just an object for dashbaords variable but you can change it to an array as per your requirement.
Here is the live demo (Please have a look and try to find the root cause of the issue you are facing by referring this) :
const { ref, computed, onMounted } = Vue;
let options = {
setup: function () {
let dashboards = ref({});
const reportStatus = computed(function() {
return (dashboards.value.status === 'Production') ? 'status--production' : 'status--qa'
});
onMounted(function() {
dashboards.value = {
report: "Ad Hoc",
description: "",
status: "Production"
}
});
return {
dashboards,
reportStatus
};
}
};
let appVue = Vue
.createApp(options)
.mount('#app');
.status--production {
background-color: blue;
color: white;
border: 1px solid black;
}
.status--qa {
background-color: green;
color: white;
border: 1px solid black;
}
<script src="https://unpkg.com/vue#3.0.0-beta.14/dist/vue.global.js"></script>
<div id="app">
<span :class="reportStatus">
{{ dashboards.status }}
</span>
</div>
I have this parent component which makes a call to this apollo query:
<template>
<div class="row" style="flex-wrap: nowrap; width:102%">
<BusinessContextTeamPanel :business_contexts="business_contexts"></BusinessContextTeamPanel></BusinessContextTeamDetailPanel>
</div>
</template>
<script>
apollo: {
business_contexts: {
query: gql`query ($businessContextId: uuid!) {
business_contexts(where: {id: {_eq: $businessContextId }}) {
id
businessContextTeams {
id
team {
name
id
}
role
}
}
}`,
variables: function() {
return {
businessContextId: this.currentContextId
}
}
}
},
</script>
My child component( BusinessContextTeamPanel) takes the input props and uses it like this:
<template>
<div v-if="business_contexts.length > 0">
<div v-for="teams in business_contexts" :key="teams.id">
<div v-for="team in teams.businessContextTeams" :key="team.id" style="padding-top: 20px;min-width: 400px">
<div style="height: 25px; color: #83d6c0;">
<div style="width: 7%; font-size: 10px; float: left; padding-top: 8px; cursor: pointer;" class="fa fa-minus-circle" ></div>
<div style="width: 50%; float: left; padding-right: 10px;">
<div class="gov-team-text" #click="editTeam(team)">{{ team.team.name }}</div>
</div>
<div style="width: auto; float: right;">
<b-badge variant="light">{{ team.role }}</b-badge>
</div>
<div style="width: 45%; margin-left: 4%; float: right;">
</div>
</div>
<div style="clear: both; margin-bottom: 15px;"></div>
</div>
</div>
</div>
</template>
The v-if here throws
Cannot read property 'length' of undefined
while the v-for works.
And when I'm trying to use the props in a child method(loadData()) which is called on child component mount hook, I get also undefined
<script>
import Treeselect from '#riophae/vue-treeselect';
import _ from "lodash";
export default {
name: "BusinessContextTeamPanel",
components: { Treeselect },
props: ['business_contexts'],
data() {
return {
itemModel: {},
allRoles: [],
formTitle: "",
filteredTeams: [],
showNoTeamMsg: false
}
},
mounted() {
this.loadData();
},
methods: {
loadData() {
let roles = [];
_.forEach(["Admin", "Contributor", "Owner", "Master", "Viewer", "User"], function (role) {
roles.push({id: role, label: role});
});
console.log(this.business_contexts); // here I get undefined
this.allRoles = roles;
this.showNoTeamMsg = this.business_contexts.length === 0;
}
}
}
</script>
What am I missing?
Either your apollo query isn't working, or it isn't ready in time for the child component's attempted use of it.
For debugging, in the parent component's template add {{ business_contexts }}, just so you can eyeball it. This will confirm whether the query is working.
If the query is working, it could be that your child component is trying to access the data before it's ready. Change your v-if from
<div v-if="business_contexts.length > 0">
to
<div v-if="business_contexts && business_contexts.length > 0">
EDIT: better yet, change the parent component to not try to load the child at all, until the parent has the data:
<BusinessContextTeamPanel v-if="business_contexts" :business_contexts="business_contexts"></BusinessContextTeamPanel></BusinessContextTeamDetailPanel>
I am facing an issue.
I am currently working on a framework agnostic element library with the new browser API customElements.define() and created a custom input and would like him to work with v-model binding.
But, it does not work. I am able to bind a value with :value="prop" but v-model simply does not work.
class LmTag extends HTMLElement {
constructor() {
super();
this.template = document.querySelector("#lmTag");
this.shadow = this.createShadowRoot();
this.clone = document.importNode(this.template.content, true);
this.shadow.appendChild(this.clone);
this.input = this.shadow.querySelector('input');
}
connectedCallback() {
this.value = this.getAttribute("value");
}
get value() {
return this.input.value;
}
set value(value) {
this.input.value = value;
}
attributeChangedCallback(attr, oldValue, newValue) {
console.log(attr, oldValue, newValue);
}
}
customElements.define("lm-tag", LmTag);
Vue.config.productionTip = false
Vue.config.devtools = false;
new Vue({
el: '#app',
data: {
lights: true,
input: "model"
},
methods: {
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template id="lmTag">
<style>
.lm-tag {
display: inline-block;
padding: 4px;
border-radius: 4px;
background-color: #e74c3c;
color: #ffffff;
font-size: 12px;
}
.lm-tag input {
border: none;
}
</style>
<span class="lm-tag">
<input type="text"/>
<content></content>
</span>
</template>
<div id="app" class="text-center body">
<p>
Binding :value
<lm-tag :value="input">{{input}}</lm-tag>
</p>
<p>
Binding v-model
<lm-tag v-model="input">{{input}}</lm-tag>
</p>
<p>
Raw input with :value
<input type="text" :value="input">
</p>
<p>
Raw input with v-model
<input type="text" v-model="input">
</p>
</div>
Could you help a fellow dev having trouble with his code?
Thank you!
I'm just wondering if there is a known issue with scoped styles and the v-html directive? I seem to find that either applying the same styling to the parent or removing the scoped key work from styles everything seems to work ok...?
Example component with the issue (this works with the wordpress API if anyone with a Wordpress site want's to test with their setup):
<template>
<div v-bind:id="posts">
<div v-for="(post, i) in posts" v-bind:key="i">
<div v-html="post.content.rendered"></div>
</div>
</div>
</template>
<script>
export default {
name: "Posts",
props: {
msg: String
},
data() {
return {
posts: null
};
},
created() {
this.$http.get(this.$store.state.endpoint + "posts").then(response => {
this.posts = response.body;
});
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
p {
color: #42b983; // v-html in this example spits out numerous <p></p> tags...
}
</style>
*for replicating example, you can replace this.$store.state.endpoint with your Wordpress API endpoint, e.g., http://localhost:8000/wp-json/wp/v2 or similar.
From vue-loader docs:
Dynamically Generated Content
DOM content created with v-html are not affected by scoped styles, but you can still style them using deep selectors.
So to style the dynamic content, you should use deep selectors as seen in the following example:
<div class="posts">
<div v-for="(post, i) in posts" v-bind:key="i">
<div v-html="post.content.rendered"></div>
</div>
</div>
...
<style scoped>
.posts >>> p {
color: blue;
}
</style>
demo
Can I access Vue components data properties and methods from external javascript? I am trying to create a hybrid application where a portion of the screen is a Vue component, and I want to call a method inside that component on click of a button which is handled by pure js. Is there a way to achieve this?
Thanks!
Yes. You need to assign your Vue object to a variable (i.e. vue) and then you can access vue.methodName() and vue.propertyName:
// add you new Vue object to a variable
const vue = new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
});
// Add event listener to outside button
const button = document.getElementById('outsideButton');
button.addEventListener('click', function() {
vue.toggle(vue.todos[1]);
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
<div class="outside">
<button id="outsideButton">
Click outside button
</button>
</div>
Yes, you can add an event listener to the Vue component that listens for the button click's event. See example here on codepen.
JS
new Vue({
el: '#app',
methods: {
clickMethod(event){
if (event.target.id === 'outsideButton') {
alert('button clicked')
}
}
},
created(){
let localThis = this
document.addEventListener('click', this.clickMethod)
}
})
HTML
<div>
<button id="outsideButton">Button</button>
<div id="app">
</div>
</div>