I have the following template that I would like to use to render the contents of a Kendo grid wrapper cell:
<template>
<span>
Template Rendered: {{ templateArgs.name }}
</span>
</template>
<script>
export default {
name: 'template1',
data () {
return {
templateArgs: {}
}
}
}
</script>
I am able to do this using kendo-grid-column elements as follows:
<template>
<div>
<kendo-grid :data-source="datasource">
<kendo-grid-column field="name" title="Name" :template="itemTemplate"></kendo-grid-column>
</kendo-grid>
</div>
</template>
<script>
import Vue from 'vue'
import { Grid, GridColumn } from '#progress/kendo-grid-vue-wrapper'
import Template from './Template.vue'
var itemTemplate = Vue.component(Template.name, Template)
export default {
name: 'HelloWorld',
components: {
'kendo-grid': Grid,
'kendo-grid-column': GridColumn
},
methods: {
itemTemplate: function (e) {
return {
template: itemTemplate,
templateArgs: e
}
}
},
data () {
return {
datasource: [ { name: "Jane Doe" }, { name: "John Doe" } ]
}
}
}
</script>
I would like to use the kendo-grid columns attribute instead, as follows:
<template>
<div>
<kendo-grid :data-source="datasource" :columns="columns">
</kendo-grid>
</div>
</template>
<script>
import Vue from 'vue'
import { Grid, GridColumn } from '#progress/kendo-grid-vue-wrapper'
import Template from './Template.vue'
var itemTemplate = Vue.component(Template.name, Template)
export default {
name: 'HelloWorld',
components: {
'kendo-grid': Grid,
'kendo-grid-column': GridColumn
},
methods: {
itemTemplate: function (e) {
return {
template: itemTemplate,
templateArgs: e
}
}
},
data () {
return {
columns: [{ field: "name", title: "Name", template: this.itemTemplate }],
datasource: [ { name: "Jane Doe" }, { name: "John Doe" } ]
}
}
}
</script>
Something is wrong with the code in the second case, though. Instead of rendering the cells using the template, I am getting [object Object] as the cell contents. What should I do in order to fix this?
Using the columns property when using custom render templates is not supported by the Vue grid wrapper. Something similar can be done with a native grid, however.
Source: https://www.telerik.com/forums/custom-kendo-grid-wrapper-column-template-using-kendo-grid-columns-attribute
Related
I'm using a custom component as a column on vue-tables-2, to do that I'm using a vue-component as described here: vue-components
I've created a button that opens a modal to the user confirm some information, and after that I make a request to the backend and the record is changed on the database.
Now I want to refresh the data on the table, but I don't know how to do that. The documentation said about using the $ref, but this is not an option because my component is not the parent.
How can I do that?
Links to the code:
Component using 'vue-tables-2'
<template>
<div>
<div id="payment">
<input type="checkbox" v-model="onlyPending" #change="filterPay()">Apenas pendentes</input>
<v-server-table url="/api/payments" :columns="columns" :options="options" ></v-server-table>
</div>
</div>
</template>
<script>
import pay from './ModalConfirmPay.vue'
import {Event} from 'vue-tables-2';
export default {
name: "AeraListPayment",
props: ['groupId'],
data: function(){
let groupId = this.groupId;
return {
columns: ['name','value','course','due_date','paid','installment','pay'],
options: {
responseAdapter : function(data) {
data.data = data.data.map(payment => {
payment.paid = payment.paid ? "pago" : "pendente";
return payment;
})
return data;
},
headings: {
installment: 'Parcela',
paid: 'Status',
value: 'Valor',
due_date: 'Vencimento',
pay: 'Ação',
course: 'Curso',
name: 'Nome'
},
templates : {
pay
},
customFilters: ['onlyPending','groupId'],
initFilters:{groupId:groupId,onlyPending:true}
},
onlyPending: true
}
},
methods: {
filterPay(){
Event.$emit('vue-tables.filter::onlyPending', this.onlyPending);
}
}
}
</script>
Component that is being used as a custom column:
<template>
<div>
<button #click.prevent="show">Pagar</button>
<modal :name="modalName">
<p>Confirma o pagamento de {{data.value}} ?</p>
<p>Parcela: {{data.installment}}</p>
<p>Vecimento: {{data.due_date}}</p>
<button #click.prevent="pay">Confirmar</button>
<button #click.prevent="hide">Cancelar</button>
</modal>
</div>
</template>
<script>
import PaymentService from '../../services/PaymentService'
let service = new PaymentService();
export default {
name:"ModalConfirmPay",
props: ["data"],
computed: {
modalName: function () {
// `this` aponta para a instância Vue da variável `vm`
return `confirm-pay-${this.data.clientGroup_id}-${this.data.installment}`
}
},
methods: {
show () {
this.$modal.show(this.modalName);
},
pay ( ) {
service.pay(this.data)
.then(this.hide());
},
hide () {
this.$modal.hide(this.modalName);
}
}
}
</script>
First, defined an EventBus if you don't have
EventBus.vue
import Vue from 'vue'
export default new Vue()
In ListPayment.vue, import EventBus and listen for refresh-table event. Note that I add ref="table" to vue-tables-2 element
<template>
<v-server-table ref="table" ... />
</template>
<script>
import EventBus from './EventBus.vue'
export default {
mounted() {
EventBus.$on('refresh-table', this.refreshTable)
},
beforeDestroy() {
EventBus.$off('refresh-table', this.refreshTable)
},
methods: {
refreshTable() {
this.$refs.table.refresh();
}
}
}
</script>
Finally, emit event in modal
pay() {
service.pay(this.data)
.then(() => {
EventBus.$emit('refresh-table')
})
.then(this.hide());
}
I have an array in My store which i am looping through to get a list which will be their own pages and share a same paragraph. I have a button which hides the paragraph. How do i make it so that it hides only on the first page and not on the other pages? So the way it should work is that if hide the paragraph in the Apple page, it should still appear on the other four pages.Or for the matter, if i hide the paragraph on any of the pages, it should not affect on any other page. Thank You
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
show:true,
specialTypes:[
{
name: "Apple",
Id:1
},
{
name: "Banana",
Id:2
},
{
name: "Berries",
Id:3
},
{
name: "Mango",
Id:4
},
{
name: "Oranges",
Id:5
}
]
},
mutations: {
toggle : state => {
state.show = !state.show
}
},
actions: {
}
})
My app.vue file looks like this
<template>
<div>
<ul >
<li v-for="specialType in $store.state.specialTypes"
:key="specialType.specialTypeId" #click="setActiveType(specialType)"
:class="{'active': activeType === specialType}">
<a><span>{{ specialType.name }}</span></a>
</li>
</ul>
<router-view/>
<Home></Home>
<About></About>
</div>
</template>
<script>
import Home from "./views/Home"
import About from "./views/About"
export default {
components:{
Home,
About
},
data(){
return{
activeType:""
}
},
methods:{
setActiveType(type) {
this.activeType = type
}
}
}
</script>
This is the paragraph which is shared by each tab.
<template>
<p v-if="$store.state.show">This needs to disappear</p>
</template>
<script>
import {mapMutations} from "vuex"
export default {
data(){
return{
}
},
methods : {
...mapMutations ([
"toggle"
])
}
}
</script>
The button is on this file
<template>
<div>
<button #click="toggle">Click Me</button>
</div>
</template>
<script>
import {mapMutations} from "vuex"
export default {
methods : {
...mapMutations ([
"toggle"
])
}
}
</script>
In your code, you are comparing objects here.
:class="{'active': activeType === specialType}"
Javascript doesn't work like this.
Replace above line with.
:class="{'active': activeType.Id === specialType.Id}"
Example:
var jangoFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var bobaFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
console.log(bobaFett === jangoFett); // Outputs: false
The properties of bobaFett and jangoFett are identical, yet the objects themselves aren't considered equal.
I hope that helps.
i am learning typeahead with vue js. And i want to ask how can i set the default value in food?
<vue-bootstrap-typeahead
:data="list"
v-model="food"
/>
data() {
return {
//Not work
food: 'Apple',
list: [],'
}
}
FYI:https://www.npmjs.com/package/vue-bootstrap-typeahead
There's a workaround from github. You can directly set the inputValue using a ref for the typeahead:
<template>
<div id="app">
<vue-bootstrap-typeahead :data="list" v-model="food" ref="typeahead" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
data: function() {
return {
food: "Apple",
list: ["Aardvark", "Apple", "Beach", "Bear"]
};
},
components: {
HelloWorld
},
mounted() {
this.$refs.typeahead.inputValue = "Apple";
},
methods: {}
};
</script>
https://codesandbox.io/s/6xn4y5321k
I read Reactivity in Depth but can't solve the issue.
I'm creating a small single page app that contains images and text.
When the user clicks a button I want the language to change.
Currently I am storing the content in two files that export an object.
export default {
projects: [
{
title: 'Project Title',
year: 2016,
...
},
]
}
and importing that
import contentEn from './assets/content.en.js'
import contentDe from './assets/content.de.js'
new Vue({
el: '#app',
components: { App },
data: {
mainContent: {
content: contentEn
}
},
methods: {
switchToGerman(){
this.mainContent.content = contentDe
}
},
template: '<App :mainData="mainContent"/>',
})
When I assign another object to mainContent.content the rendering is not triggered.
I understand that adding and deleting properties from object don't lead to change detection but I switch out a whole object. I tried assigning it with this.$set with no success.
I also tried this and googled a lot but can't get it work.
Or is my approach just wrong?
Thank you for helping,
best,
ccarstens
EDIT:
See below the code for the App component and the ProjectElement
// App.vue
<template>
<div id="app">
<button #click="switchGerman">Deutsch</button>
<ProjectElement v-for="(project, key) in fullData.content.projects" :key="key" :content="project"/>
</div>
</template>
<script>
import ProjectElement from './components/ProjectElement'
export default {
name: 'App',
props: [
'mainData'
],
data () {
return{
fullData: {}
}
},
methods: {
switchGerman(){
this.$root.switchToGerman()
}
},
created(){
this.fullData = this.$props.mainData
},
watch: {
mainData: {
handler: function(newData){
this.fullData = newData
},
deep: true
}
},
components: {
ProjectElement,
}
}
</script>
And the ProjectElement
//ProjectElement.vue
<template>
<article :class="classObject" v-observe-visibility="{
callback: visibilityChanged,
throttle,
intersection: {
threshold
}
}">
<header v-html="description"></header>
<div class="content">
<carousel :per-page="1" :pagination-enabled="false">
<slide v-for="(slide, index) in projectContent.media" :key="index">
<VisualElement :content="slide" ></VisualElement>
</slide>
</carousel>
</div>
</article>
</template>
<script>
import {Carousel, Slide} from 'vue-carousel'
import VisualElement from './VisualElement'
export default {
name: "ProjectElement",
components: {
Carousel,
Slide,
VisualElement
},
props: [
'content'
],
data () {
return {
projectContent: {},
isVisible: false,
throttle: 300,
threshold: 0.8
}
},
created(){
this.projectContent = this.content
},
methods: {
visibilityChanged(isVisible){
this.isVisible = isVisible
}
},
computed: {
description(){
return `
<p>${ this.projectContent.title } - ${this.projectContent.year}</p>
<p>${ this.projectContent.description }</p>
`
},
classObject(){
return {
visible: this.isVisible,
'project-element': true
}
}
}
}
</script>
Did you try doing deep copy:
switchToGerman () {
const copyContent = JSON.parse(JSON.stringify(this.mainContent))
copyContent.content = contentDe
this.mainContent = copyContent
}
I found the solution (thank you #EricGuan for pointing out that the mistake must lay somewhere else)
As you can see in the original post I created a watcher for the mainData property and expected that this would trigger the re render.
What was missing is, that I didn't watch the content property on the ProjectElement component, thus not triggering a re render there.
I added this to ProjectElement.vue and now it works like a charm:
watch: {
content(newContent){
this.projectContent = newContent
}
},
Thank you everybody for helping me! <3
I'm building a SPA with a scroll navigation being populated with menu items based on section components.
In my Home.vue I'm importing the scrollNav and the sections like this:
<template>
<div class="front-page">
<scroll-nav v-if="scrollNavShown" #select="changeSection" :active-section="activeItem" :items="sections"></scroll-nav>
<fp-sections #loaded="buildNav" :active="activeItem"></fp-sections>
</div>
</template>
<script>
import scrollNav from '.././components/scrollNav.vue'
import fpSections from './fpSections.vue'
export default {
data() {
return {
scrollNavShown: true,
activeItem: 'sectionOne',
scrollPosition: 0,
sections: []
}
},
methods: {
buildNav(sections) {
this.sections = sections;
console.log(this.sections)
},
changeSection(e) {
this.activeItem = e
},
},
components: {
scrollNav,
fpSections
}
}
</script>
this.sections is initially empty, since I'm populating this array with data from the individual sections in fpSections.vue:
<template>
<div class="fp-sections">
<keep-alive>
<transition
#enter="enter"
#leave="leave"
:css="false"
>
<component :is="activeSection"></component>
</transition>
</keep-alive>
</div>
</template>
<script>
import sectionOne from './sections/sectionOne.vue'
import sectionTwo from './sections/sectionTwo.vue'
import sectionThree from './sections/sectionThree.vue'
export default {
components: {
sectionOne,
sectionTwo,
sectionThree
},
props: {
active: String
},
data() {
return {
activeSection: this.active,
sections: []
}
},
mounted() {
this.buildNav();
},
methods: {
buildNav() {
let _components = this.$options.components;
for(let prop in _components) {
if(!_components[prop].hasOwnProperty('data')) continue;
this.sections.push({
title: _components[prop].data().title,
name: _components[prop].data().name
})
}
this.$emit('loaded', this.sections)
},
enter(el) {
twm.to(el, .2, {
autoAlpha : 1
})
},
leave(el, done) {
twm.to(el, .2, {
autoAlpha : 0
})
}
}
}
</script>
The buildNav method loops through the individual components' data and pushes it to a scoped this.sections array which are then emitted back to Home.vue
Back in Home.vue this.sections is populated with the data emitted from fpSections.vue and passed back to it as a prop.
When I inspect with Vue devtools the props are passed down correctly but the data does not update.
What am I missing here? The data should react to props when it is updated in the parent right?
:active="activeItem"
this is calld "dynamic prop" not dynamic data. You set in once "onInit".
For reactivity you can do
computed:{
activeSection(){ return this.active;}
}
or
watch: {
active(){
//do something
}
}
You could use the .sync modifier and then you need to emit the update, see my example on how it would work:
Vue.component('button-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
props: ['counter'],
watch: {
counter: function(){
this.$emit('update:counter',this.counter)
}
},
})
new Vue({
el: '#counter-sync-example',
data: {
foo: 0,
bar: 0
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="counter-sync-example">
<p>foo {{ foo }} <button-counter :counter="foo"></button-counter> (no sync)</p>
<p>bar {{ bar }} <button-counter :counter.sync="bar"></button-counter> (.sync)</p>
</div>