How to send readOnly as a props in Vuequill - vue.js

I have a text-editor component which is based on vue-quill.
<text-editor :disabled="!editModeActive"/>
So this prop is successfully being sent to the component:
<template>
<QuillEditor v-model:content="value" theme="snow" toolbar="#custom-toolbar" :readOnly="disabled">
<template #toolbar>
<div id="custom-toolbar">
</div>
</template>
</QuillEditor>
</template>
<script>
import { QuillEditor } from '#vueup/vue-quill'
import '#vueup/vue-quill/dist/vue-quill.snow.css'
import {v4 as uuidv4} from "uuid";
export default {
props: {
value: { type: String, default: "" },
disabled: { type: Boolean, default: true },
},
}
</script>
So if I send the prop as a boolean (true/false), I can see that readOnly works fine. But if I send it as editModeActive (which depends on a button, when it is clicked, it is false, when it is not clicked it is true), it doesnt work anymore. And the things is inside of the component if I do :readOnly="true" instead of :readOnly="disabled", it works again. It doesnt give any error so I dont know whats wrong.
Thanks for helping.

Related

How to transform an "extends" from the vue class-api into the vue composition-api?

I have two components, that I would like to transform into composition api
Base component:
<template>
<div>
<h3>prop type: {{ typeof message }}</h3>
</div>
</template>
<script>
export default {
name: "BaseComponent",
props: {
message: {
type: String,
required: false
}
}
};
</script>
Extended component:
<script>
import BaseComponent from "./BaseComponent";
export default {
name: "ExtendedComponent",
extends: BaseComponent,
props: {
message: {
type: Number,
required: false
}
}
};
</script>
When changing both components into composition API, I run into issues and the inheritance does not seem to work properly. It doesn't seem to pass the right prop either. Am I missing something here? I would appreciate any help.

Add checks to vue component/template in development mode

I would like to add additional check to a vue component.
They should appears like this:
Those checks should be executed only in development mode.
Here an example:
<template>
<div>
<slot />
</div>
</template>
<script>
export default {
name: 'FuiButton',
props: {
labeled: {
type: Boolean,
},
labeledIcon: {
type: Boolean,
},
},
};
</script>
And the check could be, for instance, the two properties cannot be simultaneously true.
Another type of check could be: MyCompB can only exists as child of MyCompA.

Why do some of my images placed in a Vue template disappear when content in filtered?

I've been put in charge of a Drupal website that has some content that is placed through a complex set of Vue files. I've never used Vue so I need some help with a critical problem we're facing.
I have a news section on the site that users can filter from a dropdown (by choosing a topic from the menu). This works fine. However, whenever you bring back a previously filtered news story, the featured image fails to load. The Vue template brings back the default image. But this only happens for SOME of the images.
As I mentioned, the Vue.js file is complex - it imports nearly a dozen other files that control other features of the site. This is the code from the the most applicable file:
export default {
props: {
url: {
type: String,
required: true,
},
tag: {
type: String,
required: false,
},
eyebrow: {
type: String,
required: false,
},
title: {
type: String,
required: true,
},
image: {
type: Object,
required: false,
},
},
computed: {
imageUrl() {
return this.image && this.image.url ? this.image.url : '/themes/my-theme/images/no-image.jpg';
}
},
template: `
<a :href="url" class="news-card">
<span class="news-card__image">
<img :src="imageUrl" :alt="image.alt">
</span>
<span class="news-card__text">
<span class="news-card__tag" v-if="tag" v-html="tag">
</span>
<p class="news-card__eyebrow" v-if="eyebrow">
{{ eyebrow }}
</p>
<h2 class="news-card__title">
{{ title }}
</h2>
</span>
</a>
`
};
I would post more, but I don't know what else is needed to diagnose a Vue issue. Sorry, I'm brand new to Vue and it's very confusing!
UPDATE
I've added the view.js code below:
import Vue from '#vue';
import { mapState } from 'vuex';
import ListingGrid from '/components/listing-grid';
import ListingNav from '/components/listing-nav';
import ProgramCard from '/components/program-card';
import ListingCard from '/components/listing-card';
import NewsCard from '/components/news-card';
import ListingFilters from '/components/listing-filters';
import ListingLoader from '/components/listing-loader';
import ListingStickyNav from '/components/listing-sticky-nav';
import TabSelect from '/components/tab-select';
import ajaxStore from './store/ajax';
import staticStore from './store/static';
export default (id) => {
new Vue({
el: `#${id}`,
components: {
ListingFilters,
ListingGrid,
ListingNav,
ProgramCard,
ListingCard,
NewsCard,
ListingLoader,
ListingStickyNav,
TabSelect,
},
data: {
lazy: true
},
store: id === 'listing-static' ? staticStore : ajaxStore,
computed: mapState({
grid() {
return this.$store.getters.filteredGrid;
},
filters: state => state.filters,
tabs: state => state.tabs,
loading: state => state.loading,
showLoader: state => state.infiniteLoading,
cardType: state => state.cardType
}),
image: {
type: Object,
default: function () {
return {
url: '/themes/my-theme/images/no-image.jpg',
alt: 'Default No Image Alt Text'
}
},
},
template: `
<main class="view__content v-listing vue-loaded" id="${id}">
<listing-sticky-nav>
<template slot="tab-content">
<tab-select
:tabs="tabs"
v-if="tabs.items"
>
</tab-select>
</template>
<template slot="panel">
<listing-filters
:filters="filters"
additional-classes="mobile"
>
</listing-filters>
</template>
</listing-sticky-nav>
<listing-filters
:filters="filters"
>
</listing-filters>
<listing-grid
:has-results="grid.length > 0"
:loading="loading"
:lazy="lazy">
<listing-nav
v-if="tabs"
:tabs="tabs"
slot="nav"
>
</listing-nav>
<template slot="grid">
<component
:is="cardType"
v-bind="item"
v-for="item, index in grid"
:key="index">
</component>
</template>
<template slot="empty">
No results found.
</template>
</listing-grid>
<span class="v-listing-footer" v-if="showLoader">
<listing-loader></listing-loader>
<span class="visually-hidden">loading</span>
</span>
</main>
`
});
}
Although I agree with the comments above that there are many lacking elements, I am a huge proponent of VueJS and I would like to help if possible.
First glaring issue I see is that the property image is not required. The computed property imageUrl() is defensive in that it will return a default value of '/themes/my-theme/images/no-image.jpg' if the property image is omitted.
However in your template code in the <img /> you are referencing the object key of 'alt' from the image object which may or may not be present.
I would highly recommend adding a computed property of:
imageAlt() {
return this.image && this.image.alt ? this.image.alt : 'Default No Image Alt Text';
}
Then in your template update the image tag as follows:
<img :src="imageUrl" :alt="imageAlt">
This will harden your code ensuring that error cannot happen. You may want to update the default Alt Text as it was provided for example purposes only.
If this does not work in resolving the issue please comment with the following information for additional assistance.
Are there any JS errors in the console?
Did you download the Vue Devtools extension?
If so what is the computed properties value for imageUrl when working and after you experience this issue?
Update. As an alternative approach (and I prefer this) you could also remove all computed properties and provided a default image object. This would require no observers and looks like this:
image: {
type: Object,
default: function () {
return {
url: '/themes/my-theme/images/no-image.jpg',
alt: 'Default No Image Alt Text'
}
},
}
Your image tag would then look like this:
<img :src="image.url" :alt="image.alt">
For more info visit: https://v2.vuejs.org/v2/guide/components-props.html

Why is the activated lifecycle hook not called on first visit

I have a problem where a component within a router-view that is being kept alive does not call its activated lifecycle hook when first created. The created and mounted lifecycle hooks are being called. On a second visit, the activated hook is being called.
The scenario is quite complicated as there is a bit of nesting and slot using involved.
I've tried to create a minimal example which you can find below, or a bit more detailed on https://codesandbox.io/s/251k1pq9n.
Unfortunately, it is quite large and still not as complicated as the real code which I unfortunately cannot share.
Worse, I failed to reproduce the actual problem in my minimal example. Here, the created, mounted, and activated lifecycle hooks are all called when first visiting SlotExample.
In my real code, only the created and mounted, lifecycle hooks are called on the first visit, the activated hook is called on subsequent visits. Interestingly, all lifecycle hooks are called as expected for SlotParent.
The real code involves more nesting and makes use of slots to use layout components.
My code is using Vue 2.5.16 and Vue-Router 3.0.1 but it also doesn't work as expected in Due 2.6.7 and Vue-Router 3.0.2. I am also using Vuetify and Vue-Head but don't think think this has anything to do with my problem.
index.js.
Does anyone have an idea what I could have been doing wrong. I actually suspect a bug in vue-router
when using multiple nested slots and keep-alive but cannot reproduce.
index.js
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import Start from "./Start.vue";
import SlotExample from "./SlotExample.vue";
const routes = [
{
path: "/start",
component: Start
},
{
path: "/slotExample/:id",
component: SlotExample,
props: true
}
];
const router = new VueRouter({ routes });
Vue.use(VueRouter);
new Vue({
render: h => h(App),
router,
components: { App }
}).$mount("#app");
App.vue
<template>
<div id="app">
<div>
<keep-alive><router-view/></keep-alive>
</div>
</div>
</template>
SlotExample.vue
<template>
<div>
<h1>Slot Example</h1>
<router-link to="/start"><a>start</a></router-link>
<router-link to="/slotExample/123">
<a>slotExample 123</a>
</router-link>
<slot-parent :id="id">
<slot-child
slot-scope="user"
:firstName="user.firstName"
:lastName="user.lastName"/>
</slot-parent>
</div>
</template>
<script>
import SlotParent from "./SlotParent.vue";
import SlotChild from "./SlotChild.vue";
export default {
name: "slotExample",
components: { SlotParent, SlotChild },
props: {
id: {
type: String,
required: true
}
}
};
</script>
SlotParent.vue
<template>
<div>
<div slot="header"><h1>SlotParent</h1></div>
<div slot="content-area">
<slot :firstName="firstName" :lastName="lastName" />
</div>
</div>
</template>
<script>
export default {
name: "slotParent",
props: {
id: {
type: String,
required: true
}
},
computed: {
firstName() {
if (this.id === "123") {
return "John";
} else {
return "Jane";
}
},
lastName() {
return "Doe";
}
}
};
</script>
SlotChild.vue
<template>
<div>
<h2>SlotChild</h2>
<p>{{ firstName }} {{ lastName }}</p>
</div>
</template>
<script>
export default {
name: "slotChild",
props: {
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
},
created() {
console.log("slotChild created");
},
mounted() {
console.log("slotChild mounted");
},
activated() {
console.log("slotChild activated");
}
};
</script>
I think you need to put SlotChild within keep-alive block.
Take a look at vue js doc about activated hook

VueJS Passing computed data as a prop returns undefined

I've tried and tried, but i can't figure it out the problem. From what I could read elsewhere, the variable passed to the child component gets sent as undefined before the data is available in the parent.
Please see here for reference:
the code in codesandbox
<template>
<div id="app">
<child :parentData="data.message"/>
</div>
</template>
<script>
import Child from "./components/Child";
export default {
name: "App",
components: {
Child
},
computed: {
quote() { return 'Better late than never' }
},
data() {
return {
data: { message: this.quote } ,
thisWorks: { message: "You can see this message if you replace what is passed to the child" }
};
}
};
</script>
Then in the child:
<template>
<div>
<h1>I am the Child Component</h1>
<h2> {{ parentData }}</h2>
</div>
</template>
<script>
export default {
name: "Child",
props: {
parentData: { type: String, default: "I don't have parent data" },
},
};
</script>
The answer is, you cannot access the value of this.quote because at the moment the data objectis creating, the computed object actually does not exist.
This is an alternative, we will use the created() lifecycle hook to update the value of data object:
created(){
this.data = {
message: this.quote
}
},
You don't need to change any things, just adding those line of codes is enough.
I've already tested those codes in your CodeSandbox project and it works like a charm.
Hopefully it helps!