PostMessage Issues Using IFrame in JS - javascript

I have two buttons that work differently. The first one is when you click the reload button, it should reload the page. The second one is when you click it, it will show the alert on the page.
I'm using postMessage because its inside the iframe. I'm not sure why the two buttons are not working but I already implemented the postMessage and window.onmessage
CODESANDBOX
PARENT WINDOW
CHILD PAGE
CODE
function reloadPage() {
window.parent.postMessage({ reload: true }, 'https://8beu4h.csb.app');
}
function alertPage() {
window.parent.postMessage({ alert: true }, 'https://8beu4h.csb.app');
}
window.onmessage = (event) => {
const { data, origin, source } = event;
if (source == frameToListen.contentWindow) {
try {
if (data.reload) {
window.location.reload();
}
} catch (e) {
console.error(e);
}
}
};
window.onmessage = (event) => {
const { data, origin, source } = event;
if (source == frameToListen.contentWindow) {
try {
if (data.alert) {
alert("HI");
}
} catch (e) {
console.error(e);
}
}
};

The second argument to Window.postMessage() is the target origin, not the source one.
Change
postMessage({ reload: true }, 'https://8beu4h.csb.app');
to
postMessage({ reload: true }, 'https://eq0o2y.csb.app');
Also, onmessage can only set one handler at a time. Your second one will remove the previous one. Either you merge both handlers in a single function, either you use addEventListener()

Parent Window
window.addEventListener(
"message",
function (event) {
const { data, source } = event;
console.log(source);
if (source == frameToListen.contentWindow) {
if (data.reload) window.location.reload();
if (data.alert) alert("HI");
}
},
false
);
CHILD WINDOW
function reloadPage() {
window.parent.postMessage({ reload: true }, 'https://eq0o2y.csb.app/');
}
function alertPage() {
window.parent.postMessage({ alert: true }, 'https://eq0o2y.csb.app/');
}

Related

Angular 8 return focus to trigger element after closing modal

For screen reader usability I need to return the focus to the element that triggered the modal launch after closing the modal. I have a modal service with
showModal(type: string, config: ModalConfiguration) {
setTimeout(()=> {
this.store.dispatch(new ModalAction.ShowModal({ modalType: type, modalConfig: config }));
}, 100);
}
hideModal() {
this.store.dispatch(new ModalAction.HideModal());
}
And the function called on the trigger element is this
login() {
setTimeout(() => {
this.modalService.showModal(ModalService.LOGIN_MODAL, { ...new ModalConfiguration(), title: "Sign In" });
}, 200);
}
I have tried setting activeElement in the modal service but it returns focus to the body. Any help would be appreciated ;)
Expose a property in the modal service:
returnFocusElementId: string;
Set it something like
modalService.returnFocusElementId = '#submitButtonId'; // the id of the element which triggers the modal to open
Now change the hide modal something like this:
hideModal() {
this.store.dispatch(new ModalAction.HideModal());
let elementToFocus = document.querySelector(this.returnFocusElementId);
if (elementToFocus != null) {
elementToFocus.focus();
} else if (<HTMLElement>document.activeElement != null) {
(<HTMLElement>document.activeElement).focus();
}
}

Vue screen that refreshes periodically, done safely

I have a page in Vue/Nuxt that needs to refresh a list of items every few seconds. This is an SPA that does an Axios fetch to a server to get updated information. At the moment, I have something like this:
methods: {
doRefresh() {
setTimeout(function() {
// trigger server fetch here
doRefresh();
}, 5000);
}
}
It works, unless the other code in doRefresh throws an error, in which case the refreshing stops, or somehow the code gets called twice, and I get two timers going at the same time.
An alternative is call setInterval() only once. The trouble with that is that it keeps going even after I leave the page. I could store the reference returned by the setInterval(), and then stop it in a destroyed() hook. But again, an error might prevent that from happening.
Is there a safe and reliable way to run a timer on a Vue page, and destroy it when the user leaves the page?
This approach together with try-catch is a way to go, have a look at this snippet:
https://codepen.io/alexbrohshtut/pen/YzXjNeB
<div id="app">
<wrapper/>
</div>
Vue.component("interval-component", {
template: `
<div> {{lastRefreshed}}
<button #click="init">Start</button></div>`,
data() {
return {
timeoutId: undefined,
lastRefreshed: undefined
};
},
methods: {
doJob() {
if (Math.random() > 0.9) throw new Error();
this.lastRefreshed = new Date();
console.log("Job done");
},
init() {
if (this.timeoutId) return;
this.run();
},
run() {
console.log("cycle started");
const vm = this;
this.timeoutId = setTimeout(function() {
try {
vm.doJob();
} catch (e) {
console.log(e);
} finally {
vm.run();
}
}, 2000);
}
},
destroyed() {
clearTimeout(this.timeoutId);
console.log("Destroyed");
}
});
Vue.component("wrapper", {
template: `<div> <button #click="create" v-if="destroyed"> Create</button>
<button v-else #click="destroy">Destroy</button>
<interval-component v-if="!destroyed" /></div>`,
data() {
return {
destroyed: true
};
},
methods: {
destroy() {
this.destroyed = true;
},
create() {
this.destroyed = false;
}
}
});
new Vue({
el: "#app"
});

Stripe js: don't let empty form be sent

I'm trying to avoid letting users submit stripe form when inputs are empty, I`m using stripe.js elements integration to render my form and handle form submition inside my vue component.
this.cardNumberElement.on('change', this.enableForm);
this.cardExpiryElement.on('change', this.enableForm);
this.cardCvcElement.on('change', this.enableForm);
After checking the docs I tried to use the change event on inputs but this is not working sice the user can just not type anything and click submit button.
This is my component:
mounted()
{
console.log(this.$options.name + ' component succesfully mounted');
this.stripe = Stripe(this.stripePK);
this.elements = this.stripe.elements();
this.cardNumberElement = this.elements.create('cardNumber', {style: this.stripeStyles});
this.cardNumberElement.mount('#card-number-element');
this.cardExpiryElement = this.elements.create('cardExpiry', {style: this.stripeStyles});
this.cardExpiryElement.mount('#card-expiry-element');
this.cardCvcElement = this.elements.create('cardCvc', {style: this.stripeStyles});
this.cardCvcElement.mount('#card-cvc-element');
let stripeElements = document.querySelectorAll("#card-number-element, #card-expiry-element, #card-cvc-element");
stripeElements.forEach(el => el.addEventListener('change', this.printStripeFormErrors));
this.cardNumberElement.on('change', this.enableForm);
this.cardExpiryElement.on('change', this.enableForm);
this.cardCvcElement.on('change', this.enableForm);
},
methods:
{
...mapActions('Stripe', ['addSource', 'createSourceAndCustomer']),
...mapMutations('Stripe', ['TOGGLE_PAYMENT_FORM']),
...mapMutations('Loader', ['SET_LOADER', 'SET_LOADER_ID']),
enableForm:function(event){
if(event.complete){
this.disabled = false;
}
else if(event.empty){
this.disabled = true;
}
},
submitStripeForm: function()
{
this.SET_LOADER({ status:1, message: 'Procesando...' });
var self = this;
this.stripe.createSource(this.cardNumberElement).then(function(result) {
if (result.error) {
self.cardErrors = result.error.message;
}
else {
self.stripeSourceHandler(result.source.id);
}
});
},
stripeSourceHandler: function(sourceId)
{
console.log('stripeSourceHandler');
this.cardNumberElement.clear();
this.cardExpiryElement.clear();
this.cardCvcElement.clear();
if(this.customerSources.length == 0)
{
console.log('createSourceAndCustomer');
this.createSourceAndCustomer({ id: sourceId });
}
else
{
console.log('addSource');
this.addSource({ id: sourceId });
}
},
printStripeFormErrors: function(event)
{
if(event.error)
{
self.cardErrors = event.error.message
}
else
{
self.cardErrors = '';
}
}
}
Given the stripe docs, the use of the event seems correct (though it can be improved a bit with using this.disabled = !event.complete to cover error case and not only empty case).
You may try to console.log in the event callback enableForm to check if event is well fired.
Anyway, it's more likely coming from the disabling logic of the submit button and it misses in your post. I've created below a fake secure-component that triggers a change event when value change.
The interesting part in on the container component :
Submit is disabled by default through data disabled,
Submit is enabled if event received has a property complete set to true. If false, it is disabled.
Hope it will help you to focus your trouble.
/**
Mock component to emulate stripes card element behavior with change event
*/
const SecureInput = {
template: '<input type="text" v-model="cardnumber"/>',
data: () => ({
cardnumber: null
}),
watch: {
cardnumber: function(val) {
if(!val) {
this.$emit('change', {empty: true, error: false, complete: false});
return;
}
if(val.length < 5) {
this.$emit('change', {empty: false, error: true, complete: false});
return;
}
this.$emit('change', {empty: false, error: false, complete: true});
}
}
}
/* Logic is here */
const app = new Vue({
el: '#app',
components: {
SecureInput
},
data: {
disabled: true
},
methods: {
updateDisable: function(event) {
this.disabled = !event.complete;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form #submit.prevent="$emit('submitted')">
<p><secure-input #change="updateDisable"/></p>
<p><input type="submit" :disabled="disabled"/></p>
</form>
</div>

Firing a custom event in ExtJS 5.1

I have been trying to fire a custom event when a file has been successfully uploaded using a modal window. A grid on the main page listens for the event and should reload its store when a file is successfully uploaded. Problem is, the grid never catches this event.
I think I have a fundamental misunderstanding of how custom events work. What steps should I take to get back on track?
SomeCommonUtilityClass.js
upload: function(args) {
Ext.create('Ext.window.Window', {
/* form with some controls */
buttons: [{
text:'Upload',
handler: function() {
var win = this.up('window');
var form = this.up('form').getForm();
form.submit ({
url: myAjaxCall,
success: function() {
/* fire event here */
win.fireEvent('uploadSuccess');
},
failure: function() {
/*...*/
}
});
}
},
/* etc. */
});
}
SomeOtherFileView.js
{
xtype:'grid',
itemId:'uploadedGrid',
listeners: {
uploadSuccess: 'reloadUploadStore'
},
bind: {
store:'{form}'
},
columns:[/*...*/]
}
SomeOtherFileViewController.js
reloadUploadStore: function() {
console.log("My event fired!") // Never gets here.
/* .... */
store.load({
params: ({
a: "a",
b: "b"
});
callback: function() {
/* do more stuff */
}
});
}
SomeCommonUtilityClass
win.fireEvent('uploadSuccess');
Example of custom event and Controller that listen on it:
SomeOtherFileViewController
init: function() {
this.listen({
// We are using Controller event domain here
controller: {
// This selector matches any originating Controller
'*': {
uploadSuccess: 'reloadUploadStore'
}
}
});
},
reloadUploadStore: function() {
//your code
}
or if you want pass a argument:
win.fireEvent('uploadSuccess',extraArgument);
Controller code is the same. Only your function definition changes:
reloadUploadStore: function(yourArgument) {
//Do your stuff with extraArgument
}

In extjs, How don't expand in tree on double click

I want my treepanel to do something when double clicked.
But when I double click a treenode, the node always expends or collapses.
How can I disable this expanding or collapsing from happening when I double click.
my english isn't very good
sorry!
You can add toggleOnDblClick: false in the viewConfig when declaring the treepanel, just add viewConfig as any other propriety:
{
xtype: 'treepanel',
id: 'tree_id',
name: 'tree_name',
viewConfig: {
toggleOnDblClick: false
},
width:....
}
yourTree.on('beforeitemdblclick', function() { return false; });
Actually, overriding (Ext.tree.TreeNodeUI.override) is not a good practice (because it changes behavior for all TreeNodeUI's of application), so I propose to override createNode method in TreeLoader of the current tree:
new Ext.tree.TreePanel({
...
loader:new Ext.tree.TreeLoader({
...
// override the CreateNode function
createNode:function (attr) {
attr.uiProvider = Ext.extend(Ext.tree.TreeNodeUI, {
// private
onDblClick:function (e) {
e.preventDefault();
if (this.disabled) {
return;
}
if (this.fireEvent("beforedblclick", this.node, e) !== false) {
// if (this.checkbox) {
// this.toggleCheck();
// }
// if (!this.animating && this.node.isExpandable()) {
// this.node.toggle();
// }
// DO YOUR STAFF HERE
this.fireEvent("dblclick", this.node, e);
}
}
});
return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
}});

Categories