I am using CKEditor5 with Vue. In my Vuex store, I have the following property:
const state = {
EditMode: false,
}
On a button click by a user with permission, I modify the Vuex store. If EditMode: true, I want to display the in-line editor. Else, display the raw HTML editorData (the user is not authorized to edit, or not in edit mode). I do that below:
<template>
<vx-card :title="editorName" v-if="this.$store.state.EditMode">
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
</vx-card>
<vx-card :title="editorName" v-else>
<div v-html="editorData"></div>
</vx-card>
</template>
<script>
import InlineEditor from '#ckeditor/ckeditor5-build-inline'
export default {
name: "RichTextEditor",
props: {
editorName: {
type: String,
required: true,
},
},
data() {
return {
loaded: false,
time: null,
timeElapsedSinceEdit: 0,
editor: InlineEditor,
editorData: 'New entry!',
editorConfig: {
toolbar: {
items: [
'|',
'heading',
'fontFamily',
'fontSize',
'fontColor',
'bold',
'underline',
'italic',
'alignment',
'link',
'highlight',
'superscript',
'subscript',
'|',
'indent',
'outdent',
'|',
'blockQuote',
'horizontalLine',
'imageUpload',
'insertTable',
'mediaEmbed',
'undo',
'redo'
]
},
language: 'en',
image: {
toolbar: [
'imageTextAlternative',
'imageStyle:full',
'imageStyle:side'
]
},
table: {
contentToolbar: [
'tableColumn',
'tableRow',
'mergeTableCells',
'tableCellProperties',
'tableProperties'
]
},
},
}
},
// Below code is situation-specific and not completely relevant
watch: {
editorData: function() {
if (this.loaded) {
this.upsertData()
}
}
},
methods: {
async pollData() {
await
this.$http.get('/api/rte/' + this.editorName)
.then((response) => {
this.editorData = response.data.content
})
.catch((error) => {
if (window.environment == "production") {
location.href = 'pages/error-500/'
} else {
console.log(error.stack)
}
})
this.loaded = true;
},
async upsertData() {
console.log('up')
await
this.$http.post('/api/rte/' + this.editorName + '/upsert', {
data: this.editorData,
})
.then((response) => {
this.$vs.notify({
title: 'Action Completed',
text: response.data.message,
color: 'success',
position: 'top-right'})
})
.catch((error) => {
if (window.environment == "production") {
location.href = 'pages/error-500/'
} else {
console.log(error)
}
})
},
},
created() {
this.pollData();
},
}
</script>
This works, but the in-line styling isn't respected with v-html (sizing and centering). If this.$store.state.EditMode: false, I get the following output:
If this.$store.state.EditMode: true I get this in the in-line editor (as expected).
Raw HTML (editorData property after pollData() is called)
<figure class="image image_resized" style="width:25.51%;"><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTRJO0xRohucbxcjlRoiRaw2cWYTbilYch5NQ&usqp=CAU" alt="Free clipart megaphone announcement public domain vectors - Clipartix"></figure><h2 style="text-align:center;"><span style="color:hsl(30,75%,60%);"><strong>We have a new Intranet!</strong></span></h2><p style="text-align:center;">Summer / Fall Wellness Challenge Link</p>
Research showed that Vue's v-html doesn't respect scoped styling. I'm not entirely sure how that applies to in-line styling. To test output, I replaced my else with the raw HTML and got the same visual output as when I used v-html:
<vx-card :title="editorName" v-else>
<figure class="image image_resized" style="width:25.51%;"><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTRJO0xRohucbxcjlRoiRaw2cWYTbilYch5NQ&usqp=CAU" alt="Free clipart megaphone announcement public domain vectors - Clipartix"></figure><h2 style="text-align:center;"><span style="color:hsl(30,75%,60%);"><strong>We have a new Intranet!</strong></span></h2><p style="text-align:center;">Summer / Fall Wellness Challenge Link</p>
</vx-card>
What is the proper way to disable the inline editor and maintain visual consistency?
<template>
<vx-card :title="editorName" v-if="loaded">
<ckeditor :editor="editor" v-model="editorData" :config="editorConfig" :readonly="editorDisabled" :disabled="editorDisabled" ></ckeditor>
</vx-card>
</template>
//...
watch:{
'$store.state.EditMode'(value, oldValue) {
if(value) {
this.editorDisabled = false;
} else {
this.editorDisabled = true;
}
},
},
//...
Question answered here:
https://github.com/ckeditor/ckeditor5-vue/issues/154
Related
So what i'm trying to do is pretty simple but for some reason is not working please let me show you.
This is my navigation bar written in vue js and here i'm holding almost all my vue components.
Navigation.vue
<template>
<single-video :user_id="user_id" :video="this.video" :videoUser="this.videoUser"
:videoOptions="videoOptions"></single-video>
</template>
<script>
export default {
name: "Navigation",
data: function () {
return {
user_id: user_id,
// isLogin: isLogin,
video: {},
videoUser: {},
videoOptions: {
autoplay: true,
controls: true,
sources: [
{
src: "",
type: "application/x-mpegURL"
}
]
}
}
},
methods: {
openVideoModal(video){
this.videoOptions.sources[0].src = 'storage/videos/' + video.id + '/' + video.id + '.m3u8';
axios.get('video/' + video.channel_id).then(response => {
this.videoUser = response.data;
}).catch(error => {
if (error.response.status == 422) {
this.errors = error.response.data.errors;
}
console.log('Error');
});
this.video = video;
$('#singleVideo').modal('show');
},
},
}
</script>
In one of my child component i have this method which calling openVideoModal() function in navigation. With the video id. So far it's working.
<a #click="$parent.openVideoModal(video)">
This one is my single video component which display the video itself.And is in modal ("singleVideo" modal)
single-video.vue
<video v-if="this.video"
ref="videoPlayer"
id="my-video"
class="video-js vjs-default-skin vjs-big-play-centered"
controls
preload="auto"
width="749"
height="421"
:poster="this.video.thumbnail"
data-setup="{}">
</video>
<script>
import videojs from 'video.js';
export default {
props: ['user_id', 'video', 'videoUser', 'videoOptions'],
data: function () {
return {
errors: {},
singleVideo: {},
player: null
}
},
mounted() {
if(this.videoOptions.sources[0].src) {
this.player = videojs(this.$refs.videoPlayer, this.videoOptions, function onPlayerReady() {
})
}
},
methods: {
beforeDestroy() {
if (this.player) {
this.player.dispose()
}
}
}
</script>
Please don't delete my question it is very important to me to solve this. I'm going nuts over it. :(
I'm trying to use React-slick with gatsby-plugin images and I have the page setup like this.
import React from "react";
import { graphql } from "gatsby"
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { GatsbyImage } from "gatsby-plugin-image"
const settings = {
autoPlay: true,
arrows: false,
dots: true,
infinite: true,
speed: 500,
slidesToShow: 1,
slidesToScroll: 1,
};
const ImgSlide = ({ data }) => {
return (
<div>
<Slider {...settings}>
<div>
<GatsbyImage fluid={data.image1.childImageSharp.fluid} />
</div>
<div>
<GatsbyImage fluid={data.image2.childImageSharp.fluid} />
</div>
</Slider>
</div>
);
};
export const pageQuery = graphql`
query {
image1: file(relativePath: { eq: "images/icon.png" }) {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
image2: file(relativePath: { eq: "images/icon.png" }) {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
`
export default ImgSlide;
When i run Gatsby develop I get an error saying image1 is not defined. I really don't know what I'm missing here. I think it has something to do with how I'm trying to define image1 but I'm pretty sure I've used relativePath properly unless I'm not specifying the location properly.
I do have the same image specified twice that is just because I have not imported the photos in just yet I'm just testing to make it work.
gatsby-config setup is
module.exports = {
siteMetadata: {
title: "Inkd Era",
description: "Clothing and brand built for tattoo and tattoed culture",
},
plugins: [
"gatsby-plugin-sass",
"gatsby-plugin-image",
"gatsby-plugin-react-helmet",
"gatsby-plugin-sitemap",
{
resolve: "gatsby-plugin-manifest",
options: {
icon: "src/images/icon.png",
},
},
"gatsby-transformer-remark",
"gatsby-plugin-sharp",
"gatsby-transformer-sharp",
{
resolve: "gatsby-transformer-remark",
options: {
plugins: [
{
resolve: "gatsby-remark-images",
options: {
maxWidth: 650,
},
},
],
},
},
{
resolve: "gatsby-source-filesystem",
options: {
name: "images",
path: `${__dirname}/src/images/`,
},
__key: "images",
},
{
resolve: "gatsby-source-filesystem",
options: {
name: "pages",
path: `${__dirname}/src/pages/`,
},
__key: "pages",
},
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Inkd Era`,
short_name: `Inkd era`,
start_url: `/`,
background_color: `#000`,
theme_color: `#fafafa`,
display: `standalone`,
icon: `content/assets/gatsby-icon.png`,
},
},
],
};
The structure for the new <GatsbyImage> component when passing the image itself is using the image prop, not fluid. In addition, the query needs to fetch gatsbyImageData, not fluid as you can see in the docs:
import { graphql } from "gatsby"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
function BlogPost({ data }) {
const image = getImage(data.blogPost.avatar)
return (
<section>
<h2>{data.blogPost.title}</h2>
<GatsbyImage image={image} alt={data.blogPost.author} />
<p>{data.blogPost.body}</p>
</section>
)
}
export const pageQuery = graphql`
query {
blogPost(id: { eq: $Id }) {
title
body
author
avatar {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
`
In your scenario, you are mixing the gatsby-image approach, from Gatsby v2 with the new gatsby-plugin-image, which stills in beta, but it's from the v3.
If you want to use the <GatsbyImage>, adapt the query and the component to the needs, otherwise, use the gatsby-image properly like:
import Img from `gatsby-image`
<Img fluid={data.image1.childImageSharp.fluid} />
I'm using #nuxt/auth module with Laravel Passport I'm facing a problem when a I attempt to login it logged me in successfully but when I refresh the page it redirect me back to login page I checked the cookies and it's set and has the token but i don't why this is happening?
Login.vue
<template>
<v-parallax
dark
:src="require(`../assets/images/auth-bg.jpg`)"
height="100%"
>
<v-app id="app-container" dark>
<v-container fill-height fluid>
<v-card
class="mx-auto"
justify="center"
width="512"
max-width="512"
elevation="2"
outlined
>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-card-title>Login</v-card-title>
<v-card-text>
<v-text-field
v-model="form.username"
:counter="32"
type="text"
label="Username"
name="usermame"
required
/>
<v-text-field
v-model="form.password"
type="password"
label="Password"
name="password"
required
/>
</v-card-text>
<v-alert
v-if="error"
border="left"
close-text="Close Alert"
color="#C51162"
dark
dismissible
>
{{ error }}
</v-alert>
<v-card-actions>
<v-btn
block
outlined
shaped
#click="loginPassport"
>
Login
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-container>
</v-app>
</v-parallax>
</template>
<script>
export default {
layout: 'auth',
auth: 'guest',
data: () => ({
valid: false,
loader: null,
loading: false,
usernameRules: [
v => !!v || 'Username is required',
v => v.length <= 32 || 'Name must be less than 32 characters'
],
passwordRules: [
v => !!v || 'Password is required',
v => v.length <= 32 || 'Password must be less than 32 characters'
],
form: {
username: '',
password: ''
},
error: null
}),
watch: {
loader () {
const status = this.loader
this[status] = !this[status]
setTimeout(() => (this[status] = false), 500)
this.loader = null
}
},
methods: {
async loginPassport () {
this.error = null
await this.$auth
.loginWith('DersTVCentral', { data: this.form })
.then(() => this.$router.push('/'))
.catch(() => (this.error = 'Incorrect email or password.'))
}
}
}
</script>
<style>
#app-container {
background-color: transparent;
}
</style>
Nuxt.config.js
import colors from 'vuetify/es5/util/colors'
export default {
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
titleTemplate: '%s - DersTV-Cloud-UI',
title: 'DersTV-Cloud-UI',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// runtime config
publicRuntimeConfig: {
apiURL: process.env.API_URL
},
privateRuntimeConfig: {
apiId: process.env.PASSPORT_CLIENT_ID,
apiSecret: process.env.PASSPORT_CLIENT_SECRET
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
// https://go.nuxtjs.dev/eslint
'#nuxtjs/eslint-module',
// https://go.nuxtjs.dev/vuetify
'#nuxtjs/vuetify'
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'#nuxtjs/axios',
'#nuxtjs/auth-next',
'#nuxtjs/pwa',
'#nuxtjs/dotenv'
],
axios: {
proxy: true,
baseURL: process.env.API_URL
},
proxy: {
'/backend': {
target: process.env.API_URL,
pathRewrite: { '^/backend': '/' }
}
},
router: {
middleware: ['auth']
},
// PWA module configuration: https://go.nuxtjs.dev/pwa
pwa: {
manifest: {
lang: 'en'
}
},
// Vuetify module configuration: https://go.nuxtjs.dev/config-vuetify
vuetify: {
customVariables: ['~/assets/variables.scss'],
theme: {
dark: true,
themes: {
dark: {
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3
}
}
}
},
auth: {
redirect: {
home: '/profile'
},
cookie: {
options: {
secure: false
}
},
strategies: {
DersTVCentral: {
name: 'DersTVCentral',
provider: 'laravel/passport',
url: '/backend',
endpoints: {
user: 'api/v1/user',
userInfo: 'api/v1/user'
},
responseType: 'token',
clientId: process.env.PASSPORT_CLIENT_ID,
clientSecret: process.env.PASSPORT_CLIENT_SECRET,
grantType: 'password'
}
}
},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}
Screenshot of successfull login:
I use dropdown v-autocomplete in Vuetify and i want to query relationship from laravel. When select in input field in v-autocomplete, its work well, its show name that i want to query, but when i selected on it, it show to default value.
Here my Vuetify
<template>
<v-autocomplete
:items="purchases"
dense
solo
v-model="model"
item-text="name"
item-value="name"
return-object
clearable
#input="addTocart"
>
<template v-slot:item="{ item }">
{{ item.product.name }}
</template>
</v-autocomplete>
</template>
In my script
<script>
export default {
name: 'Add Purchase',
created() {
this.fetchPurchase()
},
data() {
return {
products: [],
purchases: [],
model: [],
items: [],
purchase_status: ['Received', 'Partial', 'Pending', 'Ordered'],
discount: 0.00,
tax: 0.00,
shipping_cost: 0.00,
}
},
computed: {
total() {
return this.products.reduce((total, item) => {
return total + item.unit;
}, 0);
}
},
methods: {
fetchPurchase() {
this.$axios.$get(`api/purchase`)
.then(res => {
this.purchases = res.data;
console.log(res);
})
.catch(err => {
console.log(err)
})
},
addTocart (item) {
if(this.items.includes(item)) {
alert("already there");
}else {
this.items.push(item);
}
item.unit = 1;
},
removeItem(index) {
this.items.splice(index, 1)
},
}
}
</script>
Is there a way that i can archieve this? Thanks in advance..
I tried to add Quill Emojis to editor but I am getting console error as
Uncaught ReferenceError: Quill is not defined
I am using Laravel 5.6 and vue js and definately new to vue and its components so I may sound silly to you but for the past 3 days I am searching on the google for the solution and even contacted author of vue2editor on github here is the link
This is what I have tried so far:
vue2editor.vue
<template>
<div id="app">
<vue-editor v-model="content"></vue-editor>
</div>
</template>
<script>
import { VueEditor, Quill } from 'vue2-editor';
import Emoji from 'quill-emoji/dist/quill-emoji';
Quill.register('modules/quill-emoji', Emoji);
export default {
name: 'vue2editor',
components: { VueEditor },
data() {
return {
content: "<h1>Some initial content</h1>",
editorSettings: {
modules: {
toolbar: {
container: [
[{'size': ['small', false, 'large']}],
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }],
[{ 'font': [] }],
[{ 'align': [] }],
['clean'],
['link', 'image', 'video'],
['emoji'],
],
handlers: {
'emoji': function () {}
},
},
toolbar_emoji: true,
short_name_emoji: true,
textarea_emoji:true,
},
},
text: null,
};
},
};
</script>
I even tried the method mentioned by one of the user on github for Quill-Emoji, here is the link.
I came here with lots of hopes; if anyone here is to help me out, at least tell me what I am missing will be more than a help for me.
Quill.register({
'formats/emoji': Emoji.EmojiBlot,
'modules/short_name_emoji': Emoji.ShortNameEmoji,
'modules/toolbar_emoji': Emoji.ToolbarEmoji,
'modules/textarea_emoji': Emoji.TextAreaEmoji}, true);
you need register the model, add the up code to you code.
Edit:
//1) Add plugin to laravel mix
const mix = require('laravel-mix')
mix.webpackConfig(webpack => {
return {
plugins: [
new webpack.ProvidePlugin({
"window.Quill": "quill/dist/quill.js",
Quill: "quill/dist/quill.js"
})
]
};
});
//2 example vue file
<template>
<div class="mt-1">
<vue-editor
ref="editor"
v-model="content"
:editor-toolbar="customToolbar"
:editorOptions="editorSettings"
/>
</div>
</template>
<script>
import { VueEditor, Quill } from "vue2-editor";
import Emoji from "quill-emoji/dist/quill-emoji";
Quill.register("modules/emoji", Emoji);
export default {
components: {
VueEditor,
},
props: {
bubble: Object,
contentCol: {
type: String,
},
},
data() {
return {
edit: false,
content: "<b>Content is here</b>",
customToolbar: [["bold", "italic", "underline"], ["link"], ["emoji"]],
editorSettings: {
modules: {
"emoji-toolbar": true,
"emoji-textarea": true,
"emoji-shortname": true,
},
},
};
},
beforeDestroy() {},
};
</script>
<style src="quill-emoji/dist/quill-emoji.css"/>