How to pass property value between to instances of a component - javascript

I have to datepicker components, from date and to date.
i want to update the to date with the from date if the to date is empty.
I've looked at doing emit on the value but feel this is not right and i'm not sure how to do it
<datepicker
input-label="From"
input-id="start-date"
input-name="start_date"
input-value="<%= group_discount.start_date %>"
#change-date="changeDate"
>
</datepicker>
<datepicker
input-label="To"
input-id="end-date"
input-name="end_date"
input-value="<%= group_discount.end_date %>">
</datepicker>
import Vue from "vue"
import Datepicker from "../components/DatePicker"
Vue.use(Datepicker)
const initGroupDiscount = () => {
new Vue({
el: "#js-group-discounts",
components: {
Datepicker,
},
methods: {
changeDate(value) {
console.log("value")
console.log(value)
},
},
})
}
document.addEventListener("DOMContentLoaded", () => {
initGroupDiscount()
})
<template>
<div >
<label :for="this.inputId">{{ this.inputLabel }}</label>
<input type="text"
class="form-control form-control-info"
placeholder="dd/mm/yyyy"
:name="this.inputName"
:id="this.inputId"
pattern="\d{1,2}/\d{1,2}/\d{4}"
required
v-model="isInput"
v-on:keyup="updateCalendar($event)"
ref="dateinput"
#blur="blur"
#focus="focus">
<datepicker format="dd/MM/yyyy"
input-class="form-control"
placeholder="dd/mm/yyyy"
v-model="isPicker"
:inline="true"
v-show="isOpen"
#mouseover.native="mouseOver"
#mouseleave.native="mouseLeave"
#selected="updateInput"></datepicker>
</div>
</template>
<script>
import Vue from "vue"
import Datepicker from "vuejs-datepicker"
Vue.use(Datepicker)
export default {
name: "neptune-datepicker",
props: {
inputLabel: {
type: String,
},
inputId: {
type: String,
},
inputValue: {
type: String,
},
inputName: {
type: String,
},
},
data(){
let value = ""
if (this.inputValue) {
const dateParts = this.inputValue.split("-")
value =`${dateParts[2]}/${dateParts[1]}/${dateParts[0]}`
}
return {
isInput: value,
isPicker: this.inputValue,
isOpen: false,
}
},
components: {
Datepicker
},
methods: {
updateInput(date) {
this.isInput = date.toLocaleDateString("en-GB")
this.$emit('changeDate', this.isInput);
},
updateCalendar(event) {
const dateString = event.srcElement.value
if (dateString.length === 10) {
const dateParts = dateString.split("/")
const dateObject = new Date(
dateParts[2],
dateParts[1],
dateParts[0],
)
if ((dateObject !== "Invalid Date") && !Number.isNaN(dateObject)) {
this.isPicker = dateObject
}
}
},
blur() {
this.isOpen = false
},
focus() {
this.$refs.dateinput.focus()
this.isOpen = true
},
mouseOver() {
this.$refs.dateinput.focus()
this.isOpen = true
},
mouseLeave() {
this.$refs.dateinput.focus()
this.isOpen = true
},
},
}
</script>
it emits the correct value to the console but i don't know how i would pass this to only that particular instance of the component, the "To" date

The parent component should pass a prop to both data sub-components:
<datepicker
:myValue="myValue"
input-label="From"
input-id="start-date"
input-name="start_date"
input-value="<%= group_discount.start_date %>"
#change-date="changeDate"
>
</datepicker>
<datepicker
:myValue="myValue"
input-label="To"
input-id="end-date"
input-name="end_date"
input-value="<%= group_discount.end_date %>">
</datepicker>
Then you can use that prop in a computed or method to validate if it contains a value or not and override the to or from value.
Edit: You can also use your emit strategy but you still need to pass a prop to one of the components so it can read the data. If you've never used props before read about them here: https://v2.vuejs.org/v2/guide/components-props.html

Related

VueJS Duplicate components all updating at the same time

Might be a simple solution, but I'm currently not seeing it. I have an object that describes several configurations.
Object looks like this:
export const fieldSelectionDefault = {
cohort: {
currency_key: null,
salary_key: null,
timeframe_key: null
},
school: {
currency_key: null,
salary_key: null,
timeframe_key: null,
response_count_key: null,
},
}
export const cohortListFieldDefault = {
field_student: { ...fieldSelectionDefault },
field_alum_1: { ...fieldSelectionDefault },
field_alum_2: { ...fieldSelectionDefault },
field_alum_3: { ...fieldSelectionDefault },
}
Now, I have a parent component where I have a form. This form will list each field_* to have a <CohortFieldConfig /> component where we can input the values of the fieldSelectionDefault.
In the parent form, I add them like this:
<h5>Student</h5>
<CohortFieldConfig
:key="'settings.field_student'"
:disabled="settings.active_entities.student"
:selection-fields="settings.field_student"
#update-fields="(val) => test(val, 'stu')"
/>
<h5>Alumnus 1</h5>
<CohortFieldConfig
:key="'settings.field_alum_1'"
:disabled="settings.active_entities.alum_1"
:selection-fields="settings.field_alum_1"
#update-fields="(val) => test(val, 'alum')"
/>
CohortFieldConfig looks like this (example of one inputs, removed js imports):
<template>
<div>
<a-form-item label="Currency input">
<a-input
:disabled="!disabled"
placeholder="Select a currency form key"
v-model="objSelectionFields.cohort.currency_key"
/>
</a-form-item>
<FieldSelector
#select="val => (objSelectionFields.cohort.currency_key = val)"
:user="user"
:disabled="!disabled"
/>
</div>
</template>
<script>
export default {
name: 'CohortFieldConfig',
components: { FieldSelector },
props: {
selectionFields: {
type: [Object, null],
default: () => {
return { ...fieldSelectionDefault }
},
},
disabled: {
type: Boolean,
default: () => false,
},
},
data: function() {
return {
fieldSelectionDefault,
objSelectionFields: { ...this.selectionFields },
}
},
watch: {
objSelectionFields: {
handler(){
this.$emit('update-fields', this.objSelectionFields)
},
deep: true
}
},
methods: {
update() {
// not really used atm
this.$emit('update-fields', this.objSelectionFields)
},
},
}
</script>
When you type in the input, BOTH are updated at the same time. For student & alum_1.
The update-fields event is fired for both (same) components
Whats the reason? I've tried setting different key, doesn't work.
UPDATE
As pointed out in the comments, the issue was I was giving the same object. To correct this, I make a (deep) copy of the object as so:
export const cohortListFieldDefault = {
field_student: JSON.parse(JSON.stringify(fieldSelectionDefault)),
field_alum_1: JSON.parse(JSON.stringify(fieldSelectionDefault)),
field_alum_2: JSON.parse(JSON.stringify(fieldSelectionDefault)),
field_alum_3: JSON.parse(JSON.stringify(fieldSelectionDefault)),
}

end date must be less than start date, Vuelidate and VueJs

I'm having problems doing a form validation. I have a start date and an end date. I need to confirm that the end date is greater than the start date.
I'm using vuejs and vuelidate.
I'm working with an Input.vue component, which has a standard input being reused in other forms. I also have a Form.vue component that has Input.vue inside. And this Form.vue component is called both in the modal of creating a new event and in the editing mode.
My problem is in the validation of when the event is edited. If you select an end date less than the start date, it doesn't allow saving the information, that's ok. However, when trying to correct it by selecting a date greater than the start date, and clicking on save again, it seems that it is no longer calling validation. With this, the error that the end date must be greater than the initial one does not disappear. I did several tests and noticed that validation is not called after the error occurs when editing. I do not know why.
component Input.vue:
<template>
<b-form-group :label="label" :label-for="labelFor">
<b-form-input
:id="labelFor"
v-model="value"
:type="type"
:placeholder="placeholder"
:class="{ 'is-invalid': this.isInvalid }"
required>
</b-form-input>
<slot></slot>
</b-form-group>
</template>
<script>
export default {
name: 'InputForm',
props: [ 'label', 'labelFor', 'vModel', 'type', 'placeholder', 'isInvalid' ],
computed: {
value: {
get() {
return this.vModel
},
set(value) {
this.newValue = value
return this.$emit('emitValue', this.newValue)
}
}
},
}
</script>
component Form.vue:
<template>
<b-form>
....
<InputForm
class="col-8 pl-0"
:label="'*Início'"
:labelFor="'event_start_at_date'"
:vModel="event.start_at_date"
:type="'date'"
#emitValue="($event) => event.start_at_date = $event"
/>
<InputForm
class="col-4 pr-0"
v-if="!event.all_day"
:labelFor="'event_start_at_time'"
:vModel="event.start_at_time"
:type="'text'"
#emitValue="($event) => event.start_at_time = $event"
/>
</b-col>
<b-col cols="12" lg="6" class="d-flex align-items-end">
<InputForm
class="col-8 pl-0"
:label="'*Fim'"
:labelFor="'event_end_at_date'"
:type="'date'"
:vModel="event.end_at_date"
#emitValue="($event) => event.end_at_date = $event"
:isInvalid="invalid.end_at_date.$error"
>
<b-form-invalid-feedback v-if="this.submitted" :state="invalid.end_at_date.$error == 0">Fim deve ser maior que início.</b-form-invalid-feedback>
</InputForm>
<InputForm
class="col-4 pr-0"
v-if="!event.all_day"
:labelFor="'event_end_at_time'"
:vModel="event.end_at_time"
:type="'text'"
:isInvalid="invalid.end_at_date.$error"
:class="{ 'error' : invalid.end_at_date.$error }"
#emitValue="($event) => event.end_at_time = $event"
/>
...
</b-form>
</template>
export default {
name: 'Form',
props: [ 'event', 'event_shared', 'error', 'submitted' ],
components: { Input },
computed: {
invalid() {
if(this.error.edit_event != undefined) {
return this.error.edit_event
} else if(this.error.event != undefined) {
return this.error.event
} else {
return false;
}
},
stateInvalid() {
if(this.error.edit_event != undefined) {
return this.error.edit_event
} else if(this.error.event != undefined) {
return this.error.event
} else {
return 0;
}
},
},
watch: {
event() {
let newEvent = this.event
this.$emit('emitObj', newEvent)
},
},
}
</script>
component ModalEditEvent.vue:
...
<VWarningErrorForm v-if="$v.$error" />
<FormEvent
:event="edit_event"
:event_shared="get_shared_users_id"
:error="this.$v"
:submitted="this.submitted"
#emitObj="($event) => edit_event = $event"
/>
...
<b-button
v-if="editEvent && !view_popover_save"
variant="primary" type="submit" #click="save()">Gravar</b-button>
...
<script>
import moment from 'moment';
import { required } from "vuelidate/lib/validators";
import isAfterDate from '#/functions/isAfterDate'
...
validations: {
edit_event: {
name: {
required
},
end_at_date: {
required: function(value) {
return isAfterDate(value, this.edit_event);
}
},
},
},
...
save() {
this.submitted = true;
this.$v.$touch();
if(!this.$v.$error) {
....
}
},
validation
import moment from 'moment';
function isAfterDate(value, vm) {
let hour = function(att) {
return `${att[0]}${att[1]}`;
}
let minute = function(att) {
return `${att[3]}${att[4]}`;
}
let start = moment(vm.start_at_date).set({hour: hour(vm.start_at_time), minute: minute(vm.start_at_time) })._d
let end = moment(vm.end_at_date).set({hour: hour(vm.end_at_time), minute: minute(vm.end_at_time) })._d
return end >= start;
}
export default isAfterDate;
Solved my issue with Vue.set. I realized that I was trying to detect the change of a property that I added later myself. So I had to use Vue.set when adding the property, so I could detect the change later, and then correctly validate the form. Thanks to whoever tried to help me!

how to fix Error componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops

I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
this is the input component code Number I have tried the input component works well,:
import React from "react";
import propTypes from "prop-types";
import "./index.scss";
export default function Number(props) {
const {
value,
placeholder,
name,
min,
max,
prefix,
suffix,
isSuffixPlural,
} = props;
const onChange = (e) => {
let value = String(e.target.value);
if (+value <= max && +value >= min) {
props.onChange({
target: {
name: name,
value: +value,
},
});
}
};
const minus = () => {
value > min &&
onChange({
target: {
name: name,
value: +value - 1,
},
});
};
const plus = () => {
value < max &&
onChange({
target: {
name: name,
value: +value + 1,
},
});
};
return (
<div className={["input-number mb-3", props.outerClassName].join(" ")}>
<div className="input-group">
<div className="input-group-prepend">
<span className="input-group-text minus" onClick={minus}>
-
</span>
</div>
<input
min={min}
max={max}
name={name}
pattern="[0-9]*"
className="form-control"
placeholder={placeholder ? placeholder : "0"}
value={`${prefix}${value}${suffix}${
isSuffixPlural && value > 1 ? "s" : ""
}`}
onChange={onChange}
/>
<div className="input-group-append">
<span className="input-group-text plus" onClick={plus}>
+
</span>
</div>
</div>
</div>
);
}
Number.defaultProps = {
min: 1,
max: 1,
prefix: "",
suffix: "",
};
Number.propTypes = {
value: propTypes.oneOfType([propTypes.string, propTypes.number]),
onChange: propTypes.func,
placeholder: propTypes.string,
isSuffixPlural: propTypes.bool,
outerClassName: propTypes.string,
};
and this is my input date component code I have tried the input component works well, :
import React, { useState, useRef, useEffect } from "react";
import propTypes from "prop-types";
import { DateRange } from "react-date-range";
import "./index.scss";
import "react-date-range/dist/styles.css"; // main css file
import "react-date-range/dist/theme/default.css"; // theme css file
import formatDate from "utils/formatDate";
import iconCalendar from "assets/images/icon/icon-calendar.svg";
export default function Date(props) {
const { value, placeholder, name } = props;
const [isShowed, setIsShowed] = useState(false);
const datePickerChange = (value) => {
const target = {
target: {
value: value.selection,
name: name,
},
};
props.onChange(target);
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
const refDate = useRef(null);
const handleClickOutside = (event) => {
if (refDate && !refDate.current.contains(event.target)) {
setIsShowed(false);
}
};
const check = (focus) => {
focus.indexOf(1) < 0 && setIsShowed(false);
};
const displayDate = `${value.startDate ? formatDate(value.startDate) : ""}${
value.endDate ? " - " + formatDate(value.endDate) : ""
}`;
return (
<div
ref={refDate}
className={["input-date mb-3", props.outerClassName].join(" ")}
>
<div className="input-group">
<div className="input-group-prepend bg-gray-900">
<span className="input-group-text">
<img src={iconCalendar} alt="icon calendar" />
</span>
</div>
<input
readOnly
type="text"
className="form-control"
value={displayDate}
placeholder={placeholder}
onClick={() => setIsShowed(!isShowed)}
/>
{isShowed && (
<div className="date-range-wrapper">
<DateRange
editableDateInputs={true}
onChange={datePickerChange}
moveRangeOnFirstSelection={false}
onRangeFocusChange={check}
ranges={[value]}
/>
</div>
)}
</div>
</div>
);
}
Date.propTypes = {
value: propTypes.object,
onChange: propTypes.func,
placeholder: propTypes.string,
outerClassName: propTypes.string,
};
I have tried the inpudate component to run well, as well as the input number, but if I combine these components I have an error did i miss something, and I tried to combine these components on the bookingform page but when I tried on the browser I experienced the above error.
My code is in the Booking Form:
import React, { Component } from "react";
import propTypes from "prop-types";
import Button from "elements/Button";
import { InputNumber, InputDate } from "elements/Form";
export default class BookingForm extends Component {
constructor(props) {
super(props);
this.state = {
data: {
duration: 1,
date: {
startDate: new Date(),
endDate: new Date(),
key: "selection",
},
},
};
}
updateData = (e) => {
this.setState({
...this.state,
data: {
...this.state.data,
[e.target.name]: e.target.value,
},
});
};
componentDidUpdate(prevProps, prevState) {
const { data } = this.state;
if (prevState.data.date !== data.date) {
const startDate = new Date(data.date.startDate);
const endDate = new Date(data.date.endDate);
const countDuration = new Date(endDate - startDate).getDate();
this.setState({
data: {
...this.state.data,
duration: countDuration,
},
});
}
if (prevState.data.duration !== data.duration) {
const startDate = new Date(data.date.startDate);
const endDate = new Date(
startDate.setDate(startDate.getDate() + +data.duration - 1)
);
this.setState({
...this.state,
data: {
...this.state.data,
date: {
...this.state.data.date,
endDate: endDate,
},
},
});
}
}
startBooking = () => {
const { data } = this.state;
this.props.startBooking({
_id: this.props.itemDetails._id,
duration: data.duration,
date: {
startDate: data.date.startDate,
endDate: data.date.endDate,
},
});
this.props.history.push("/checkout");
};
render() {
const { data } = this.state;
const { itemDetails } = this.props;
console.log(this.state);
return (
<div className="card bordered" style={{ padding: "60px 80px" }}>
<h4 className="mb-3">Start Booking</h4>
<h5 className="h2 text-teal mb-4">
${itemDetails.price}{" "}
<span className="text-gray-500 font-weight-light">
per {itemDetails.unit}
</span>
</h5>
<label htmlFor="duration">How long you will stay?</label>
<InputNumber
max={30}
suffix={" night"}
isSuffixPlural
onChange={this.updateData}
name="duration"
value={data.duration}
/>
<label htmlFor="date">Pick a date</label>
<InputDate onChange={this.updateData} name="date" value={data.date} />
<h6
className="text-gray-500 font-weight-light"
style={{ marginBottom: 40 }}
>
You will pay{" "}
<span className="text-gray-900">
${itemDetails.price * data.duration} USD
</span>{" "}
per{" "}
<span className="text-gray-900">
{data.duration} {itemDetails.unit}
</span>
</h6>
<Button
className="btn"
hasShadow
isPrimary
isBlock
onClick={this.startBooking}
>
Continue to Book
</Button>
</div>
);
}
}
BookingForm.propTypes = {
itemDetails: propTypes.object,
startBooking: propTypes.func,
};
I encountered this error and tried to fix it, but couldn't find a solution to the problem
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
I encountered this error and tried to fix it, but couldn't find a solution to the problem
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
I encountered this error and tried to fix it, but couldn't find a solution to the problem
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.

How to catch events is vue.js v-calendar

I am using the vuejs v-calender plugin so I have a daterange picker. It all renders fine and I can select dates but that is is it.
What I want to do is just log out the selected dates so later I can store them in database, update form etc but I don't know how to achieve this. I can't find any examples in the documentation of how to do this.
Does anyone know how to get the start and end dates of a selected date range?
Here is what I have so far...
<template>
<v-date-picker mode='range' v-model='range' is-inline :columns="$screens({ default: 1, lg: 2 })" />
</template>
<script>
import { DatePicker } from 'v-calendar'
export default {
name: 'Booking',
components: {
DatePicker
},
data() {
return {
range: {
start: new Date(),
end: null
}
}
},
mounted() {
this.$root.$on('input', (value) => {
console.log('dxggdfg');
});
}
}
</script>
Add input event
<v-date-picker mode='range' v-model='range' #input="onDateRangeChange" is-inline :columns="$screens({ default: 1, lg: 2 })" />
{
...
methods: {
onDateRangeChange() {
console.log(this.range)
}
},
mounted() {
this.$root.$on('input', (value) => {
console.log('dxggdfg');
});
}
}
Alternatively you can use watch, which works well if you also update your v-model externally:
{
...
watch: {
range: {
handler: function () {
console.log(this.range)
},
deep: true
}
},
mounted() {
this.$root.$on('input', (value) => {
console.log('dxggdfg');
});
}
}
For me, it worked with #input
For example:
<date-picker mode='range' v-model='range' is-range class="m-auto" #input="changeDate"/>
Then, in the methods, I just have a method called "changeDate". This will be called once the second date has been selected in the date range.
Stumbled across this - hopefully will help someone else.
When using just single dates, you can set things up like this.
<v-date-picker
is-expanded
:columns="$screens({ default: 1, lg: 2 })"
:attributes="calendar.attributes"
v-model="calendar.today"
#dayclick="changeDate"
/>
...
methods: {
changeDate() {
console.log('changeDate called');
// emit your own event or ??
},
}
...
computed: {
dateRange: {
set (val) {
// action after date change here
},
get () {
let range = {
start: new Date(moment().subtract(7, 'day').format('YYYY-MM-DD')),
end: new Date(moment().format('YYYY-MM-DD'))
}
// action after receives date from props
return range
}
}
}
<v-date-picker
:columns="$screens({ default: 1, lg: 2 })"
is-range
:value="dateRange"
:attributes="attrs"
:max-date='new Date()'
v-model="dateRange"
/>
You can achieve the result with computed, I waste a lot of time with data() but it didn't work for me

How do I render a child component within an iframe in Vue?

So I want to show the user a preview of what an email will look like before it's sent out. To avoid styles from leaking from the parent page into the preview, I've decided to use an iframe. I want the preview to update in real time as the user enters form details.
How would I render a component within an iframe so that its props update automatically when the parent form is updated? This is the code I have so far:
this is the html:
<template>
<div id="confirmation">
<h2>Give a gift</h2>
<form #submit.prevent="checkout()">
<div class="date-section">
<label class="wide">Send</label>
<input type="radio" name="sendLater" v-model="sendLater" required :value="false">
<span>Now</span>
<input type="radio" name="sendLater" v-model="sendLater" required :value="true">
<span style="margin-right: 5px;">Later: </span>
<date-picker :disabled="!sendLater" v-model="date" lang="en" />
</div>
<div>
<label>Recipient Email</label>
<input type="email" class="custom-text" v-model="form.email" required>
</div>
<div>
<label>Recipient Name</label>
<input type="text" class="custom-text" v-model="form.name" required>
</div>
<div>
<label>Add a personal message</label>
<textarea v-model="form.message" />
</div>
<p class="error" v-if="error">Please enter a valid date.</p>
<div class="button-row">
<button class="trumpet-button" type="submit">Next</button>
<button class="trumpet-button gray ml10" type="button" #click="cancel()">Cancel</button>
</div>
</form>
<iframe id="preview-frame">
<preview-component :form="form" :sender-email="senderEmail" :term="term" />
</iframe>
</div>
</template>
here is the js (note: PreviewComponent is the actual preview that will be rendered in the iframe):
export default {
name: 'ConfirmationComponent',
components: {
DatePicker,
PreviewComponent
},
props: {
term: {
required: true,
type: Object
}
},
data() {
return {
form: {
name: null,
email: null,
message: null,
date: null
},
date: null,
sendLater: false,
error: false
}
},
computed: {
senderEmail() {
// utils comes from a separate file called utils.js
return utils.user.email || ''
}
},
watch: {
'form.name'(val) {
this.renderIframe()
},
'form.email'(val) {
this.renderIframe()
}
},
methods: {
renderIframe() {
if (this.form.name != null && this.form.email != null) {
console.log('rendering iframe')
// not sure what to do here......
}
}
}
}
I've tried all sorts of things but what seems to be the hardest is setting the props of the preview-component properly. Any help you all can give would be appreciated.
So as posted in one of the comments, Vuex works perfectly for this.
I also ended up creating a custom "IFrame" component that renders whatever you have inside its slot in an iframe.
Here is my Vuex store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
form: {
name: null,
email: null,
message: null
},
senderEmail: null,
term: null,
styles: null
},
mutations: {
updateForm(state, form) {
state.form = form
},
updateEmail(state, email) {
state.senderEmail = email
},
updateTerm(state, term) {
state.term = term
},
stylesChange(state, styles) {
state.styles = styles
}
}
})
my IFrame component:
import Vue from 'vue'
import { store } from '../../store'
export default {
name: 'IFrame',
data() {
return {
iApp: null,
}
},
computed: {
styles() {
return this.$store.state.styles
}
},
render(h) {
return h('iframe', {
on: {
load: this.renderChildren
}
})
},
watch: {
styles(val) {
const head = this.$el.contentDocument.head
$(head).html(val)
}
},
beforeUpdate() {
this.iApp.children = Object.freeze(this.$slots.default)
},
methods: {
renderChildren() {
const children = this.$slots.default
const body = this.$el.contentDocument.body
const el = document.createElement('div') // we will mount or nested app to this element
body.appendChild(el)
const iApp = new Vue({
name: 'iApp',
store,
data() {
return {
children: Object.freeze(children)
}
},
render(h) {
return h('div', this.children)
}
})
iApp.$mount(el)
this.iApp = iApp
}
}
}
finally here is how data is passed to the PreviewComponent from the ConfirmationComponent:
export default {
name: 'ConfirmationComponent',
mounted() {
this.$store.commit('updateEmail', this.senderEmail)
this.$store.commit('updateTerm', this.term)
},
watch: {
'form.name'(val) {
this.updateIframe()
},
'form.email'(val) {
this.updateIframe()
}
},
methods: {
updateIframe() {
this.$store.commit('updateForm', this.form)
}
}
}
then lastly the actual PreviewComponent:
import styles from '../../../templates/styles'
export default {
name: 'PreviewComponent',
mounted() {
this.$store.commit('stylesChange', styles)
},
computed: {
redemption_url() {
return `${window.config.stitcher_website}/gift?code=`
},
custom_message() {
if (this.form.message) {
let div = document.createElement('div')
div.innerHTML = this.form.message
let text = div.textContent || div.innerText || ''
return text.replace(/(?:\r\n|\r|\n)/g, '<br>')
}
return null
},
form() {
return this.$store.state.form
},
term() {
return this.$store.state.term
},
senderEmail() {
return this.$store.state.senderEmail
}
}
}
hopefully this will help somebody.

Categories