Use a template in vue component passed as a prop - javascript

I'm a total newbie, so please bear with me if I'm still grasping with the coding fundamentals.
I want to use a template that is defined in the prop. The template is inside the DOM. The reason I want to do it this way is that I want to reuse the component logic (specifically the pagination), but may want to change how the way the template displays the data in different pages. So I wanted to seaparate the template from the script file.
This is the HTML File:
<div id="store-list">
<paginated-list :list-data="stores" use-template="#displayList"></paginated-list>
</div>
<script type="text/template" id="display-list">
<div>
<div v-for="p in paginatedData">
{{p.BusinessName}}
</div>
</div>
</script>
This is the .js file:
Vue.component('paginated-list', {
data() {
return {
pageNumber: 0
}
},
props: {
listData: {
type: Array,
required: true
},
useTemplate: {
type: String,
required: false
},
size: {
type: Number,
required: false,
default: 10
}
},
computed: {
pageCount() {
let l = this.listData.length,
s = this.size;
return Math.floor(l / s);
},
paginatedData() {
const start = this.pageNumber * this.size,
end = start + this.size;
return this.listData
.slice(start, end);
}
},
//template: document.querySelector('#display-list').innerHTML // this works
template: document.querySelector(useTemplate).innerHTML // this does not
});
var sl = new Vue({
el: '#store-list',
data: {
stores: [{
"BusinessName": "Shop Number 1"
}, {
"BusinessName": "Shop Number 2"
}, {
"BusinessName": "Shop Number 3"
}]
}
});
var shoppingList = new Vue({
el: '#shopping-list',
data: {
header: 'shopping list app',
newItem: '',
items: [
'1',
'2',
'3',
]
}
})
Any help is greatly appreciated. Thank you.

You can use the inline-template attribute to override the component's template at render time. For example
<paginated-list :list-data="stores" inline-template>
<div>
<div v-for="p in paginatedData">
{{p.BusinessName}}
</div>
</div>
</paginated-list>
See https://v2.vuejs.org/v2/guide/components-edge-cases.html#Inline-Templates
Your component can still have a default template but this will override it if set.

Related

How load html content into Quill wysiwyg editor?

I want to use #vueup/vue-quill ^1.0.0-beta.7 on my control panel admin based on vue ^3.2.29.
Unfortunately, I noticed that loading HTML content is not working for me. Quill converts <div> tags to <p> tags for me, also removing classy and css styles, which destroys the appearance of the content. On backand i use Laravel.
Can anyone help me with this? I sit on it all day to no avail.
<template>
// [...]
<QuillEditor
ref="mainContent"
v-model:content="form.content"
style="min-height: 300px"
:options="editorOptions"
theme="snow"
content-type="html"
output="html"
#text-change="countMainContent"
/>
// [...]
</template>
<script>
import { QuillEditor, Quill } from "#vueup/vue-quill";
import "#vueup/vue-quill/dist/vue-quill.snow.css";
import BlotFormatter from "quill-blot-formatter";
import QuillImageDropAndPaste, { ImageData } from "quill-image-drop-and-paste";
import ArticleCategoryField from "../forms/ArticleCategoryField.vue";
import htmlEditButton from "quill-html-edit-button";
import useVuelidate from "#vuelidate/core";
import { required } from "../../utils/i18n-validators";
Quill.register({
"modules/blotFormatter": BlotFormatter,
"modules/htmlEditButton": htmlEditButton,
"modules/imageDropAndPaste": QuillImageDropAndPaste,
});
// [...]
data() {
return {
// [...]
editorOptions: {
handlers: {
// handlers object will be merged with default handlers object
link: function (value) {
if (value) {
var href = prompt("Enter the URL");
this.quill.format("link", href);
} else {
this.quill.format("link", false);
}
},
},
modules: {
toolbar: [
["bold", "italic", "underline", "strike"], // toggled buttons
["blockquote", "code-block"],
[{ header: 1 }, { header: 2 }], // custom button values
[{ list: "ordered" }, { list: "bullet" }],
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
[{ font: [] }],
[{ align: [] }],
["image", "link", "video"],
["clean"], // remove formatting button
],
blotFormatter: {},
htmlEditButton: {
debug: false,
msg: "Edytuj zawartość przy pomocy HTML",
cancelText: "Anuluj",
buttonTitle: "Pokaż kod źródłowy HTML",
},
imageDropAndPaste: {
handler: this.imageHandler,
},
},
},
// [...]
}
}
// [...]
methods: {
getArticle() {
if (this.articleId) {
this.$axios
.get("article/" + this.articleId, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${this.$store.state.auth.token}`,
},
})
.then((response) => {
this.form.title = response.data.article.title;
this.form.mainImage =
response.data.article.uploaded_file_id;
this.form.category =
response.data.article.categories[0].id ?? 0;
this.$refs.mainContent.pasteHTML(
response.data.article.content
);
this.form.articleGallery = this.prepareGallery(
response.data.article.images
);
})
.catch((error) => {
if (process.env.MIX_APP_DEBUG)
this.$toast.error(error.message);
throw new Error(error);
});
}
},
// [...]
I know its bit late. But posting this to help those who visit this question.
I had the same problem. When i tried to load contents into use #vueup/vue-quill editor on the edit page, there were complications. I could load delta, html and plain text using setContents(),setHTML() etc, but after that the problem was that when i tried to type inside the same editor js errors occur. The only solution i found was to implement the quill on own. Sharing my experience to help others.
//do bootstrap if needed
// import './bootstrap';
import { createApp } from 'vue';
import { watch, ref, nextTick } from 'vue'
import axios from 'axios';
import Quill from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.bubble.css";
import "quill/dist/quill.snow.css";
createApp({
data() {
return {
mainContentEditor: null,
mainContentEditorValue: '',
}
},
mounted() {
this.initMainContentEditor();
//call this to get article on load
this.getArticle();
},
methods: {
//initiate the main content editor
initMainContentEditor() {
var _this = this;
this.mainContentEditor = new Quill(this.$refs.mainContentEditor, {
modules: {
toolbar: [
[
{
header: [1, 2, 3, 4, false],
},
],
["bold", "italic", "underline", "link"],
],
},
//theme: 'bubble',
theme: "snow",
formats: ["bold", "underline", "header", "italic", "link"],
placeholder: "Type something in here!",
});
//register the event handler
this.mainContentEditor.on("text-change", function () {
_this.mainContentEditorChanged();
});
},
//this method is called when the editor changes its value
mainContentEditorChanged() {
console.log("main content changed!");
// do somethign with it like assign it to mainContentEditorValue
this.mainContentEditorValue = this.mainContentEditor.root.innerHTML;
},
getArticle() {
//do the api call to get the article response.
//once you get the respose
// assign the html content to quill editor
// check getArticle() method on question to fill this part
//replace
// this.$refs.mainContent.pasteHTML(
// response.data.article.content
// );
// with this
this.mainContentEditor.root.innerHTML = response.data.article.content;
}
}
}).mount('#app')
html/template
<div id="app">
<div ref="mainContentEditor"></div>
</div>
You can make it to a component and reuse it. I shared it to just show a working implementation.

use fetched info in an array in vue.js

I have this array:
data() {
return {
landingInfo: null,
slides: [
{
title: `this`,
},
{
title: "that",
},
{
title: "those",
},
],
};
},
Which is being displayed this way:
<div
class="slide"
v-for="(slide, index) in slides"
:key="index",
}"
>
<div>
<h1 class="text-bold">{{ slide.title }}</h1>
</div>
The problem is that I'm fetching info from and api and once I try to do:
slides: [
{
title: {{ landingInfo.data.subtitle }},
},
{
title: {{ landingInfo.data.subtitle2 }},
},
{
title: {{ landingInfo.data.subtitle3 }},
},
],
Everything explodes, I am new in vue using Nuxt.js and I cannot find any solution in how to achieve that.
Can someone show me how to include the fetched info inside the array property?
PD: I already tried using "{{thefetchedinfo}}" but it takes it literally that way and displays "{{thefetchedinfo}}"
The OP doesn't provide much info on the fetch, like when it is performed or what the returned data looks like, but the common pattern goes like this...
// <template> as in the OP, then...
data() {
return {
landingInfo: null,
slides: [], // empty before the fetch
};
},
mounted () {
fetchFromTheAPI().then(landingInfo => {
// More commonly, you'd map over the returned data to form slides,
// or, trying to match the OP...
this.slides = [
{ title: landingInfo.data.subtitle },
{ title: landingInfo.data.subtitle2 }, // ... and so on
]
});
},

Vue, after push to array, all properties react simultaneously [duplicate]

In my case, I have data array with multiple objects
data() {
return {
selected: 0,
presetData: [true, true, true],
data: [
{
name: "name 1"
},
{
name: "name 2"
}
]
};
},
then I want to push inside each object in data like below
setNewData() {
this.data.forEach((o, i) => {
this.$set(this.data[i], "time", this.presetData);
});
},
now my with presetData pushed into data will look like this
data: [
{
name: "name 1",
time: [true, true, true]
},
{
name: "name 2",
time: [true, true, true]
}
]
and I want to change individual time property of each object, which I use something like below
$set(item.time,selected,true)
My Issue
my issue is, this going to change both objects time property. How do I first push/set correctly presetData to data, below is my entire code , I'm sorry I'm very new to programming, here is the link to jsfiddle
new Vue({
el: "#app",
data() {
return {
selected: 0,
presetData: [true, true, true],
data: [
{
name: "name 1",
},
{
name: "name 2",
}
]
};
},
methods: {
setNewData() {
this.data.forEach((o, i) => {
this.$set(this.data[i], "time", this.presetData);
});
},
}
})
<div id="app">
<button #click="setNewData">Set Data</button>
<br>
<br>
<select v-model="selected">
<option>0</option>
<option>1</option>
<option>2</option>
</select>
<div v-for="item in data" :key="item.id">
<p>{{item.name}}</p>
<p>{{item.time}}</p>
<button #click="$set(item.time,selected,true)">Change True</button>
<button #click="$set(item.time,selected,false)">Change False</button>
</div>
This is an object reference issue. Each of your time properties references the same array (presetData). You can break out of this problem by making shallow copies via spread syntax.
You can also avoid Vue.set() when assigning new data using the same technique
setNewData() {
this.data = this.data.map(d => ({
...d, // create a shallow copy of each data item
time: [...this.presetData] // add "time" as a shallow copy of presetData
}))
},
To change individual array elements within the time property, you need to continue using Vue.set(), ie
this.$set(item.time, selected, true)

How to return value from loop. js, vuejs

Sorry, I'm beginner with JS,i have a basic question, but i spent a whole day trying to find answer in google and i didn't.
I have a massic financial instrument developed on php and I need to build complex financial calculator that shows everything with reactivity. I need help to figure out how to make complex calculations with many if statements inside of the loop and then sum output value from each object in array and return total summed value. Using Vuejs for this.
So my cashDividends() must be a sum of calculated values from each object in the loop.
Below I put a piece of code to understand problem I'm facing.
Please check if have a minute. Thanks!
new Vue({
el: "#waterfall",
data() {
return {
info: {
cash_dividends: true,
converted_liabilities: true,
},
equities: [
#foreach($preferredEquities as $equity)
{ name: '{{ $equity->name }}', id: {{ $equity->id }} },
#endforeach
]
}
},
computed: {
remainingExit () {
return this.form.exit_value - this.form.uncovered_debt - this.form.transaction_fees
},
cashDividends() {
//I supposed should be something like this.
this.equities.forEach(function(equity)
{
//Here I make a lot of calculations with bunch of if statements using object and DOM input values. for each object
}
// And here I need to return sum of calculated values from each object (equity) in array
}
},
You could use reduce function which you could learn more about here:
new Vue({
el: "#app",
data: {
equities: [{
name: "Serias A",
price: 20
},
{
name: "Serias B",
price: 21
},
]
},
computed: {
cashDividends() {
return this.equities.reduce(this.sum);
}
},
methods: {
sum(total, num) {
return total.price + num.price
}
}
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.min.js"></script>
<div id="app">
{{cashDividends}}
</div>
Use reduce function
new Vue({
el: "#waterfall",
data: {
equities: [
{name: "Serias A", price: '20'},
{name: "Serias B", price: '21'},
]
},
computed: {
cashDividends() {
var sum = this.equities.reduce(function(acc, equity) {
return acc + parseInt(equity.price);
}, 0)
}
}
});
You can define a variable sum and iteratively add equity.price to it as follows:
cashDividends() {
let sum = 0
this.equities.forEach(function(equity)) {
sum+=equity.price
}
return sum
}

How to use React to reconstruct serialized DOM in JSON?

In my application, I serialize DOM into JSON and send it back to server.
I need help finding an efficient way to later reconstruct the DOM from this serialized JSON using React.
To share a concrete example, let's start with this DOM:
<div id="parent">
<div id="one"></div>
<span id="two"></span>
</div>
This will get serialized into following JSON:
[
{
"index": 0,
"tagName": "DIV",
"attributes": {"id": "parent"}
},
{
"index": 1,
"parent": 0,
"tagName": "DIV",
"attributes": {"id": "one"}
},
{
"index": 2,
"parent": 0,
"tagName": "SPAN",
"attributes": {"id": "two"}
}
]
Now, in vanilla Javascript, I can re-build DOM like following:
var nodes = {};
for (var i = 0; i < json.length; i++) {
var node = json[i];
// Create element
var element = document.createElement(node.tagName);
// Add attributes
for (var attribute in node.attributes) {
element.setAttribute(attribute, node.attributes[attribute]);
}
// Add parent-child association
if (node.parent != undefined) nodes[node.parent].appendChild(element);
// Cache it for later reference
nodes[node.index] = element;
}
I'm new to React, and so far I haven't figured out a way to establish dynamic parent-child relationship between components that I can change later. What would be a good way to accomplish this using React, or may be, is it even a good application of React?
The example above is a reduced example, but you can imagine the complexity can increase as different JSON instructions are introduced to re-parent, add or remove a particular DOM node.
If you deserialize your JSON recursively it saves the need to manage parent-child relations explicitly.
Provide your JSON-HTML representation as a tree (rather than a list), and make sure your serialization observes a consistent pattern for all tags - they can then (at least in theory) be interpreted by the same React component.
Somewhat elaborate below, two things to note
Each tag needs a unqiue key, e.g. a hash or application-id
All nodes - and specifically text-only nodes - need a html root (I've chosen the tag <text> to enable text-only content in <p> and <div> bodies
Output should be
<div id="root_node">
<div>
<p><i>hi </i><b>there </b><text>friend</text></p>
<p><b>hey</b></p>
</div>
<p><i>hey again</i></p>
</div>
React code
class JsonToHtml extends React.Component {
constructor(props) {
super(props);
this.state = {
json_html:
{ node : 'root',
child: [
{ type: 'parent', tag: 'div', key: '84a4fb',
child: [
{ type: 'parent', tag: 'p', key: '1bb600',
child: [
{type: 'child', tag: 'i', val: 'hi ', key: 'f6a3ad'},
{type: 'child', tag: 'b', val: 'there ', key: '57ef5e'},
{type: 'child', tag: 'text', val: 'friend', key: '57ef52'}
]
},
{ type: 'parent', tag: 'p', key: 'a43a9f',
child: [
{type: 'child', tag: 'b', val: 'hey', key: '4e2d46'},
]
}
]
},
{ type: 'parent', tag: 'p', key: '3e21f6',
child: [
{type: 'child', tag: 'i', val: 'hey again', key: 'ed5a49'},
]
}
]
}
};
}
render() {
return (
<div id='root_node'>
{this.state.json_html.child.map(function(node) {
return (
<HtmlComponent key={node.key} node={node} />
)
})
}
</div>
)
}
} export default JsonToHtml
class HtmlComponent extends React.Component {
render() {
if (this.props.node.type == "child") {
return React.createElement(
this.props.node.tag, {},
this.props.node.val)
}
if (this.props.node.type == "parent") {
return React.createElement(
this.props.node.tag, {},
this.props.node.child.map(function(node) {
return (
<HtmlComponent key={node.key} node={node} />
)
})
)
}
}
}
Use should use
React.createElement(type, props, children)
-
type: can be string or ReactClass, in your case you can 'DIV', 'SPAN'
props: are properties of element, in your case you pass {id:'elm_id',className:'css_class'}
children: This is where you build hierarchy. This can be reactText, reactNode, reactFragment (reactNodes)
I am not writing the loop, I am just explaining how to create hierarchy.
var child1 = React.createElement('div',{id:'one'}, null);
var child2 = React.createElement('span',{id:'two'}, null);
var reactFragment = [child1,child2];
var parent = React.createElement('div',{id:'parent'}, reactFragment);

Categories