Hi I am trying to append and render the HTML at current cursor. But I am not able to append the HTML. I tried everything. But not able to get solution.
<quill-editor #quillEditor [(ngModel)]="content" [modules]="modules"> </quill-editor>
<button (click)="addChip()">Add Chip</button>
<div id="toolbar">
<!-- Add buttons as you would before -->
<button class="ql-bold">B</button>
<button class="ql-italic">I</button>
<button class="ql-emoji">I</button>
<span class="ql-formats">
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
</div>
ts
#ViewChild('quillEditor', { static: false }) editor: QuillEditorComponent;
constructor() {
this.modules = {
'emoji-shortname': true,
'emoji-toolbar': true,
toolbar: {
container: '#toolbar',
},
};
}
addChip() {
const cursorPosition = this.editor.quillEditor.getSelection().index;
const delta = this.editor.quillEditor.clipboard.convert(
`<p>Your HTML code here</p>`
);
console.log('delte', delta);
this.editor.quillEditor.updateContents(
{ index: cursorPosition, length: 0 },
delta,
'api'
);
}
My quill versions:
"ngx-quill": "^3.4.0",
"quill": "^1.3.7",
"quill-emoji": "^0.2.0",
I tried everything, I don't know what I am doing wrong in this. If anyone knows any solution or you have stackblitz, please suggest.
you should add information about cursor position to delta, not as parameter of updateContents method.
install
npm i quill-delta
change
import Delta from 'quill-delta';
...
addChip() {
const cursorPosition = this.editor.quillEditor.getSelection().index;
const clipboardDelta = this.editor.quillEditor.clipboard.convert(
`<p>Your HTML code here</p>`
);
const delta = new Delta().retain(cursorPosition).concat(clipboardDelta);
console.log('delte', delta);
this.editor.quillEditor.updateContents(
delta,
'user'
);
}
Related
Am very new to vuejs.am trying to replicate the javascript with vue. where a user can toggle button. I have a list of buttons and I would like to toggle the active class but remove the active class from all other buttons.. Is there a better way of writtting the function without the querySelector? Am really stuck..
<template>
<div #click="selectItem" class="menu-tabs">
<button type="btn" class="menu-tab-item active" data-target="#remis-transfer"> Transfer
</button>
<button type="btn" class="menu-tab-item text-muted" data-target="#bank-transfer">
Transfer Money
</button>
<button type="btn" class="menu-tab-item text-muted" data-target="#fueling">
Fueling
</button>
</div>
</template>
<script>
export default {
methods: {
selectItem(e) {
if (e.target.classList.contains("menu-tab-item") && !e.target.classList.contains("active")) {
const target = e.target.getAttribute("data-target")
menuTabs.querySelector(".active").classList.remove("active");
e.target.classList.add("active");
const menuSection = document.querySelector(".menu-section");
menuSection.querySelector(".menu-tab-content.active").classList.remove("active");
menuSection.querySelector(target).classList.add('active');
}
}
}
}
</script>
Look at the code
var Main = {
data() {
return {
active: 0,
buttonList: [
{
text: "Transfer",
target: "#remis-transfer",
},
{
text: "Transfer Money",
target: "#bank-transfer",
},
{
text: "Fueling",
target: "#fueling",
},
],
};
},
methods: {
selectItem(i) {
this.active = i;
},
},
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>
<div id="app">
<div class="menu-tabs">
<button
type="btn"
class="menu-tab-item"
v-for="(item, index) in buttonList"
:class="[{ active: active == index }, { 'text-muted': active != index }]"
:data-target="item.target"
:key="index"
#click="selectItem(index)"
>
{{ item.text }}
</button>
</div>
</div>
You can simply define some states like:
data() {
return {
myActiveClass: 'active',
myMutedClass: 'text-muted'
}
},
and pass this myActiveClass and myMutedClass states to your elements like this:
class=menu-tab-item ${myMutedClass} ${myActiveClass}
with this approach, you can quickly achieve what you want. So when you want an element to not be active, in your function you can make myActiveClass = '', or if you want the text to lose muted class you can say myMutedClass = '' .
Just make sure to play with states in the way you want.
I'm working with BootstrapVue. To my problem: I have a v-for in my template in which I have two buttons.
Looping over my v-for my v-if doesn't generate unique IDs and than after clicking one button each button will be triggered (from Open me! to Close me! and other way around).
How can I manage to get each button only triggers itself and doesn't affect the other?
I think I have to use my n of my v-for but I actually don't know how to bind this to a v-if..
Thanks in advance!
<template>
<div>
<div v-for="n in inputs" :key="n.id">
<b-button v-if="hide" #click="open()">Open me!</b-button>
<b-button v-if="!hide" #click="close()">Close me! </b-button>
</div>
<div>
<b-button #click="addInput">Add Input</b-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
id: null,
inputs: [{
id: 0
}],
hide: true,
};
},
methods: {
open() {
this.hide = false
},
close() {
this.hide = true
},
addInput() {
this.inputs.push({
id: this.id += 1;
})
}
}
};
</script>
Everything seems to look fine. In order to handle each button triggers,
you can maintain an object like so:
<script>
export default {
data() {
return {
inputs: [{id: 0, visible: false}],
};
},
methods: {
open(index) {
this.inputs[index].visible = false
},
close(index) {
this.inputs[index].visible = true
},
addInput() {
this.inputs.push({id: this.inputs.length, visible: false});
}
}
};
</script>
and your template should be like
<template>
<div>
<div v-for="(val, index) in inputs" :key="val.id">
<b-button v-if="val.visible" #click="open(index)">Open me!</b-button>
<b-button v-if="!val.visible" #click="close(index)">Close me! </b-button>
</div>
</div>
</template>
Edit:
You don't need to insert an id every time you create a row, instead can use the key as id. Note that the inputs is an object and not array so that even if you want to delete a row, you can just pass the index and get it removed.
I would create an array of objects. Use a boolean as property to show or hide the clicked item.
var app = new Vue({
el: '#app',
data: {
buttons: []
},
created () {
this.createButtons()
this.addPropertyToButtons()
},
methods: {
createButtons() {
// Let's just create buttons with an id
for (var i = 0; i < 10; i++) {
this.buttons.push({id: i})
}
},
addPropertyToButtons() {
// This method add a new property to buttons AFTER its generated
this.buttons.forEach(button => button.show = true)
},
toggleButton(button) {
if (button.show) {
button.show = false
} else {
button.show = true
}
// We are changing the object after it's been loaded, so we need to update ourselves
app.$forceUpdate();
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template>
<div>
<div v-for="button in buttons" :key="button.id">
<button v-if="button.show" #click="toggleButton(button)">Open me!</button>
<button v-if="!button.show" #click="toggleButton(button)">Close me! </button>
</div>
</div>
</template>
</div>
I am new to Vuejs. I am using Primevue library to build the api using the composition vuejs 3.
my problem is that menu is not updating. I want to hide the show button when the element is shown and vice versa. I search all the internet and tried all the solutions I found but in vain.
Any help is appreciated, thank you
export default {
name: "Quote",
components: {
loader: Loader,
"p-breadcrumb": primevue.breadcrumb,
"p-menu": primevue.menu,
"p-button": primevue.button,
},
setup() {
const {
onMounted,
ref,
watch,
} = Vue;
const data = ref(frontEndData);
const quoteIsEdit = ref(false);
const toggle = (event) => {
menu.value.toggle(event);
};
const quote = ref({
display_item_date: true,
display_tax: true,
display_discount: false,
});
const menu = ref();
const items = ref([
{
label: data.value.common_lang.options,
items: [{
visible: quote.value.display_item_date,
label: data.value.common_lang.hide_item_date,
icon: 'pi pi-eye-slash',
command: (event) => {
quote.value.display_item_date = !quote.value.display_item_date;
}
},
{
visible: !quote.value.display_item_date,
label: data.value.common_lang.unhide_item_date,
icon: 'pi pi-eye',
command: () => {
quote.value.display_item_date = !quote.value.display_item_date;
}
}
]
]);
}
return {
data,
quoteIsEdit,
menu,
items,
toggle
};
},
template:
`
<div class="container-fluid" v-cloak>
<div class="text-right">
<p-menu id="overlay_menu" ref="menu" :model="items" :popup="true"></p-menu>
<p-button icon="pi pi-cog" class="p-button-rounded p-button-primary m-2" #click="toggle" aria-haspopup="true" aria-controls="overlay_menu"></p-button>
<p-button :label="data.common_lang.save + ' ' + data.common_lang.quote" class=" m-2" /></p-button>
</div>
</div>
`
};
The problem is the items subproperty change is not reactive, so the items.value.items[].visible props are not automatically updated when quote.value.display_item_date changes.
One solution is to make items a computed prop, so that it gets re-evaluated upon changes to the inner refs:
// const items = ref([...])
const items = computed(() => [...])
demo
I am trying to update string on change inside a textarea but it does not work.
Vue component:
<template>
<div>
<textarea :value="text" #change="changed = true" #keyup="changed = true"></textarea>
<div class="buttons">
<button #click.prevent="saveChanges">Save Changes</button>
<button #click.prevent="discardChanges">Discard Changes</button>
</div>
</div>
</template>
<script>
export default {
name: "Editor",
props: ["fileText"],
data() {
return {
changed: false,
text: this.fileText,
sendText: "",
};
},
computed: {
getCurrentText() {
return this.text;
}
},
methods: {
emitToParent() {
this.$parent.$emit("custom-event", this.changed);
},
saveChanges() {
if (this.changed) {
console.log("I changed");
this.sendText = this.getCurrentText;
this.changed = false;
this.$emit("close");
console.log(this.sendText);
} else {
this.$emit("close");
}
},
discardChanges() {
this.text = this.fileText;
this.$emit("close");
},
},
};
</script>
I proceed to make a change inside the text area, then click on the Save Changes button, this.changed is set to true, console.log("I changed"); fires up, but console.log(this.sendText); returns the same, unmodified text.
As a whole, I am trying to edit text inside a file. The above component gets fileText from it's parent component and I would like to send back to the parent a text that has been modified (if it has been modified). Basically updating the text inside a file, if the text changes.
This is a raw version and will change accordinly.
DEMO
Instead of :value="text" try to use v-model="text"
Like this:
<textarea v-model="text" #change="changed = true" #keyup="changed = true"></textarea>
I found lots of libraries that somehow marry an external library (and their DOM elements) with Vue.js. All of them though seem to only add child elements to the Vue.js-managed DOM node.
I wrote Vue-Stripe-Elements to make the use of the new Stripe V3 easier but struggled to mount Stripes elements to the Vue component.
The obvious way would be a .vue component like this:
<template>
</template>
<script>
export default {
// could also be `mounted()`
beforeMount () {
const el = Stripe.elements.create('card')
el.mount(this.$el)
}
}
</script>
This would work if Stripe only adds children to the element it is mounted too but it looks like Stripe instead transcludes or replaces the given DOM node. Stripe of course also doesn't support any VNodes.
My current solution to the problem is to create a real DOM node and add it as a child:
<template>
</template>
<script>
export default {
mounted () {
const dom_node = document.createElement('div')
const el = Stripe.elements.create('card')
el.mount(dom_node)
this.$el.appendChild(el)
}
}
</script>
It works but it feels like I'm fighting against Vue.js here and I might create odd side effects here. Or am I just doing what other appending libraries do manually and it is the best way to go?
Is there an "official" way to do this?
Thanks in advance for any helpful comment about it.
Stripe Elements Vuejs 2
Use refs to get DOM elements in vuejs.
HTML
<div ref="cardElement"></div>
JS
mounted() {
const stripe = Stripe('pk');
const elements = stripe.elements();
const card = elements.create('card');
card.mount(this.$refs.cardElement);
}
I faced the same problem, so the method mounted is correct to add, but for the big applications where u call a specific vuejs i got the error
"please make sure the element you are attempting to use is still mounted."
HTML Snippet :
<div style="min-height:100px;">
<div class="group">
<h4><span class="label label-default"> Enter Card Details</span> </h4>
<label class="cardlabel">
<span>Card number</span>
<div id="card-number-element" class="field"></div>
<span class="brand"><i class="pf pf-credit-card" id="brand-icon"></i></span>
</label>
<label class="cardlabel">
<span>Expiry date</span>
<div id="card-expiry-element" class="field"></div>
</label>
<label class="cardlabel">
<span>CVC</span>
<div id="card-cvc-element" class="field"></div>
</label>
</div>
</div>
Vue.js
(function () {
let stripe = Stripe('keyhere');
elements = stripe.elements(),
cardNumberElementStripe = undefined;
cardExpiryElementStripe = undefined;
cardCvcElementStripe = undefined;
var style = {
base: {
iconColor: '#666EE8',
color: '#31325F',
lineHeight: '40px',
fontWeight: 300,
fontFamily: 'Helvetica Neue',
fontSize: '15px',
'::placeholder': {
color: '#CFD7E0',
},
},
};
var purchase= new Vue({
el: '#purchase',
mounted() {
cardNumberElementStripe = elements.create('cardNumber', {
style: style
});
cardExpiryElementStripe = elements.create('cardExpiry', {
style: style
});
cardCvcElementStripe = elements.create('cardCvc', {
style: style
});
cardNumberElementStripe.mount('#card-number-element');
cardExpiryElementStripe.mount('#card-expiry-element');
cardCvcElementStripe.mount('#card-cvc-element');
cardNumberElementStripe.on('change', function (event) {
// Switch brand logo
if (event.brand) {
if (event.error) { setBrandIcon("unknown"); } else { setBrandIcon(event.brand); }
}
//setOutcome(event);
});
function setBrandIcon(brand) {
var brandIconElement = document.getElementById('brand-icon');
var pfClass = 'pf-credit-card';
if (brand in cardBrandToPfClass) {
pfClass = cardBrandToPfClass[brand];
}
for (var i = brandIconElement.classList.length - 1; i >= 0; i--) {
brandIconElement.classList.remove(brandIconElement.classList[i]);
}
brandIconElement.classList.add('pf');
brandIconElement.classList.add(pfClass);
}
var cardBrandToPfClass = {
'visa': 'pf-visa',
'mastercard': 'pf-mastercard',
'amex': 'pf-american-express',
'discover': 'pf-discover',
'diners': 'pf-diners',
'jcb': 'pf-jcb',
'unknown': 'pf-credit-card',
}
},
created() {
//on the buttn click u are calling this using v-on:click.prevent="payment"
payment: function (e) {
stripe.createToken(cardNumberElementStripe).then(function (result) {
debugger;
if (result.token) {
cardId = result.token.id;
// $("#paymentform").get(0).submit();
} else if (result.error) {
errorElement.textContent = result.error.message;
return;
}
});
}
}