Vue element UI tree, get node in method - vue.js

How to get node data from custom methods (applySelected in my sample)?
I try to use getNode (from element ui guide), but not really understand, how its work. I would want to get 'markup' from current node.
I added custom button with applySelected method, with node.id parameter.
Element ui tree - http://element.eleme.io/#/en-US/component/tree
Method getNode - get node by data or key.
Possible, there must be more easy way to do that.
var Main = {
data() {
return {
data: [
{
id: 1,
label: 'Level one 1',
markup: '111',
children: [{
id: 4,
label: 'Level two 1-1',
markup: '112',
children: [{
id: 9,
label: 'Level three 1-1-1',
markup: '131'
}, {
id: 10,
label: 'Level three 1-1-2',
markup: '141'
}]
}]
}, {
id: 2,
label: 'Level one 2',
markup: '161',
children: [{
id: 5,
label: 'Level two 2-1',
markup: '117'
}, {
id: 6,
label: 'Level two 2-2',
markup: '118'
}]
}, {
id: 3,
label: 'Level one 3',
markup: '119',
children: [{
id: 7,
label: 'Level two 3-1',
markup: '211'
}, {
id: 8,
label: 'Level two 3-2',
markup: '177'
}]
}]
}
},
methods: {
handleNodeClick(data) {
console.log(data.markup);
},
applySelected(nodeid) {
console.log(nodeid);
//console.log(this.$refs.markupTree.getNode(nodeid));
console.log(this.$refs.markupTree.getNode(nodeid).markup);
console.log(this.$refs.markupTree.getCheckedNodes());
},
getCheckedNodes() {
console.log(this.$refs.markupTree.getCheckedNodes());
}
}
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
#import url("//unpkg.com/element-ui#2.4.4/lib/theme-chalk/index.css");
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-tree-node__content {
height: 55px !important;
}
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.4.11/lib/index.js"></script>
<div id="app">
<el-button #click="getCheckedNodes">get by node</el-button>
<div class="custom-tree-container">
<div class="block">
<el-tree
:data="data"
show-checkbox=""
node-key="id"
:expand-on-click-node="false"
ref="markupTree"
#node-click="handleNodeClick"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span class="catname">
<el-input
v-model="node.label"
size="small"
:ref="'node'+ node.id"
></el-input>
</span>
<span class="catmarkup">
<el-input
placeholder="Please input"
v-model="data.markup"
size="small"
v-bind:name="data.input"
>
<template slot="append">%</template>
</el-input>
</span>
<el-button
icon="el-icon-check"
type="primary"
size="small"
v-on:click="applySelected(node.id)"
></el-button>
</span>
</el-tree>
</div>
</div>
</div>

The element.io "click" event handler already returns the clicked object, no need to specify a separate function for when the Tree emits an event. So you can use one function straight away:
Edit: I just see you're using a custom template with a button in it, in that case:
var Main = {
data() {
return {
data: [
{
id: 1,
label: 'Level one 1',
markup: '111',
children: [{
id: 4,
label: 'Level two 1-1',
markup: '112',
children: [{
id: 9,
label: 'Level three 1-1-1',
markup: '131'
}, {
id: 10,
label: 'Level three 1-1-2',
markup: '141'
}]
}]
}, {
id: 2,
label: 'Level one 2',
markup: '161',
children: [{
id: 5,
label: 'Level two 2-1',
markup: '117'
}, {
id: 6,
label: 'Level two 2-2',
markup: '118'
}]
}, {
id: 3,
label: 'Level one 3',
markup: '119',
children: [{
id: 7,
label: 'Level two 3-1',
markup: '211'
}, {
id: 8,
label: 'Level two 3-2',
markup: '177'
}]
}]
}
},
methods: {
nodeClickButton(nodeDataObj) {
console.log(nodeDataObj.markup);
},
getCheckedNodes() {
console.log(this.$refs.markupTree.getCheckedNodes());
}
}
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
#import url("//unpkg.com/element-ui#2.4.4/lib/theme-chalk/index.css");
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-tree-node__content {
height: 55px !important;
}
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.4.11/lib/index.js"></script>
<div id="app">
<el-button #click="getCheckedNodes">get by node</el-button>
<div class="custom-tree-container">
<div class="block">
<el-tree
:data="data"
show-checkbox=""
node-key="id"
:expand-on-click-node="false"
ref="markupTree"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span class="catname">
<el-input
v-model="node.label"
size="small"
:ref="'node'+ node.id"
></el-input>
</span>
<span class="catmarkup">
<el-input
placeholder="Please input"
v-model="data.markup"
size="small"
v-bind:name="data.input"
>
<template slot="append">%</template>
</el-input>
</span>
<el-button
icon="el-icon-check"
type="primary"
size="small"
#click="nodeClickButton(data)"
></el-button>
</span>
</el-tree>
</div>
</div>
</div>

Related

Including a standalone component in vue ant design steps

I want to use any design steps component and i wonder how i can include a standalone component from https://www.antdv.com/components/steps/
<template>
<div>
<a-steps :current="current">
<a-step v-for="item in steps" :key="item.title" :title="item.title" />
</a-steps>
<div class="steps-content">
{{ steps[current].content }}
</div>
<div class="steps-action">
<a-button v-if="current < steps.length - 1" type="primary" #click="next">
Next
</a-button>
<a-button
v-if="current == steps.length - 1"
type="primary"
#click="$message.success('Processing complete!')"
>
Done
</a-button>
<a-button v-if="current > 0" style="margin-left: 8px" #click="prev">
Previous
</a-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
current: 0,
steps: [
{
title: 'First',
content: 'First-content',
},
{
title: 'Second',
content: 'Second-content',
},
{
title: 'Last',
content: 'Last-content',
},
],
};
},
methods: {
next() {
this.current++;
},
prev() {
this.current--;
},
},
};
</script>
<style scoped>
.steps-content {
margin-top: 16px;
border: 1px dashed #e9e9e9;
border-radius: 6px;
background-color: #fafafa;
min-height: 200px;
text-align: center;
padding-top: 80px;
}
.steps-action {
margin-top: 24px;
}
</style>
this is the code that assigns content in steps
steps: [
{
title: 'First',
content: 'First-content',
},
{
title: 'Second',
content: 'Second-content',
},
{
title: 'Last',
content: 'Last-content',
},
],
How can i include a standalone component.vue here
content: 'First-content',
Change content from a string to a component definition:
import FirstContent from '#/components/FirstContent.vue'
import SecondContent from '#/components/SecondContent.vue'
import LastContent from '#/components/LastContent.vue'
export default {
data() {
return {
steps: [
{
title: 'First',
content: FirstContent,
},
{
title: 'Second',
content: SecondContent,
},
{
title: 'Last',
content: LastContent,
},
],
}
},
}
In your template, replace the string interpolation with <component>:
<component :is="steps[current].content" />
demo

Vue Element UI - html inside <el-select>

I would like to implement a select element with different colored-labels for each entry:
My code looks like this:
var Main = {
data() {
return {
selectedState: null,
processStates: [
{
value: 0,
label: 'New',
color: 'ffffff'
},
{
value: 1,
label: 'Ready',
color: 'ff9933'
},
{
value: 2,
label: 'Running',
color: '008000'
},
{
value: 3,
label: 'Rejected',
color: 'cc0000'
},
{
value: 4,
label: 'Terminated',
color: '2E9AFE'
}
]
}
},
methods: {}
}
var Ctor = Vue.extend(Main);
new Ctor().$mount('#app');
#import url("//unpkg.com/element-ui#2.4.4/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.4.11/lib/index.js"></script>
<div id="app">
<el-select v-model="selectedState" style="width:200px">
<el-option
v-for="state in processStates"
:key="state.value"
:label="state.label"
:value="state.value"
>
<span>
<el-tag :style="'background-color:#' + state.color"> </el-tag> {{ state.label }}
</span>
</el-option>
</el-select>
</div>
AS you can see, I managed to inject html into the option tag and have the desired result.
However, I would like to have the same html when one option is selected.
Desired result:
Any idea how can I achieve it?
You have to use the prefix slot for this. As done below, also I changed the selectedState to an object, but you can also still use the string value, but then you have to do a lookup to get the color
var Main = {
data() {
return {
selectedState: { color: 'ffffff'},
processStates: [
{
value: 0,
label: 'New',
color: 'ffffff'
},
{
value: 1,
label: 'Ready',
color: 'ff9933'
},
{
value: 2,
label: 'Running',
color: '008000'
},
{
value: 3,
label: 'Rejected',
color: 'cc0000'
},
{
value: 4,
label: 'Terminated',
color: '2E9AFE'
}
]
}
},
methods: {}
}
var Ctor = Vue.extend(Main);
new Ctor().$mount('#app');
#import url("//unpkg.com/element-ui#2.4.4/lib/theme-chalk/index.css");
.el-input--prefix .el-input__inner {
padding-left: 40px;
}
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.4.11/lib/index.js"></script>
<div id="app">
<el-select v-model="selectedState" value-key="value" style="width:200px">
<template slot="prefix">
<el-tag class="prefix" :style="`background-color: #${selectedState.color}`"/>
</template>
<el-option
v-for="state in processStates"
:key="state.value"
:label="state.label"
:value="state"
>
<span>
<el-tag :style="'background-color:#' + state.color"> </el-tag> {{ state.label }}
</span>
</el-option>
</el-select>
</div>

vue dynamic vlaue based on multiple data

based on most of vue docs that have mentioned to single parameter,
I used the v-on:mouseover and leave to control style dynamically based on each item color because i need to change each item color by hover based on its color and although I used !important with styles, it doesn't change
<li v-for="item in items" v-bind:key="item.id">
<a class="my-link"
v-on:mouseleave="mouseLeave(item)"
v-on:mouseover="mouseOver(item)">
{{ item.title }}
</a>
</li>
data() {
return {
items: [
{
id: 1,
title: "one",
color: "#ccc"
},
{
id: 2,
title: "two",
color: "#000"
},
{
id: 3,
title: "three",
color: "#c7c7c7"
}
]
}
},
methods: {
mouseOver: function(item){
this.$el.children[0].style.color = 'red !important';
},
mouseLeave: function(item){
this.$el.children[0].style.color = `${item.color} !important`;
}
}
Another approach without using mouseleave and mouseover, only CSS:
Apply the main color with :style for each list item from its data definition. Also add class on the parent element class="list" with the color for hover effect. And finally class="list-item" which inherits color from the parent on hover only. Thus color red is inherit on hover only:
<li v-for="item in items" v-bind:key="item.id" class="list" :style="{ color: item.color }">
<a class="list-item">
{{ item.title }}
</a>
</li>
<style scopped>
.list-item {
color: red;
}
.list-item:hover {
color: inherit !important;
}
</style>
Live example:
new Vue({
el: '#app',
data: {
items: [
{
id: 1,
title: "one",
color: "red",
},
{
id: 2,
title: "two",
color: "green",
},
{
id: 3,
title: "three",
color: "blue",
}
]},
template: `
<div>
<li v-for="item in items" v-bind:key="item.id" class="list" :style="{ color: item.color }">
<a class="my-link list-item">
{{ item.title }}
</a>
</li>
</div>`
})
.list-item {
color: #ccc;
}
.list-item:hover {
color: inherit !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

Unable to display data from vue instance in a component that is inside another componnt

I have a Vue instance that has data that I am trying to access from a component that is inside another component to iterate through all values with v-for from the items inside data, in this case either banners or posts.
I have reviewed the composing components section on the vue.js website, using their posts data as an example but am unable to utilize the posts data inside my blog-post component.
I have tried adding props to the blog-post component, to the nav-home component and have tried modifying the data in my vue app instance to return all values of data and tried to add the following to the mounted property, this.$emit('posts', this.posts) without success.
Console.log(this.posts) of course returns the data, however it isn't accessible in the nested components either product-banner or blog-post when included in the nav-home component.
Vue.component('nav-home', {
props: ['posts','banners'],
template: `<div>
<h1> Home component </h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<!-- banner -->
<product-banner
v-for="banner in banners"
v-bind:key="banner.id"
v-bind:banner="banner">
</product-banner>
<!-- post -->
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post">
</blog-post>
</div>`
})
Vue.component('blog-post', {
props: ['posts'],
template: `<h3>{{posts.title}}</h3>`
})
Vue.component('product-banner', {
props: ['banner'],
template: `<div id="product-banner">
<div class="product-container">
<h2>{{banner.title}}</h2>
<p>{{banner.content}}</p>
</div>
</div>`
})
Vue.directive('focus', {
inserted: function(el) {
el.focus();
}
})
new Vue({
el: '#app',
data: {
currentNav: 'Home',
navs: ['Home'],
posts: [
{ id: 1, title: 'Title one' },
{ id: 2, title: 'Title two' },
{ id: 3, title: 'Title three' }
],
banners: [
{id: 1, title: 'Title 1', content: 'Content 1'},
{id: 2, title: 'Title 2', content: 'Content 2'},
{id: 3, title: 'Title 3', content: 'Content 3'}
]
},
computed: {
contentComponent: function() {
return 'nav-' + this.currentNav.toLowerCase()
}
},
directive: {
focus: {
inserted: function(el) {
el.focus()
}
}
}
})
#charset "utf-8";
body {
font-family: tahoma;
color:#282828;
margin: 0px;
overflow: hidden;
}
.nav {
display: flex;
width: 100%;
height: 50%;
justify-content: center;
}
.nav > nav {
padding: 20px;
align-self: center;
border: 1px solid #000000;
margin: 0 10px 0 0;
transition: background 0.2s ease-in-out;
}
.nav > nav:hover {
cursor: pointer;
background: #cecece;
}
nav.active-nav.active, .active {
background: #cecece;
}
.contentDetails {
margin: 40px;
height: 100%;
display: block;
position: absolute;
}
/* SLIDE IN OUT */
.slide-enter {
transform: translate(100%, 0%);
opacity: 1;
}
.slide-leave-to {
transform: translate(-100%, -0%);
opacity: 0;
}
.slide-leave-active,
.slide-enter-active {
position: absolute;
transition: transform 1s ease-in-out,
opacity 0.2s ease-in-out;
}
/* FLIP IT */
.flipit-enter,
.flipit-leave-to {
opacity: 0;
transform: rotateY(50deg);
}
.flipit-enter-to,
.flipit-leave {
opacity: 1;
transform: rotateY(0deg);
}
.flipit-enter-active,
.flipit-leave-active {
transition: opacity, transform 200ms ease-out;
}
/* SLIDE UP */
.slideup-enter,
.slideup-leave-to {
opacity: 0;
position: absolute;
right: -9999px
}
.slideup-enter-to,
.slideup-leave {
opacity: 1;
right: 0;
}
.slideup-enter-active,
.slideup-leave-active {
transition: right, transform 200ms ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<!-- NAVIGATION -->
<div class="nav">
<nav v-for="nav in navs"
v-bind:key="nav"
v-bind:class="['nav-button', { active: currentNav === nav }]"
v-on:click="currentNav = nav">
{{ nav }}
</nav>
</div>
<!-- CONTENT COMPONENT-->
<transition name="flipit">
<component
v-bind:is="contentComponent"
class="contentDetails">
</component>
</transition>
</div>
I am expecting to see either the product banner or blog-post displayed from the product-banner or blog-post component inside the nav-home component.
You need to pass banners and posts as props to your nav-home component too.
<component
:is="contentComponent"
class="contentDetails"
:banners="banners"
:posts="posts"
>
Vue.component('nav-home', {
props: ['banners', 'posts'],
template: `
<div>
<h1> Home component </h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<!-- banner -->
<product-banner
v-for="banner in banners"
:key="'banner' + banner.id"
:banner="banner"
/>
<!-- post -->
<blog-post
v-for="post in posts"
:key="'post' + post.id"
:post="post"
/>
</div>
`
})
Vue.component('blog-post', {
props: ['post'],
template: `<h3>{{post.title}}</h3>`
})
Vue.component('product-banner', {
props: ['banner'],
template: `
<div id="product-banner">
<div class="product-container">
<h2>{{banner.title}}</h2>
<p>{{banner.content}}</p>
</div>
</div>
`
})
new Vue({
el: '#app',
data: {
currentNav: 'Home',
navs: ['Home'],
posts: [
{
id: 1,
title: 'Title one'
},
{
id: 2,
title: 'Title two'
},
{
id: 3,
title: 'Title three'
}
],
banners: [
{
id: 1,
title: 'Title 1',
content: 'Content 1'
},
{
id: 2,
title: 'Title 2',
content: 'Content 2'
},
{
id: 3,
title: 'Title 3',
content: 'Content 3'
}
]
},
computed: {
contentComponent: function() {
return 'nav-' + this.currentNav.toLowerCase()
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<!-- NAVIGATION -->
<div class="nav">
<nav
v-for="nav in navs"
:key="nav"
:class="['nav-button', { active: currentNav === nav }]"
#click="currentNav = nav"
>
{{ nav }}
</nav>
</div>
<!-- CONTENT COMPONENT-->
<transition name="flipit">
<component
:is="contentComponent"
class="contentDetails"
:banners="banners"
:posts="posts"
>
</component>
</transition>
</div>
I've made a few others tweaks too:
Shortening v-bind: to : and v-on: to #.
Changing the key attributes so that banners and posts wouldn't clash with each other.
Renaming the prop for blog-post from posts to post, updating the template accordingly.

Vue Carousel not sliding correctly

I am working on a project using Vue Carousel for some product slides with images and text in each slide. I only want 5 to show up on a page and I want the nav arrows and I want it to drag. I can't get any of that stuff working. I tried following the examples as best as I can, but there is limited resources that I can find on this plugin.
new Vue({
el: "#app",
components: {
'carousel': VueCarousel.Carousel,
'slide': VueCarousel.Slide
},
props: {
numSlides: {
type: Number,
default: 5
},
itemsPerPageCssStyle: {
type: String,
default: "slider5buckets"
}
},
data: function () {
return {
products: [
{id: 1, img: 'https://placeimg.com/100/100'},
{id: 2, img: 'https://placeimg.com/101/101'},
{id: 3, img: 'https://placeimg.com/102/102'},
{id: 4, img: 'https://placeimg.com/103/103'},
{id: 5, img: 'https://placeimg.com/104/104'},
{id: 6, img: 'https://placeimg.com/105/105'},
{id: 7, img: 'https://placeimg.com/106/106'},
{id: 8, img: 'https://placeimg.com/107/107'},
{id: 9, img: 'https://placeimg.com/108/108'},
{id: 10, img: 'https://placeimg.com/109/109'},
{id: 11, img: 'https://placeimg.com/110/110'},
{id: 12, img: 'https://placeimg.com/111/111'},
{id: 13, img: 'https://placeimg.com/112/112'},
]
}
}
})
.VueCarousel-slide {
height: 350px;
text-align: center;
}
.VueCarousel-slide .img-container {
height: 130px;
width: 100%;
float: left;
}
.VueCarousel-slide img {
margin: 0 auto;
}
.VueCarousel-slide h3 {
height: 180px;
}
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
<script src="https://ssense.github.io/vue-carousel/js/vue-carousel.min.js"></script>
<div id="app">
<div
:class="['imageSliderContainer', itemsPerPageCssStyle]"
style="width:100%;height:374px;">
<div
class="wrapper"
style="height:355px;margin-left:15px;padding-right:4px;z-index:1;overflow: hidden;">
<div class="carousel-view">
<carousel
:per-page="5"
:navigation-enabled="true"
:min-swipe-distance="1">
<div
v-for="product in products"
:key="product.id">
<slide>
<div class="img-container">
<img
:src="product.img"
:alt="'Product #' + product.id">
</div>
<h3>Product #{{ product.id }}</h3>
<a
href="#"
tabindex="0"
name="instantadd">
<div class="btn_CA_Search buttonSearch gradient"> Add to Cart</div>
</a>
</slide>
</div>
</carousel>
</div>
</div>
</div>
</div>
Here is my Fiddle that is behaving exactly like it is behaving in my project. It is showing as many slides as it can fit, not 5. And it won't drag with the mouse correctly.
http://jsfiddle.net/gdw2hn5x/
Your template syntax should be:
<carousel>
<slide></slide>
<slide></slide>
<slide></slide>
...
</carousel>
You have an extra <div> wrapping the slides which you don't need and seems to break the component. You can put the v-for directly on the slide tag.
new Vue({
el: "#app",
components: {
'carousel': VueCarousel.Carousel,
'slide': VueCarousel.Slide
},
props: {
numSlides: {
type: Number,
default: 5
},
itemsPerPageCssStyle: {
type: String,
default: "slider5buckets"
}
},
data: function () {
return {
products: [
{id: 1, img: 'https://placeimg.com/100/100'},
{id: 2, img: 'https://placeimg.com/101/101'},
{id: 3, img: 'https://placeimg.com/102/102'},
{id: 4, img: 'https://placeimg.com/103/103'},
{id: 5, img: 'https://placeimg.com/104/104'},
{id: 6, img: 'https://placeimg.com/105/105'},
{id: 7, img: 'https://placeimg.com/106/106'},
{id: 8, img: 'https://placeimg.com/107/107'},
{id: 9, img: 'https://placeimg.com/108/108'},
{id: 10, img: 'https://placeimg.com/109/109'},
{id: 11, img: 'https://placeimg.com/110/110'},
{id: 12, img: 'https://placeimg.com/111/111'},
{id: 13, img: 'https://placeimg.com/112/112'},
]
}
}
})
.VueCarousel-slide {
height: 350px;
text-align: center;
}
.VueCarousel-slide .img-container {
height: 130px;
width: 100%;
float: left;
}
.VueCarousel-slide img {
margin: 0 auto;
}
.VueCarousel-slide h3 {
height: 180px;
}
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
<script src="https://ssense.github.io/vue-carousel/js/vue-carousel.min.js"></script>
<div id="app">
<div
:class="['imageSliderContainer', itemsPerPageCssStyle]"
style="width:100%;height:374px;">
<div
class="wrapper"
style="height:355px;margin-left:15px;padding-right:4px;z-index:1;overflow: hidden;">
<div class="carousel-view">
<carousel
:per-page="5"
:navigation-enabled="true"
:min-swipe-distance="1">
<!-- don't wrap with div here -->
<!-- just v-for on slide -->
<slide
v-for="product in products"
:key="product.id"
>
<div class="img-container">
<img
:src="product.img"
:alt="'Product #' + product.id">
</div>
<h3>Product #{{ product.id }}</h3>
<a
href="#"
tabindex="0"
name="instantadd">
<div class="btn_CA_Search buttonSearch gradient"> Add to Cart</div>
</a>
</slide>
</carousel>
</div>
</div>
</div>
</div>