How i can access to iframe in modal ?
i can't find it in document
as seem your document very poor.
I mean how i can access to modal content like that
https://help.shopify.com/en/api/embedded-apps/embedded-app-sdk/features#modal-and-application-communication
Can you explain more what youāre trying to achieve, in terms of your appās functionality or the user experience?
Hi,
i want i can access to Content in iframe (React js) like https://help.shopify.com/en/api/embedded-apps/embedded-app-sdk/features#modal-and-application-communication
When youāre using Polaris react, you need to provide the Modal componentās src prop with a URL from your own application. Since the URL is a page you control, the Polaris API doesnāt have the same concept of reaching into the modal in the same way as the EASDK.
If you can describe what youāre trying to achieve in more detail, I can probably provide more specific help.
You mean i can't access to iframe when it opened ?
_ShopifyApp.Modal.window().$("body")_
I looked into this some more, and I understand that this is painful right now. Especially with React, itās not ideal to be reaching into the DOM. For this and other reasons weāve decided not to expose the iframe content in Polaris the way the EASDK does. This has made things more difficult in the short term, even for our own developers (we use the EASDK in our own apps too). Weāre exploring ways to make this better right now.
For the time being, I can outline some of the solutions you can try, most of which weāve used ourselves. In these points Iāll talk about the modal iframe (which holds the page you point to with the Modalās src prop) and the embedded iframe (which holds the current page in your embedded app).
src for all modals. When this page is loaded, you can postmessage to the embedded iframe to signal the modal iframeās DOM is loaded. Then, you can use a portal to ātransportā some content from your embedded iframe into the modal. This way the embedded iframe and the modal content can share state.apiKey and shopOrigin to the AppProvider, and instead use the EASDK itself in combination with Polaris React.None of these suggestions is ideal, I know. Please bear with us as we work to make this better, and let me know if you have any more questions or comments.
Oh, sad ;(
I think that postmessage via iftame is the best of way in this case
Thanks for your ideal
Thanks for the workaround ideas @ry5n.
While I can understand why you mightn't want to reach into DOM from a technical perspective, I eventually came to the conclusion it wasn't really worth the effort to work around the limitations.
So I ended up abandoning the EASDK altogether. Which is fine, I guess it did improve the developer experience a bit.
I just wonder whether it runs counter to the goals of a cohesive design system in the first place if in all likelihood the app I am working on will no longer sit within the Shopify admin.
The technical issues aside, I believe stand-alone apps _can_ still benefit from using the same design language so that merchants feel at home. At the same time, non-embedded apps should take care to differentiate themselves (using color, a logo, or the way the application frame is structured) so that merchants are aware theyāre a different product.
Shogun does a good job of this IMO (screenshot).
Thatās not to minimize the challenges weāve discusses in this issue. If you liked the idea of creating an embedded experience, we hate to see you not able to achieve it, and as I said before weāre working on ways to make this much less painful.
Hi Ryan. I totally agree with you, and Shogun is a great example.
I am not abandoning the design language, just the idea that it should be embedded. Not embedding the app provides me with an opportunity to add a bit more brand personality and will make for a possibly nicer experience. Whether that's ideal from Shopify's perspective is a different question though.
No complaints from me in any case, and thanks for sharing the alternative approaches.
Oh, totally. We support both embedded and non-embedded apps, and there are good reasons to go either route, depending on your needs. Branding is one good reason for choosing a stand-alone app.
Whether that's ideal from Shopify's perspective is a different question though
@ptcampbell this is a really great question. The short answer for embedded vs not embedded is whichever you feel is in the best interest of Shopify merchants. If you can deliver a better merchant experience by not embedding I would say go for it.
So I ended up abandoning the EASDK altogether.
This can be okay and other apps do this, but we donāt want embedded apps to rebuild their own app loading state, flash messages, modal, breadcrumbs and other UI features which the EASDK offers. If you need to use any of these UI elements we prefer that is done with the EASDK.
I mean how i can access to modal content
@khiconit back to your original question, you would have to include the app.js in addition to using the Polaris React components to access ShopifyApp.Modal.window() and ShopifyApp.window(). We know this is not ideal and will be working on making all features of the EASDK available via Polaris React.
Here is a workaround as suggested by @ry5n.
I could successfully open a modal from an embedded app using react portals,
A quick guide below, any thoughts and improvements on this solution is highly appreciated:
First, define a global variable to contain your app host:
window.SHOPIFY_APP_HOST = 'xxxxxx.ngrok.io'
Then, define a route in your application (in this case "/modal"), this will handle all your modals.
Render this component when this route is called, something like <Route exact path="/modal" component={Modal} /> :
import React from 'react';
export default class Modal extends React.Component {
componentDidMount(){
window.parent['app-iframe'].postMessage({modalPortal:true},window.SHOPIFY_APP_HOST)
document.body.style.backgroundColor = "#FFF"
}
render(){
return <div id="ModalPortal_Content"></div>
}
}
import React from 'react';
import ReactDOM from 'react-dom'
import { Modal } from '@shopify/polaris';
export default class ModalPortal extends React.Component {
constructor(props){
super(props)
this.messageHandler = this.messageHandler.bind(this)
this.onClose = this.onClose.bind(this)
this.state = {
portalTarget : null
}
}
messageHandler(event){
if(!window.SHOPIFY_APP_HOST || event.origin !== window.SHOPIFY_APP_HOST) return;
if(event.data.hasOwnProperty('modalPortal') && event.data.modalPortal && !this.state.portalTarget && this.props.open){
const portalTarget = event.source.window.document.getElementById('ModalPortal_Content')
if(portalTarget){
this.setState({
portalTarget : portalTarget
})
}
}
}
componentDidMount(){
if (window.addEventListener) {
window.addEventListener("message", this.messageHandler, false);
} else {
window.attachEvent("onmessage", this.messageHandler);
}
}
onClose(){
this.setState({
portalTarget : null,
})
this.props.onClose()
}
render(){
const {onClose,...props} = this.props
return(
<React.Fragment>
<Modal {...props} onClose={this.onClose} src={window.SHOPIFY_APP_HOST+"/modal"}>
</Modal>
{(this.props.open && this.state.portalTarget) &&
<ModalPortalRemote target={this.state.portalTarget}>
{this.props.children}
</ModalPortalRemote>
}
</React.Fragment>
)
}
}
class ModalPortalRemote extends React.Component {
render(){
return ReactDOM.createPortal(
this.props.children,
this.props.target
);
}
}
<ModalPortal
open={active}
onClose={this.handleChange}
title="Reach more shoppers with Instagram product tags"
primaryAction={{
content: 'Add Instagram',
onAction: this.handleChange,
}}
secondaryActions={[
{
content: 'Learn more',
onAction: this.handleChange,
},
]}
>
<Modal.Section>
<TextContainer>
<p>
Use Instagram posts to share your products with millions of
people. Let shoppers buy from your store without leaving
Instagram.
</p>
</TextContainer>
</Modal.Section>
</ModalPortal>
@rmtngh stellar work! Will have to give it a try.
@tmlayton I agree with your points and I'm actually experimenting with offering both an embedded vs. standalone experience. Will have to see how that goes. š
@ptcampbell Iām very interested to hear what you discover.
Maybe there could be an option to display it within the iframe of the embedded app if the developer doesn't want to use the src attribute on the embedded modal. This way we could use children in the component and not have to worry about a complex work-around.
In my experience stuff that breaks the standard way of doing things can be a real pain to maintain further down the road when you've forgotten how the complex thing that you did as a work-around functions. The way that it works now seems really opinionated and not at all intuitive.
I've seen several related issue reports when trying to research this and it seems a lot of people are running into the same issue repeatedly.
I like that there is a way to completely take over the screen like a native part of Shopify but I just wish there was an option for apps that don't really need that capability.
Personally, I'm going to create the functionality I want, without a nice looking modal, just to make this easier to maintain.
Oh, sad ;(
I think that postmessage via iftame is the best of way in this case
Thanks for your ideal
What you are trying to do is communicate between child and parent component. This is more of a ReactJS question because you want to for example display a form in the modal right? If so then you need to pass a function as a prop. You can use the primaryAction prop that's built into the Modal component, it's meant for this very reason, at least the only real option for an embedded app.
Thanks everyone for your participation in this issue. Closing for now as the original question has been answered. As of v5.0.0 we will be removing App Bridge from Polaris and moving those components to their own @shopify/app-bridge-react library.
More information will come with docs, etc, but for those immediately curious read https://github.com/Shopify/polaris-react/issues/814#issuecomment-493249756