xcomponent
A cross-domain component toolkit. Useful if you want to build a component which can be rendered into an inline iframe, a
lightboxed iframe, or a popup, and take advantage of the sandboxing provided by iframes alongside the flexibility of passing
props and callbacks instead of url params and post message listeners.
Xcomponent will handle rendering your component, passing down props (including data and callbacks) and transmitting the
callbacks back up to the parent page.
It's 'data-down, actions up', but 100% cross-domain!
MyComponent.render({
someData: {
foo: 'bar'
},
onComplete: function(result) {
console.log('The component called back with a result:', result);
}
}, '#container');
And on the child page:
console.log('We were passed some data from the parent page:', window.xchild.props.someData);
window.xchild.props.onComplete({ hello: 'world' });
Rationale
Writing cross domain components is really hard.
Consider this: I own x.com
, you own y.com
, and I have some functionality I want to put within your page.
I could just give you some javascript to drop in your page. But then:
- What if I write a component in React, and you're using Ember or Angular on your page, or no framework at all?
- What if I have secure data like login credentials that I don't want you to have access to?
- What if I need to make secure calls to apis from my component that I don't want to expose using CORS?
The obvious choice would be to embed the component in an iframe, or a popup window, rather than dropping it straight
onto the parent page. But:
- How should people pass down data? Should they pass everything in the url?
- How should people get events back up? Should I set up a global post message listener, and send fire-and-forget post messages from the child?
- How do I deal with error cases when my component fails, or when messaging fails?
- How do I create a nice, simple interface for my component that people can easily reason about?
xcomponent aims to solve all of these problems, by providing a clean way to build distributable, cross-domain components
that work seamlessly with both iframes and popups. The primary focus of this is to allow you to define your interface,
and then do the heavy lifting in the background, and do all of the things you shouldn't need to think about:
It will even automatically generate React, Angular and Ember bindings so using the component feels even more native.
Example
Let's create a login component. We want the user to be able to log in on our site, and to notify the parent window
that the user has logged in, without exposing any of the users credentials to the parent window.
Take a look at the demos to see this example in action.
As the component creator
First I'd create a spec for the component's interface:
var MyLoginComponent = xcomponent.create({
tag: 'my-login-component',
url: 'http://www.my-site.com/my-login-component',
dimensions: {
width: 400,
height: 200
},
props: {
prefilledEmail: {
type: 'string',
required: false
},
onLogin: {
type: 'function',
required: true
}
}
});
This spec describes everything needed to render the component on the parent page, including the props the component expects.
Now we need to actually implement the business logic of the component -- the code that will run inside the iframe.
I just need to use window.xchild
to get the props that are passed down.
<script src="./my-login-component.js"></script>
<form>
<input id="email" type="text" />
<input id="password" type="password" />
<button id="login">Log In</button>
</form>
<script>
if (window.xchild.props.prefilledEmail) {
document.querySelector('#email').value = window.xchild.props.prefilledEmail;
}
document.querySelector('#login').addEventListener('click', function(event) {
event.preventDefault();
var email = document.querySelector('#email').value;
var password = document.querySelector('#password').value;
jQuery.post('/api/login', { email: email, password: password }, function() {
window.xchild.props.onLogin(email);
});
});
</script>
Now anyone can render the component we defined onto their page!
As the component user
My life is even easier. I just need to drop in your component onto my page:
<script src="./my-login-component.js"></script>
<div id="container"></div>
<script>
MyLoginComponent.render({
prefilledEmail: 'foo@bar.com',
onLogin: function(email) {
console.log('User logged in with email:', email);
}
}, '#container');
</script>
This is even easier if you're using a supported framework like React, Ember or Angular -- xcomponent will automatically
set up bindings for these frameworks:
React / JSX
Drop the component in any render()
method:
render: function() {
return (
<MyLoginComponent.react prefilledEmail='foo@bar.com' onLogin={onLogin} />
);
}
Angular
Specify the component name as a dependency to your angular app:
angular.module('myapp', ['my-login-component'])
Include the tag in one of your templates (don't forget to use dasherized prop names):
<my-login-component prefilled-email="foo@bar.com" on-login="onLogin" />
And we're done! Notice how I never had to write any code to create an iframe, or send post messages? That's all taken care of for you.
When you call this.props.onLogin(email);
it looks like you're just calling a function, but in reality xcomponent
is transparently
turning that callback into a post-message and relaying it to the parent for you.