TipTap Vue component - how to toggle wrap on node from component button - javascript

The awesome tiptap wrapper for prosemirror comes with nice documentation but it lacks some clarification how to approach some (i think) basic scenarios when developing custom extensions.
My question is how to invoke toggleWrap on the node when in vue component's context.
I found example that uses transactions and allows for delete - but what i want is to clear the node leaving the text of node intact.
get view() {
return {
directives: {
"click-outside": clickOutside
},
props: ['node', 'updateAttrs', 'view', 'selected', 'getPos'],
data() {
return {
showMenu: false
}
},
computed: {
href: {
get() {
return this.node.attrs.href
},
set(href) {
this.updateAttrs({
href,
})
},
},
},
methods: {
// deleteNode() {
// let transaction = this.view.state.tr // tr - transaction
// let pos = this.getPos()
// transaction.delete(pos, pos + this.node.nodeSize)
// this.view.dispatch(transaction)
// },
stopLinkPropagation(){
return null;
},
hideMenu(){
this.showMenu = false
}
},
template: `<div #click="showMenu = true" v-click-outside="hideMenu">
<a class="email-button" #click.prevent="stopLinkPropagation" :href="href" v-text="node.textContent"></a>
<input class="iframe__input" type="text" v-model="href" v-if="showMenu" />
<button #click="clearNode">clear button wrap</button>
</div>`,
}
}
Any help would be awesome. Thanks.

Related

Vuejs: Passing SAVE function into CRUD component

I am struggeling with a proper solution which requires an advanced parent-child communication in vuejs. There can be many different parent components which has a logic how to save data. From the other side there will be only one child component which has a list of elements and a form to create new elements but it doesn't know how to save the data.
The question is: Is there any other way (better approach) to have the same functionality but to get rid of this.$refs.child links. For example I am wondering if I can just pass a function (SaveParent1(...) or SaveParent2(...)) to the child component. But the problem is the function contains some parent's variables which won't be available in child context and those variables could be changed during the runtime.
Just few clarifications:
The methods SaveParent1 and SaveParent2 in real life return
Promise (axios).
The child-component is like a CRUD which is used
everywhere else.
At the moment the communication looks like that: CHILD -event-> PARENT -ref-> CHILD.
Bellow is the example:
<div id="app">
<h2>😀Advanced Parent-Child Communication:</h2>
<parent-component1 param1="ABC"></parent-component1>
<parent-component2 param2="XYZ"></parent-component2>
</div>
Vue.component('parent-component1', {
props: { param1: { type: String, required: true } },
methods: {
onChildSubmit(p) {
// Here will be some logic to save the param. Many different parents might have different logic and all of them use the same child component. So child-component contains list, form and validation message but does not know how to save the param to the database.
var error = SaveParent1({ form: { p: p, param1: this.param1 } });
if (error)
this.$refs.child.paramFailed(error);
else
this.$refs.child.paramAdded(p);
}
},
template: `<div class="parent"><p>Here is parent ONE:</p><child-component ref="child" #submit="onChildSubmit"></child-component></div>`
});
Vue.component('parent-component2', {
props: { param2: { type: String, required: true } },
methods: {
onChildSubmit(p) {
// Here is a different logic to save the param. In prictice it is gonna be different requests to the server.
var error = SaveParent2({ form: { p: p, param2: this.param2 } });
if (error)
this.$refs.child.paramFailed(error);
else
this.$refs.child.paramAdded(p);
}
},
template: `<div class="parent"><p>Here is parent TWO:</p><child-component ref="child" #submit="onChildSubmit"></child-component></div>`
});
Vue.component('child-component', {
data() {
return {
currentParam: "",
allParams: [],
errorMessage: ""
}
},
methods: {
submit() {
this.errorMessage = "";
this.$emit('submit', this.currentParam);
},
paramAdded(p) {
this.currentParam = "";
this.allParams.push(p);
},
paramFailed(msg) {
this.errorMessage = msg;
}
},
template: `<div><ol><li v-for="p in allParams">{{p}}</li></ol><label>Add Param: <input v-model="currentParam"></label><button #click="submit" :disabled="!currentParam">Submit</button><p class="error">{{errorMessage}}</p></div>`
});
function SaveParent1(data) {
// Axios API to save data. Bellow is a simulation.
if (Math.random() > 0.5)
return null;
else
return 'Parent1: You are not lucky today';
}
function SaveParent2(data) {
// Axios API to save data. Bellow is a simulation.
if (Math.random() > 0.5)
return null;
else
return 'Parent2: You are not lucky today';
}
new Vue({
el: "#app"
});
There is also a live demo available: https://jsfiddle.net/FairKing/novdmcxp/
Architecturally I recommend having a service that is completely abstract from the component hierarchy and that you can inject and use in each of the components. With this kind of component hierarchy and architecture it is easy to run into these issues. It is important to abstract as much functionality and business logic from the components as possible. I think of components in these modern frameworks just merely as HTML templates on steroids, which should at most act as controllers, keeping them as dumb and as thin as possible so that you don't run into these situations. I do not know vue.js so I cannot give you the technical solution but hope this indication helps
I think I have found a solution. So no two ways communication. I can just pass a method and the child will do everything without communicating with parent. I am happy with that I am marking it as an answer. Thanks everyone for your help.
Let me please know what do you think guys.
Bellow is my solution:
<div id="app">
<h2>😀Advanced Parent-Child Communication:</h2>
<parent-component1 param1="ABC"></parent-component1>
<parent-component2 param2="XYZ"></parent-component2>
</div>
Vue.component('parent-component1', {
props: { param1: { type: String, required: true } },
computed: {
saveFunc() {
return function(p) { SaveParent1({ form: { p: p, param1: this.param1 } }); }.bind(this);
}
},
template: `<div class="parent"><p>Here is parent ONE:</p><child-component :saveFunc="saveFunc"></child-component></div>`
});
Vue.component('parent-component2', {
props: { param2: { type: String, required: true } },
computed: {
saveFunc() {
return function(p) { SaveParent2({ form: { p: p, param2: this.param2 } }); }.bind(this);
}
},
template: `<div class="parent"><p>Here is parent TWO:</p><child-component :saveFunc="saveFunc"></child-component></div>`
});
Vue.component('child-component', {
props: {
saveFunc: { type: Function, required: true }, // This is gonna be a Promise in real life.
},
data() {
return {
currentParam: "",
allParams: [],
errorMessage: ""
}
},
methods: {
submit() {
this.errorMessage = "";
var error = this.saveFunc(this.currentParam);
if (error)
this.paramFailed(error);
else
this.paramAdded(this.currentParam);
},
paramAdded(p) {
this.currentParam = "";
this.allParams.push(p);
},
paramFailed(msg) {
this.errorMessage = msg;
}
},
template: `<div><ol><li v-for="p in allParams">{{p}}</li></ol><label>Add Param: <input v-model="currentParam"></label><button #click="submit" :disabled="!currentParam">Submit</button><p class="error">{{errorMessage}}</p></div>`
});
function SaveParent1(data) {
console.log(data);
// Axios API to save data
if (Math.random() > 0.5)
return null;
else
return 'Parent1: You are not lucky today';
}
function SaveParent2(data) {
console.log(data);
// Axios API to save data
if (Math.random() > 0.5)
return null;
else
return 'Parent2: You are not lucky today';
}
new Vue({
el: "#app"
});
The demo link: https://jsfiddle.net/FairKing/novdmcxp/126/

VueJS / JS DOM Watch / Observer in a multi phase render scenario

Scenario:
I’m developing a Vue scroll component that wraps around a dynamic number of HTML sections and then dynamically builds out vertical page navigation allowing the user to scroll or jump to page locations onScroll.
Detail:
a. In my example my scroll component wraps 3 sections. All section id’s start with "js-page-section-{{index}}"
b. The objective is to get the list of section nodes (above) and then dynamically build out vertical page (nav) navigation based on the n number of nodes found in the query matching selector criteria. Therefore, three sections will result in three page section navigation items. All side navigation start with “js-side-nav-{{index}}>".
c. Once the side navigation is rendered I need to query all the navigation nodes in order to control classes, heights, display, opacity, etc. i.e document.querySelectorAll('*[id^="js-side-nav"]');
EDIT
Based on some research here are the options for my problem. Again my problem being 3 phase DOM state management i.e. STEP 1. Read all nodes equal to x, then STEP 2. Build Side Nav scroll based on n number of nodes in document, and then STEP 3. Read all nav nodes to sync with scroll of document nodes:
Create some sort of event system is $emit() && $on. In my opinion this gets messy very quickly and feels like a poor solution. I found myself quickly jumping to $root
Vuex. but that feels like an overkill
sync. Works but really that is for parent child property state management but that again requires $emit() && $on.
Promise. based service class. This seems like the right solution, but frankly it became a bit of pain managing multiple promises.
I attempted to use Vue $ref but frankly it seems better for managing state rather than multi stage DOM manipulation where a observer event approach is better.
The solution that seems to work is Vues $nextTick(). which seems to be similar to AngularJS $digest. In essence it is a . setTimeout(). type approach just pausing for next digest cycle. That said there is the scenario where the tick doesn’t sync the time requires so I built a throttle method. Below is the code update for what is worth.
The refactored watch with nextTick()
watch: {
'page.sections': {
handler(nodeList, oldNodeList){
if (this.isNodeList(nodeList) && _.size(nodeList) && this.sideNavActive) {
return this.$nextTick(this.sideNavInit);
}
},
deep: true
},
},
The REFACTORED Vue component
<template>
<div v-scroll="handleScroll">
<nav class="nav__wrapper" id="navbar-example">
<ul class="nav">
<li role="presentation"
:id="sideNavPrefix + '-' + (index + 1)"
v-for="(item, key,index) in page.sections">
<a :href="'#' + getAttribute(item,'id')">
<p class="nav__counter" v-text="('0' + (index + 1))"></p>
<h3 class="nav__title" v-text="getAttribute(item,'data-title')"></h3>
<p class="nav__body" v-text="getAttribute(item,'data-body')"></p>
</a>
</li>
</ul>
</nav>
<slot></slot>
</div>
</template>
<script>
import ScrollPageService from '../services/ScrollPageService.js';
const _S = "section", _N = "sidenavs";
export default {
name: "ScrollSection",
props: {
nodeId: {
type: String,
required: true
},
sideNavActive: {
type: Boolean,
default: true,
required: false
},
sideNavPrefix: {
type: String,
default: "js-side-nav",
required: false
},
sideNavClass: {
type: String,
default: "active",
required: false
},
sectionClass: {
type: String,
default: "inview",
required: false
}
},
directives: {
scroll: {
inserted: function (el, binding, vnode) {
let f = function(evt) {
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f);
}
};
window.addEventListener('scroll', f);
}
},
},
data: function () {
return {
scrollService: {},
page: {
sections: {},
sidenavs: {}
}
}
},
methods: {
getAttribute: function(element, key) {
return element.getAttribute(key);
},
updateViewPort: function() {
if (this.scrollService.isInCurrent(window.scrollY)) return;
[this.page.sections, this.page.sidenavs] = this.scrollService.updateNodeList(window.scrollY);
},
handleScroll: function(evt, el) {
if ( !(this.isScrollInstance()) ) {
return this.$nextTick(this.inViewportInit);
}
this.updateViewPort();
},
getNodeList: function(key) {
this.page[key] = this.scrollService.getNodeList(key);
},
isScrollInstance: function() {
return this.scrollService instanceof ScrollPageService;
},
sideNavInit: function() {
if (this.isScrollInstance() && this.scrollService.navInit(this.sideNavPrefix, this.sideNavClass)) this.getNodeList(_N);
},
inViewportInit: function() {
if (!(this.isScrollInstance()) && ((this.scrollService = new ScrollPageService(this.nodeId, this.sectionClass)) instanceof ScrollPageService)) this.getNodeList(_S);
},
isNodeList: function(nodes) {
return NodeList.prototype.isPrototypeOf(nodes);
},
},
watch: {
'page.sections': {
handler(nodeList, oldNodeList){
if (this.isNodeList(nodeList) && _.size(nodeList) && this.sideNavActive) {
return this.$nextTick(this.sideNavInit);
}
},
deep: true
},
},
mounted() {
return this.$nextTick(this.inViewportInit);
},
}
</script>
END EDIT
ORIGINAL POST
Problem & Question:
PROBLEM:
The query of sections and render of navs work fine. However, querying the nav elements fails as the DOM has not completed the render. Therefore, I’m forced to use a setTimeout() function. Even if I use a watch I’m still forced to use timeout.
QUESTION:
Is there a promise or observer in Vue or JS I can use to check to see when the DOM has finished rendering the nav elements so that I can then read them? Example in AngularJS we might use $observe
HTML EXAMPLE
<html>
<head></head>
<body>
<scroll-section>
<div id="js-page-section-1"
data-title="One"
data-body="One Body">
</div>
<div id="js-page-section-2"
data-title="Two"
data-body="Two Body">
</div>
<div id="js-page-section-3"
data-title="Three"
data-body="THree Body">
</div>
</scroll-section>
</body>
</html>
Vue Compenent
<template>
<div v-scroll="handleScroll">
<nav class="nav__wrapper" id="navbar-example">
<ul class="nav">
<li role="presentation"
:id="[idOfSideNav(key)]"
v-for="(item, key,index) in page.sections.items">
<a :href="getId(item)">
<p class="nav__counter">{{key}}</p>
<h3 class="nav__title" v-text="item.getAttribute('data-title')"></h3>
<p class="nav__body" v-text="item.getAttribute('data-body')"></p>
</a>
</li>
</ul>
</nav>
<slot></slot>
</div>
</template>
<script>
export default {
name: "ScrollSection",
directives: {
scroll: {
inserted: function (el, binding, vnode) {
let f = function(evt) {
_.forEach(vnode.context.page.sections.items, function (elem,k) {
if (window.scrollY >= elem.offsetTop && window.scrollY <= (elem.offsetTop + elem.offsetHeight)) {
if (!vnode.context.page.sections.items[k].classList.contains("in-viewport") ) {
vnode.context.page.sections.items[k].classList.add("in-viewport");
}
if (!vnode.context.page.sidenavs.items[k].classList.contains("active") ) {
vnode.context.page.sidenavs.items[k].classList.add("active");
}
} else {
if (elem.classList.contains("in-viewport") ) {
elem.classList.remove("in-viewport");
}
vnode.context.page.sidenavs.items[k].classList.remove("active");
}
});
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f);
}
};
window.addEventListener('scroll', f);
},
},
},
data: function () {
return {
page: {
sections: {},
sidenavs: {}
}
}
},
methods: {
handleScroll: function(evt, el) {
// Remove for brevity
},
idOfSideNav: function(key) {
return "js-side-nav-" + (key+1);
},
classOfSideNav: function(key) {
if (key==="0") {return "active"}
},
elementsOfSideNav:function() {
this.page.sidenavs = document.querySelectorAll('*[id^="js-side-nav"]');
},
elementsOfSections:function() {
this.page.sections = document.querySelectorAll('*[id^="page-section"]');
},
},
watch: {
'page.sections': function (val) {
if (_.has(val,'items') && _.size(val.items)) {
var self = this;
setTimeout(function(){
self.elementsOfSideNavs();
}, 300);
}
}
},
mounted() {
this.elementsOfSections();
},
}
</script>
I hope I can help you with what I'm going to post here. A friend of mine developed a function that we use in several places, and reading your question reminded me of it.
"Is there a promise or observer in Vue or JS I can use to check to see when the DOM has finished rendering the nav elements so that I can then read them?"
I thought about this function (source), here below. It takes a function (observe) and tries to satisfy it a number of times.
I believe you can use it at some point in component creation or page initialization; I admit that I didn't understand your scenario very well. However, some points of your question immediately made me think about this functionality. "...wait for something to happen and then make something else happen."
<> Credits to #Markkop the creator of that snippet/func =)
/**
* Waits for object existence using a function to retrieve its value.
*
* #param { function() : T } getValueFunction
* #param { number } [maxTries=10] - Number of tries before the error catch.
* #param { number } [timeInterval=200] - Time interval between the requests in milis.
* #returns { Promise.<T> } Promise of the checked value.
*/
export function waitForExistence(getValueFunction, maxTries = 10, timeInterval = 200) {
return new Promise((resolve, reject) => {
let tries = 0
const interval = setInterval(() => {
tries += 1
const value = getValueFunction()
if (value) {
clearInterval(interval)
return resolve(value)
}
if (tries >= maxTries) {
clearInterval(interval)
return reject(new Error(`Could not find any value using ${tries} tentatives`))
}
}, timeInterval)
})
}
Example
function getPotatoElement () {
return window.document.querySelector('#potato-scroller')
}
function hasPotatoElement () {
return Boolean(getPotatoElement())
}
// when something load
window.document.addEventListener('load', async () => {
// we try sometimes to check if our element exists
const has = await waitForExistence(hasPotatoElement)
if (has) {
// and if it exists, we do this
doThingThatNeedPotato()
}
// or you could use a promise chain
waitForExistence(hasPotatoElement)
.then(returnFromWaitedFunction => { /* hasPotatoElement */
if (has) {
doThingThatNeedPotato(getPotatoElement())
}
})
})

vuejs - remount after update background url

I have a vue component with the binding style:
<div id="profile-icon" :style="{'background': 'url('+profile.icon+')'}"></div>
<input name="name" v-model="profile.name" type="text" placeholder="Name">
The image will only display if I update the profile.icon under beforeMount function:
props: ['profileprops'],
data: function() {
return {
profile: {
address:'',
name:'Name',
description:'Short description',
address:'address',
user_id:'',
background:'/images/admin/bg.png',
icon:''
},
}
},
beforeMount: function() {
console.log('profileedit mounted.')
var self = this;
if(self.profileprops){
self.profile = self.profileprops;
};
if(!self.profile.icon){
self.profile.icon = '/images/admin/icon-144.png';
},
mounted: function() {
var self = this;
self.$eventHub.$on('ImageSelected', (imagelink) => {///<-- I have another vue component for selecting images to emit event and to catch it here.
self.profile.icon = imagelink;
$('#profilemodal').modal('hide');
});
},
watch: {
'profile': {
handler: function(newVal, oldVal) {
console.log("changed")
},
deep: true
},
}
I did a few tests:
if I select the image, I can see the console.log within the $on has given the correct data from profile.icon. However, the :style did not update the background image with the new profile.icon. And watch.profile did not react at all.
If I start to type on input with v-model bind with profile.name, everything update immediately, including the background image in :style.
I believe I must have miss something to sync the inside and outside of the $on. How can I resolve this?
I found the solution here.
So I just $forceUpdate() inside $on:
self.$eventHub.$on('ImageSelected', (imagelink) => {
self.profile.icon = imagelink;
self.$forceUpdate();//<-- this does the magic.
});
then the data will update and re-render.

Compiling Vue.js component while rendering Laravel Blade partial view

I have a simple component in Vue.js which is used in a partial view - question.blade.php:
{{--HTML code--}}
<my-component type='question'>
<div class="question">[Very long text content...]</div>
</my-component>
{{--more HTML code--}}
The idea behind the component is to create a "show more - show less" logic around the question content.
The component is compiled and renders just fine on page load. However, there are cases where I need to dynamically load a question via Ajax. For this, I make a simple jQuery Ajax call, retrieve the HTML of the question.blade.php and append it in the DOM. The problem is, the component is not compiled.
How do I make sure the component is always compiled when the partial view gets rendered, independently of whether it occurs on page load or via Ajax call?
Full component code:
{% verbatim %}
<template>
<div>
<div v-bind:class="cssClasses" v-html="content"></div>
<div v-if="activateShowMore && !isShown" class="sml-button closed" v-on:click="toggleButton()">
<span class="sml-ellipsis">...</span><span class="sml-label">{{$t('show_more')}}</span>
</div>
<div v-if="activateShowMore && isShown" class="sml-button open" v-on:click="toggleButton()">
<span class="sml-label">{{$t('show_less')}}</span>
</div>
</div>
</template>
<style lang="sass" scoped>
/*styles*/
</style>
<script type="text/babel">
export default {
props: ['content', 'type'],
data() {
return {
activateShowMore: false,
isShown: false,
cssClasses: this.getCssClasses()
}
},
locales: {
en: {
'show_more': 'show more',
'show_less': 'show less'
},
de: {
'show_more': 'mehr anzeigen',
'show_less': 'weniger anzeigen'
}
},
mounted() {
this.checkShowMore();
},
watch: {
isShown: function(shouldBeShown) {
this.cssClasses = this.getCssClasses(shouldBeShown);
}
},
methods: {
checkShowMore: function() {
let $element = $(this.$el);
let visibleHeight = $element.outerHeight();
let realHeight = $element.find('.text-area-read').first().outerHeight();
let maxHeight = this.getMaxHeight();
this.activateShowMore = (visibleHeight === maxHeight) && (visibleHeight < realHeight);
},
getMaxHeight: function() {
switch (this.type) {
case 'question':
return 105;
case 'answer':
return 64;
}
},
toggleButton: function() {
this.isShown = !this.isShown;
},
getCssClasses: function(shouldBeShown) {
if (undefined === shouldBeShown || !shouldBeShown) {
return 'sml-container' + ' sml-' + this.type + ' sml-max-height';
}
return 'sml-container' + ' sml-' + this.type;
}
}
}
</script>
I don't think this is the best way but it should do the trick. But I had to deal with vue and jquery communication before.
What I did is created a hidden input and changed the value with jquery after the ajax call finished and then triggered the change event with jquery. Then you already listening to the event inside vue and you will know you need to update the content. This should get you going with some modification to your vue component and should be able to update. If you need to send the content to vue you might need to send it in the input hidden value. I did a quick demo code to explain what I mean. Here's the link.
var app = new Vue({
el: '#app',
data(){
return{
content: 'hi there',
}
},
methods: {
onChangeHandler: function(e){
this.content = e.target.value
}
},
});
$('#me').on('click',function(){
$('#update').val('Good Day!')
$('#update').trigger("click")
});

Vue.js component with Vuex.js (instead of vue-i18n.js)

I've been trying to reproduce the button behavior that I've here, but with a different implementation. Basically, I'm trying to use Vuex instead of vue-i18n.js for internationalization purposes.
I now have the following code block, the purpose of which is to create language states and perform a XMLHttpRequest (for the .json files storing the various translations):
Vue.use(Vuex);
var storelang = new Vuex.Store({
state: {
lang: {}
},
mutations: {
LANG: function (state, ln) {
function loadJSON(callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', '../resources/i18n/' + ln + '.json', true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
}
loadJSON(function (languageJSON) {
state.lang = JSON.parse(languageJSON);
})
},
strict: true
}
});
var mix = Vue.mixin({
computed: {
lang: function () {
return storelang.state.lang;
}
}
});
On my component constructor (created and initialized in the root Vue instance), I've the following:
components: {
lang: {
template: '<button type="button" class="btn btn-info" #click.prevent=activate(lang.code) #click="setActiveLang" v-show="!isActive">{{ lang.code }}</button>',
props: [
'active',
'lang'
],
computed: {
isActive: function() {
return this.lang.code == this.active.code
}
},
methods: {
activate: function(code) {
storelang.dispatch('LANG', code);
},
setActiveLang: function() {
this.active = this.lang;
}
},
ready: function() {
storelang.dispatch('LANG', 'en'); //default language
}
}
}
On my root Vue instance's data object, I've added the following:
langs: [{
code: "en"
}, {
code: "fr"
}, {
code: "pt"
}],
active: {
"code": "pt"
}
And finally, on my html:
<div v-for="lang in langs">
<p>
<lang :lang="lang" :active.sync="active"></lang>
</p>
</div>
I cannot figure out what I'm doing wrong here.
UPDATE
Here's a JsFiddle (I've exchanged the XMLHttpRequest request for json arrays). Also, this is a working example, but the language selector buttons do not hide when the respective language is selected, which is the opposite of what I want. Meaning that, I'm attempting to hide each individual language selector button when the user clicks it and selects the respective language (while showing the other language selector buttons).
The solution involves saving anactive state in the store, in addition to the lang state:
new Vuex.Store({
state: {
active: {},
lang: {}
Adding an ACTIVE mutation:
ACTIVE: function(state, ln) {
var langcode = 'en'
//portuguese
if (ln === 'pt') {
langcode = 'pt'
}
//french
if (ln === 'fr') {
langcode = 'fr'
}
state.active = langcode
}
On the computed properties block, one also needs to add getter functions for the active state and return the langcode that is currently active:
Vue.mixin({
computed: {
lang: function() {
return storelang.state.lang
},
enIsActive: function() {
return storelang.state.active == 'en'
},
frIsActive: function() {
return storelang.state.active == 'fr'
},
ptIsActive: function() {
return storelang.state.active == 'pt'
}
}
})
Then, it is just a question of conditionally displaying each of the buttons on the component template by adding v-show="!enIsActive", v-show="!frIsActive", etc.:
var langBtn = Vue.extend({
template: '<button type="button" class="btn btn-info" #click.prevent=activate("en") v-show="!enIsActive">en</button><button type="button" class="btn btn-info" #click.prevent=activate("pt") v-show="!ptIsActive">pt</button><button type="button" class="btn btn-info" #click.prevent=activate("fr") v-show="!frIsActive">fr</button>',
Finally, on the activate method, adding a new line to change the active state when the user clicks a button:
methods: {
activate: function(x) {
storelang.dispatch('LANG', x)
storelang.dispatch('ACTIVE', x)
}
},
The full working code here.

Categories

Resources