How to render/show Quill raw HTML in vuejs - javascript

I have quill editor in vuejs app. I save raw HTML generated by Quill directly to Database (i.e. no cleaning at all). When I fetch it from the backend, text and all styling are shown correctly (i.e. if the text was saved with color it is rendered with the same color, bold is bold etc). But text alignment is not shown correctly.
I am using v-html to show content but text alignment is not rendered.
Basically, I want to have text alignment. This is achieved if I inject (I don't want the user to modify the text) the text into quill editor, but I want to show raw content to normal HTML (i.e. vue component). On digging deep I found, it uses classes as ql-align-right, ql-align-center. So, I tried to explicitly add styling for these classes in styles still no results.
Code snippet
my homeComponent.vue
<template>
<div class="cell medium-8 large-6 columns">
<div class="blog-post" v-for="feed in feedArray" :key="feed.id">
<h3>
<router-link
:to="`article/${feed.post_uniqueidentity}`"
class="heading-link"
>
{{ feed.post_head }}
</router-link>
</h3>
<small>{{
moment(feed.post_timestamp).format("dddd, MMMM Do YYYY")
}}</small>
<div class="postBody-simple" v-html="feed.post_body"></div>
</div>
<div
v-if="feedArray.length"
v-observe-visibility="HandledScrollEvent"
></div>
</div>
</template>
<script>
// ignore from now on...
import moment from "moment";
import { mapGetters, mapActions } from "vuex";
import "../assets/css/bootstrap-icons-1.5.0/bootstrap-icons.css";
import { ObserveVisibility } from "vue-observe-visibility";
export default {
name: "feeds",
metaInfo: {
title: "Home",
},
directives: {
ObserveVisibility,
},
data() {
return {
next: null,
previous: 1,
feedArray: [],
};
},
computed: { ...mapGetters(["feedStore"]) },
methods: {
...mapActions([
"fetchFeed",
"fetchFeedPagination",
]),
async contentLoader(pageNumber) {
var localArrayData = await this.fetchFeedPagination(pageNumber);
this.feedArray.push(...localArrayData.results);
this.next = localArrayData.next;
},
HandledScrollEvent(isVisible) {
if (!isVisible) {
return;
}
if (this.next) {
this.contentLoader(this.next);
}
},
},
async created() {
this.moment = moment;
let rawResponse = await this.fetchFeed();
let isNextPageAvailable = rawResponse.next;
let isPreviousPageAvailable = rawResponse.previous;
if (isNextPageAvailable) {
this.next = isNextPageAvailable;
}
this.previous = isPreviousPageAvailable;
this.feedArray.push(...rawResponse.results);
},
};
</script>
Frontend
Vue: 2.6.11,
vue-observe-visibility: 1.0.0,
vue-quill-editor: 3.0.6,
vue-router: 3.2.0,
vuex: 3.4.0
Backend
Django: 3.0.5
django-cors-headers: 3.7.0
djangorestframework: 3.12.4
psycopg2: 2.9.1
Database: PostgreSQL
This is also tested without any styling, still not getting expected results.

As shown in the README on Github - you should import the proper CSS stylesheets:
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'
export default {
components: {
quillEditor
}
}
These stylesheets are required anywhere you need your HTML content to be rendered - because Quill by default prefers CSS classes instead of inline styles. You can customize this behavior by using attributors as explained in the manual
var ColorClass = Quill.import('attributors/class/color');
var SizeStyle = Quill.import('attributors/style/size');
Quill.register(ColorClass, true);
Quill.register(SizeStyle, true);
// Initialize as you would normally
var quill = new Quill('#editor', {
modules: {
toolbar: true
},
theme: 'snow'
});

Related

Vue3 with Vite only accepts kebab case tags while Vue3 cli accepts Pascal case tags for custom components

I have a project using Vue3 with Vite (on Laravel) which have a Wiki.vue page which loads a "MyContent.vue" component.
//On MyContent.vue:
<template>
<div>content component</div>
</template>
<script>
export default {
name: "MyContent",
};
</script>
//On Wiki.vue:
<template>
<MyContent />
</template>
<script>
import MyContent from "./wiki/components/MyContent.vue";
export default {
components: { MyContent },
};
</script>
//On vite.config.js
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import vue from "#vitejs/plugin-vue";
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => ["MyContent"].includes(tag),
},
},
}),
laravel(["resources/css/app.css", "resources/js/app.js"]),
],
});
On Wiki.vue If I dont change the tag from MyContent to my-content the component won't load at all.
I tried to start a new Vue3 Cli project and I notice that the HelloWorld tag is able to remain Pascal case and load properly which I really wonder what makes the difference.
Thanks in advance!
You've configured a compiler option to treat any elements whose name matches "MyContent" as custom elements, which prevents them from being parsed as Vue components:
// vite.config.js
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: { 👇
isCustomElement: (tag) => ["MyContent"].includes(tag),
},
},
}),
],
});
Wiki.vue's template tries to use a Vue component named "MyContent", which is ignored per the config above. HelloWorld.vue is unaffected by this because the config above only looks for "MyContent".
Solution
You should either remove the isCustomElement config, or rename MyContent.vue. It doesn't sound like you're actually using custom elements, so I think the former is the best solution.

NuxtJS passing props

I have an issue so I have in NuxtJS (vuejs) with a default layout like that
<template>
<div id="app">
<Loader #animation:complete = 'loader'/>
<Header/>
<nuxt :title = 'title'/>
<Footer/>
<BgDecor/>
</div>
</template>
<script>
import { gsap } from "gsap/dist/gsap";
import Loader from "#/layouts/parts/Loader";
import Header from "#/layouts/parts/Header";
import Footer from "#/layouts/parts/Footer";
import BgDecor from "#/layouts/parts/BgDecor";
export default {
data() {
return {
loaderDone: null,
};
},
components: {
Loader,
Header,
Footer,
BgDecor,
},
head() {
return {
titleTemplate: "%s - Product Designer UI/UX Designer Strategist",
meta: [
{
hid: "description",
name: "description",
content:
"Artistic Director, Web / Motion Designer, FrontEnd Developer for over 10 years, specializing in visual communication and web design",
},
{
hid: "keywords",
name: "keywords",
content:
"Product Design, UI, UX, Designer, UI Designer, UX Designer, FrontEnd Developer",
},
],
};
},
methods: {
loader(e) {
console.log("yesssssss");
this.loaderDone = "yup";
},
},
computed: {
title() {
return this.loaderDone;
},
},
};
</script>
<style>
</style>
my issue is I have yup in Nuxt but not inside the page inside the component nuxt
yes I know it's complicated but I would like to when my loader is done get an $emit to my index page or other pages to know my loader is done
If I put the loader inside the index directly every time I come back to the page index I get again the loader I would like just to have it when I start my website that all
so in the dev tools I have the yup inside the nuxt parent of the index but if I go again with props it seems a lot of work for just getting info my loader is done I can start my other animation
If anyone has an idea thanks :)
have a great evening
You can do this with a vuex store.
Your Store could look like this
const store = new Vuex.Store({
state: {
loaderDone: false
},
mutations: {
setLoadingDone (state) {
state.loadingDone = true
}
}
})
In your loader you could map the mutation and in your component you can map the state.
To set the done flag you can do this in your loader to have the method setLoadingDone()
// loader.vue
import { mapMutations } from 'vuex}
export default {
// ...
methods: {
...mapMutations(['setLoadingDone '])
}
}
And your other componens can get this value like this
// other components
import { mapState } from 'vuex}
export default {
// ...
computed: {
...mapState(['loadingDone '])
}
}

VueJS; wait for element before running local JavaScript File

I have a few components, javascript, and elements that needs to be ran in a certain order.
1st - opensheetmusicdisplay.min.js which I have in my index.html file. This isn't an issue.
2nd - <div id="xml">
3rd - xml-loader.js which depends on both the "xml" div and opensheetmusicdisplay.min,js
This is the index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<script rel="preload" src="<%= BASE_URL %>js/osmd/opensheetmusicdisplay.min.js"></script>
</head>
<body>
<div id="xml2">words go here</div>
<div id="app"></div>
</body>
</html>
And this is the JavaScript part I'm attempting to test:
window.onload = function() {
alert("xx == ", document.getElementById("xml2"));
}
alert("xx2 == ", document.getElementById("xml2"));
alert(JSON.stringify(opensheetmusicdisplay, null, 1));
When I run this, they both instances of "xml2" show blanks. The opensheetmusicdisplay does show data, which means it is reading from the source in the head section in index.html
It was pointed out to me in the comments that alert only take one argument. That's a mistake that I'm going to let sit for the moment. The error in the console is TypeError: document.getElementById(...) is null.
Now, this is the main.js. There are a lot of comments because of my various ideas:
// vue imports and config
import Vue from 'vue'
import App from '#/App'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue.config.productionTip = false
// page imports
import Notation from '#/components/Notation'
import HomePage from '#/components/HomePage'
// component imports and registration
import { FoundationCSS } from '#/../node_modules/foundation-sites/dist/css/foundation.min.css'
Vue.component('foundation-css', FoundationCSS)
import SideNav from '#/components/SideNav'
Vue.component('side-nav', SideNav);
// import * as Osmd from '#/../public/js/osmd/opensheetmusicdisplay.min.js'
// Vue.component('osmd-js', Osmd)
// import { OsmdJs } from '#/components/Osmd'
import * as XmlJs from '#/../public/js/osmd/xml-loader.js'
Vue.component('xml-js', XmlJs)
// import XLoad from '#/components/XmlLoader'
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/',
components: {
maininfo: HomePage
}
},
{ path: '/chromatic-scales/c-chromatic-scale',
components: {
maininfo: Notation// ,
// xmlloader: XLoad
}
}
]
})
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
I registered XmlJs as global because this is the only way out of 100 things that actually works. I then embed it in Notation.vue like so:
<template>
<div>
<div id="xml">
{{ notation.data }}
</div>
<xml-js />
</div>
</template>
<script>
import axios from 'axios'
export default ({
data () {
return {
notation: null,
}
},
mounted () {
axios
.get('http://localhost:3000/chromatic-scales/c-chromatic-scale')
.then(result => (this.notation = result))
}})
</script>
<style scoped></style>
The last file is the meat and potatoes of what I'm trying to do. The xml-loader.js slurps the data from <div id="xml"> and does whatever magic the program does in order to render the output I want. The issue is that there doesn't seem to be anyway to wait for the stuff in {{ notation.data }}.
I am new to using vuejs and front-end javascript frameworks in general. I do recognize the code is probably not optimal at this time.
There is race condition where DOM element is not available at the time when it's accessed. The solution is to not access DOM elements created by Vue outside of it. DOM element is ready for use only after asynchronous request:
<template>
<div>
<div ref="xml" id="xml">
{{ notation.data }}
</div>
<xml-js />
</div>
</template>
<script>
import axios from 'axios'
export default ({
data () {
return {
notation: null,
}
},
async mounted () {
const result = await axios
.get('http://localhost:3000/chromatic-scales/c-chromatic-scale')
this.notation = result;
this.$nextTick(); // wait for re-render
renderXml(this.$ref.xml); // pass DOM element to third-party renderer
}})
You can import xml-loader.js into the Notation.vue as a function. Then you can simply do something like this:
mounted () {
axios.get(PATH).then(result => {
this.notation = result
let xmlResult = loadXML(result)
doSomethingWithResult(xmlResult)
}
},
methods: {
doSomethingWithResult (result) {
// do something
}
}

Image Overlay plugin for FilePond Vue

I want a review button on image, but I don't find attribute.
I set the imagePreviewMarkupShow = true but it didn't work.
Package here
My Template
<template>
<div id="app">
<file-pond
name="test"
ref="pond"
max-files="4"
label-idle="Drop files here..."
:allow-multiple="true"
accepted-file-types="image/jpeg, image/png"
:files="myFiles"
v-on:init="handleFilePondInit"
allowImagePreview ="false"
/>
</div>
</template>
My Script
import vueFilePond from 'vue-filepond';
import 'filepond/dist/filepond.min.css';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css';
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
import FilePondPluginImageOverlay from 'filepond-plugin-image-overlay';
// Create component
const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview,FilePondPluginImageOverlay);
export default {
name: 'app',
data: function() {
return { myFiles: [] };
},
methods: {
handleFilePondInit: function() {
console.log('FilePond has initialized');
// this.$refs.pond.getFiles();
// FilePond instance methods are available on `this.$refs.pond`
}
},
components: {
FilePond
}
};
How do I add that button?
I was also struggling with this problem.
The solution is to import CSS.
import 'filepond-plugin-image-overlay/dist/filepond-plugin-image-overlay.min.css'
This is not mentioned in Github.

Injecting highlight.js to work with vue2-editor (Quill)

I have a trouble connecting vue2-editor (based on quill) with highlight.js
No matter what I do, I get an error saying:
Syntax module requires highlight.js. Please include the library on the page before Quill.
I'm using nuxt if it changes anything.
I've tried adding this line at the beginning of script tag:
import hljs from'highlightjs';
So it looks like:
<script>
import hljs from'highlightjs';
export default {
middleware: 'hasPermissions',
permissions: [ 'createPosts' ],
...
}
</script>
My plugin where I require vue2-editor:
import Vue from'vue';
import VueEditor from'vue2-editor';
Vue.use(VueEditor);
VueEditor component in my page:
<VueEditor
class="my-4"
v-model="content"
:editor-options="{ modules: { syntax: true } }"
placeholder="Post content" />
EDIT:
I've tried creating my own component and it shows the same error:
<template>
<v-layout
row
wrap>
<v-flex xs12>
<div ref="editor" />
</v-flex>
</v-layout>
</template>
<script>
import Quill from'quill';
export default {
data() {
return {
editor: null
};
},
mounted() {
window.hljs = require('highlight.js');
this.editor = new Quill(this.$refs.editor, {
modules: {
toolbar: [
[{ header: [ 1, 2, 3, 4, false ]}],
[ 'bold', 'italic', 'underline' ]
],
syntax: true
},
theme: 'snow',
formats: [ 'bold', 'underline', 'header', 'italic' ]
});
this.editor.root.innerHTML = this.value;
}
};
</script>
I can successfully print hljs in console in development tools in my browser. What's wrong?
This one should be a better solution, works for me.
https://github.com/surmon-china/vue-quill-editor/issues/39
In fact, this is because quill internal self-closure caused by the
problem, the solution is as follows: modules.syntax from true to
replace a function:
import hljs from 'highlight.js'
import 'highlight.js/styles/monokai-sublime.css'
editorOption: {
modules: {
syntax: {
highlight: text => hljs.highlightAuto(text).value
}
}
}
I struggled with this for a long time too and this answer worked for me!
// highlight.js component
import Vue from 'vue'
import Hljs from 'highlight.js'
import 'highlight.js/styles/googlecode.css'
let Highlight = {}
Highlight.install = function (Vue, options) {
Vue.directive('highlight', function (el) {
let blocks = el.querySelectorAll('pre code');
blocks.forEach((block) => {
Hljs.highlightBlock(block)
})
})
}
export default Highlight
// in main.js
import Highlight from 'path/to/Highlight.js'
Vue.use(Highlight)
I changed import 'highlight.js/styles/googlecode.css'
to import 'highlight.js/styles/monokai-sublime.css' seems to be a more popular and pleasing style.
you could also probably add a
hljs.configure({ // optionally configure hljs
languages: ['javascript', 'ruby', 'python']
});
to select certain languages, but I haven't tried.
Although I still haven't figured out how to change the background color. it shows up white in other places and black background in the quill window.

Categories