I want to edit my templates via Quill. I'm using bubble theme. So here is my component;
<template>
<QuillEditor #input="onInputChange" theme="bubble" toolbar="essential" id="quill-Editor" #ready="ready"></QuillEditor>
</template>
<script>
import { QuillEditor } from '#vueup/vue-quill'
import '#vueup/vue-quill/dist/vue-quill.bubble.css';
export default {
props: ['value'],
components: {
QuillEditor
},
created(){
console.log();
setTimeout(() => {
document.querySelector('#quill-Editor .ql-editor').innerHTML = this.value
}, 100);
},
methods: {
onInputChange() {
this.$emit('input', document.querySelector('#quill-Editor .ql-editor').innerHTML);
},
ready(editor){
editor.getModule('toolbar').addHandler('image', () => {
const tooltip = editor.theme.tooltip;
const originalSave = tooltip.save;
const originalHide = tooltip.hide;
tooltip.save = function () {
const range = editor.getSelection(true);
const value = this.textbox.value;
if (value) {
editor.insertEmbed(range.index, 'image', value, 'user');
}
};
tooltip.hide = function () {
tooltip.save = originalSave;
tooltip.hide = originalHide;
tooltip.hide();
};
tooltip.edit('image');
tooltip.textbox.placeholder = 'Embed URL';
});
},
}
}
</script>
Value is this html value. But when I do that quill is deleting all tags giving just few blank <p> tag. What should I do? How can I do that?
Related
what i want is when i click on textarea i want row value to be 10,
If you click outside the I want line 6
how can i do this i am newbie in vue js 3.
I couldn't figure out how to do this.
I couldn't figure out how to do about directives.
<template>
<div>
<textarea :rows="row"></textarea>
</div>
</template>
<script>
export default {
data() {
return {
row: 6
}
}
}
</script>
Try like following snippet:
const clickOutside = {
beforeMount: (el, binding) => {
el.clickOutsideEvent = event => {
if (!(el == event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted: el => {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
const app = Vue.createApp({
data() {
return {
row: 6,
};
},
methods: {
setRow() {
this.row = 10
},
removeRow() {
this.row = 6
}
}
})
app.directive("clickOut", clickOutside).mount('#demo')
<script src="https://unpkg.com/vue#3.2.29/dist/vue.global.prod.js"></script>
<div id="demo">
<textarea #click="setRow" :rows="row" v-click-out="removeRow"></textarea>
</div>
I have a big problem with React TimeLines Package(https://openbase.com/js/react-timelines)
I want something like this photo:
( having 3 P tags with different ClassNames)
but in default case of this package I cant do it!
I think I should use something like createElement and textContent in JS. but I dont know how!
My Codes:
import React, { Component } from "react";
import Timeline from "react-timelines";
import "react-timelines/lib/css/style.css";
import { START_YEAR, NUM_OF_YEARS, NUM_OF_TRACKS } from "./constant";
import { buildTimebar, buildTrack } from "./builder";
import { fill } from "./utils";
const now = new Date("2021-01-01");
const timebar = buildTimebar();
// eslint-disable-next-line no-alert
const clickElement = (element) =>
alert(`Clicked element\n${JSON.stringify(element, null, 2)}`);
class App extends Component {
constructor(props) {
super(props);
const tracksById = fill(NUM_OF_TRACKS).reduce((acc, i) => {
const track = buildTrack(i + 1);
acc[track.id] = track;
return acc;
}, {});
this.state = {
open: false,
zoom: 2,
// eslint-disable-next-line react/no-unused-state
tracksById,
tracks: Object.values(tracksById),
};
}
handleToggleOpen = () => {
this.setState(({ open }) => ({ open: !open }));
};
handleToggleTrackOpen = (track) => {
this.setState((state) => {
const tracksById = {
...state.tracksById,
[track.id]: {
...track,
isOpen: !track.isOpen,
},
};
return {
tracksById,
tracks: Object.values(tracksById),
};
});
};
render() {
const { open, zoom, tracks } = this.state;
const start = new Date(`${START_YEAR}`);
const end = new Date(`${START_YEAR + NUM_OF_YEARS}`);
return (
<div className="app">
<Timeline
scale={{
start,
end,
zoom,
}}
isOpen={open}
toggleOpen={this.handleToggleOpen}
clickElement={clickElement}
timebar={timebar}
tracks={tracks}
now={now}
enableSticky
scrollToNow
/>
</div>
);
}
}
export default App;
builder.js:
export const buildElement = ({ trackId, start, end, i }) => {
const bgColor = nextColor();
const color = colourIsLight(...hexToRgb(bgColor)) ? "#000000" : "#ffffff";
return {
id: `t-${trackId}-el-${i}`,
title: "Bye Title: Hello Type: String",
start,
end,
style: {
backgroundColor: `#${bgColor}`,
color,
borderRadius: "12px",
width: "auto",
height: "120px",
textTransform: "capitalize",
},
};
};
I'm new React developer(mainly with hooks but did not find good example with hooks), here i have antd table with search functionality, my question is when user writes something in search then user gets different result, how to cancel that search by clicking 'Reset' button ?
my code:
https://codesandbox.io/s/antd-table-filter-search-forked-mqhcn?file=/src/EventsSection/EventsSection.js
You can add an id to your input into TitleSearch.js:
<Search
id='IDYOUWANT'
placeholder="Enter Title"
onSearch={onSearch}
onChange={onChange}
style={{ width: 200 }}
/>
And add event into EventsSection.js
ResetInput = () => {
const input = document.getElementById('IDYOUWANT');
input.value = '';
this.handleSearch('');
}
....
<button
onClick={this.ResetInput}
>Reset</button>
Change IDYOUWANT with your id
run this code
Created a new function for reset value and trigger it from reset button.
function:
resetValue = () =>{
this.setState({
eventsData: eventsData
});
}
And trigger from button
<button onClick={this.resetValue}>Reset</button>
all code::
import React, { Component } from "react";
import styles from "./style.module.css";
import { EventsTable } from "../EventsTable";
import { StatusFilter } from "../StatusFilter";
import { TitleSearch } from "../TitleSearch";
const eventsData = [
{
key: 1,
title: "Bulletproof EP1",
fileType: "Atmos",
process: "match media",
performedBy: "Denise Etridge",
operationNote: "-",
updatedAt: "26/09/2018 17:21",
status: "complete"
},
{
key: 2,
title: "Dexter EP2",
fileType: "Video",
process: "Compliance",
performedBy: "Dane Gill",
operationNote: "passed",
updatedAt: "21/09/2018 12:21",
status: "inProgress"
}
];
class EventsSection extends Component {
constructor(props) {
super(props);
this.state = {
eventsData
};
}
handleFilter = (key) => {
const selected = parseInt(key);
if (selected === 3) {
return this.setState({
eventsData
});
}
const statusMap = {
1: "complete",
2: "inProgress"
};
const selectedStatus = statusMap[selected];
const filteredEvents = eventsData.filter(
({ status }) => status === selectedStatus
);
this.setState({
eventsData: filteredEvents
});
};
handleSearch = (searchText) => {
const filteredEvents = eventsData.filter(({ title }) => {
title = title.toLowerCase();
return title.includes(searchText);
});
this.setState({
eventsData: filteredEvents
});
};
handleChange = (e) => {
const searchText = e.target.value;
const filteredEvents = eventsData.filter(({ title }) => {
title = title.toLowerCase();
return title.includes(searchText);
});
this.setState({
eventsData: filteredEvents
});
};
resetValue = () =>{
this.setState({
eventsData: eventsData
});
}
render() {
return (
<section className={styles.container}>
<header className={styles.header}>
<h1 className={styles.title}>Events</h1>
<button onClick={this.resetValue}>Reset</button>
<TitleSearch
onSearch={this.handleSearch}
onChange={this.handleChange}
className={styles.action}
/>
</header>
<EventsTable eventsData={this.state.eventsData} />
</section>
);
}
}
export { EventsSection };
Here is what i did in order to solve it:
i added onClick on the button
<button onClick={this.resetSearch}>Reset</button>
Then in the function i put handleSearch to '', by doing this it reset the table:
resetSearch = () =>{
this.handleSearch('')
}
I want to use a mixin to find a referenced Node and then append some HTML to it rendered using Vue, so I can pass data into it.
const Tutorial = guide => ({
mounted() {
this.guide = guide;
this.html = Vue.compile(`<p>Test</p>`).render;
guide['add-location'].forEach(step => {
this.$refs[step.ref].appendChild(this.html);
})
},
data: function() {
return {
guide: null,
html: null
}
}
});
export default Tutorial;
This is what I have at the moment, it gets the ref correctly, just can't append the HTML as I don't think i'm using Vue.compile correctly.
Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'
In my opinion, It's better if we can avoid mutate DOM directly. What about replace ref with v-html?
const tutorial = guide => ({
mounted() {
guide['add-location'].forEach(step => {
this[step.ref] += this.html;
})
},
data: function() {
return {
...guide['add-location'].reduce((result, step) => {
result[step.ref] = ''
return result
}, {}),
html: `<p>Test</p>`
}
}
});
const Foo = {
template: `
<div>
<div v-html='foo'></div>
<div v-html='bar'></div>
</div>
`,
mixins: [tutorial({
'add-location': [
{ ref: 'foo' },
{ ref: 'bar' }
]
})]
}
Another idea is using wrapper component to wrap target or if your target is a component then you create a wrapper as mixin too.
Using with html property:
<wrapper ref='foo'>
<div>Foo</div>
</wrapper>
const Wrapper = {
props: ['html'],
render(h) {
return h('div', [this.$slots.default, h('div', {
domProps: {
innerHTML: this.html
}
})])
}
}
...
this.$refs.foo.html = '<h1>Hello Foo</h1>'
Example
Or using with custom appendChild method:
const Wrapper = {
data: () => ({
children: []
}),
methods: {
appendChild(child) {
this.children.push(child)
}
},
render(h) {
return h('div', [
this.$slots.default,
...this.children.map(child => h('div', {
domProps: {
innerHTML: child
}
}))
])
}
}
...
this.$refs.foo.appendChild('<h1>Hello Foo</h1>')
this.$refs.foo.appendChild('<h1>Hello Bar</h1>')
Example
Or using with Vue.compile in case that html is not plain html:
const Wrapper = {
data: () => ({
template: '',
context: {}
}),
methods: {
setChild(template, context) {
this.template = template
this.context = context
}
},
render(h) {
let res = Vue.compile(this.template)
return h('div', [
this.$slots.default,
h({
data: () => this.context,
render: res.render,
staticRenderFns: res.staticRenderFns
})
])
}
}
...
this.$refs.foo.setChild('<h1>Hello {{ name }}</h1>', {
name: 'Foo'
})
Example
How to binding parent's model to child in Vue.js?
These codes below is works fine. if i fill the input manually, then child's model return it's value to the parent's model.
But the issue is, if the data set from AJAX request in a parent, the input doesn't automatically filled.
Can anyone help me on this?
Form.vue
<template>
<form-input v-model="o.name" :fieldModel="o.name" #listenChanges="o.name = $event"/>
<form-input v-model="o.address" :fieldModel="o.address" #listenChanges="o.address = $event"/>
</template>
<script>
import FormInput from '../share/FormInput.vue'
export default {
data () {
return {
o: {
name: '',
address: ''
}
}
},
components: { 'form-input': FormInput },
created: function() {
axios.get('http://api.example.com')
.then(response => {
this.o.name = response.data.name
this.o.address = response.data.address
})
.catch(e => { console.log(e) })
}
}
</script>
FormInput.vue
<template>
<input type="text" v-model='fieldModelValue' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
mounted: function() {
this.fieldModelValue = this.fieldModel;
},
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Things are more straightforward if you take advantage of v-model in components.
If you put v-model on a component, the component should take a prop named value, and should emit input events to trigger it to update.
I like to make a computed to hide the event emitting, and allow me to just v-model the computed inside my component.
new Vue({
el: '#app',
data: {
o: {
name: '',
address: ''
}
},
components: {
'form-input': {
template: '#form-input',
props: ['value'],
computed: {
fieldModelValue: {
get() {
return this.value;
},
set(newValue) {
this.$emit('input', newValue.toUpperCase());
}
}
}
}
},
// Simulate axios call
created: function() {
setTimeout(() => {
this.o.name = 'the name';
this.o.address = 'and address';
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
Name ({{o.name}})
<form-input v-model="o.name"></form-input>
Address ({{o.address}})
<form-input v-model="o.address"></form-input>
</div>
<template id="form-input">
<input type="text" v-model='fieldModelValue'>
</template>
The mounted() hook is blocking subsequent updates from the parent.
Remove mounted and change v-model to 'fieldModel'
<template>
<input type="text" :value='fieldModel' #input="forceUpper($event, fieldModel)">
</template>
<script>
export default {
props: ['fieldModel'],
data() {
return {
fieldModelValue: ''
}
},
// mounted: function() {
// this.fieldModelValue = this.fieldModel;
// },
methods: {
forceUpper(e, m) {
const start = e.target.selectionStart;
e.target.value = e.target.value.toUpperCase();
this.fieldModelValue = e.target.value.toUpperCase();
this.$emit('listenChanges', this.fieldModelValue)
}
}
}
</script>
Demo CodeSandbox