Closed yusefnapora closed 8 years ago
The last couple of commits add Android support and limit the scope of the require
rename hack to just the VirtualDom.js
file in the virtual-dom
package. That seems much less invasive than altering the compiled elm.js
.
There's still some cleanup stuff to be done; the Native.ReactNative
module is actually not used anymore, so it could be removed. And I think that the init
port can be removed as well... might not have time to work on this during the week though.
Realized over lunch that using VirtualDom.Node
as the output type means you can use StartApp
directly :smiley:
man, this is too fun :)
I pulled elm-effects
into the mix with the last commit and implemented the random gif viewer from the elm architecture tutorial.
I had to add the addEventListener
method to the XMLHttpRequest
prototype to get elm-http
to work, but now you can request a random cat gif from your phone with elm!
The XHR shim is incomplete; it doesn't handle timeout
or progress
events. Also, I still haven't figured out a clean solution for local images, since they need to be statically require
'd for the react packager to pick them up. So there's no loading spinner.
I'm thinking for bundled images (and custom javascript components, etc) you'll probably have to require
them in javascript, maybe in an ElmExports
module or something. Then you could have a Native-backed bundledImage
function that looks things up in that module, given a string key. vdomToReactElement
could look there too, but you'd have to somehow indicate that you want to look in there instead of in the React
module.
Maybe the hypothetical ElmExports
object could always have a React
member, so vtreeToReactElement
could look up all component classes in there using something like lodash's get
function, which will look up nested object paths, e.g, get(someObject, "foo.bar.baz")
.
Basically I'm thinking it'd look like this:
export default const ElmExports = {
React: require('react-native'),
AppComponents: {
MySpecialComponent: require('./components/MySpecialComponent')
},
Images: {
'foo.png': require('./images/foo.png'),
'bar.gif': require('./images/bar.gif'),
}
};
Then from elm, components that are included in React could be defined with e.g.
image : List Property -> List Node -> Node
image =
node "React.Image"
and you could get to MySpecialComponent
with
special : List RN.Property -> List RN.Node -> Node
special =
RN.node "AppComponents.MySpecialComponent"
then when we map the vdom nodes to react elements you'd just do
let componentClass = _.get(ElmExports, vdomNode.tagName);
return React.createElement(componentClass, props, children);
getting the bundled images out might require a Native-backed elm function... not sure yet.
Wow, @yusefnapora, you've really put some effort into this! I really appreciate the work you've done here.
I think I will go with the modified compiler thing however, since that's what Evan suggested when I talked with him. Most of your work should be very easily adaptable to that solution, though, since it would also use the main
directly. So if you don't mind, I will pursue that goal for now.
I would love it if you can take your work on the modified compiler main
version -- there's a lot of great stuff on here!
Thanks! I think the modified compiler + elm-core route is probably the way to go for the long term. I'm not crazy about the hacks involved in this branch, and using virtual-dom kind of feels like lying to the compiler :) I'll start experimenting with that and see if I can come up with a sensible rendering method for VTree
.
I'm going to close this and keep chipping away at helping implement #24. I'll leave the branch up on my fork for reference if anyone wants to check it out.
Hey, I had some time today to play with this issue I opened yesterday, and managed to get things working. It involves quite a few nasty hacks, but comes with some nice benefits :)
The big change is the replacement of the
VTree
type withVirtualDom.Node
, which is aliased asReactNative.Node
. This allows you to return a signal ofReactNative.Node
frommain
and avoid theports
hack. Since we don't have to go through ports, you can attach properties that are not directly json encodable, so you can attach event handlers directly as properties, using the same technique as inelm-html
.Styles are also attached as a
style
object property, rather than being special-cased.Now on to the hacks :)
First, including the
virtual-dom
package causes the React Native packager to fail, sincevirtual-dom
is built with browserify, which defines arequire
function. The packager overrides this with its ownrequire
implementation and gets confused. So I added a perl one-liner to thepostcompile
npm run script to replacerequire
with_browserify_require
in the compiledelm.js
.Next, to actually get
main
to render, I defined a super barebonesglobal.document
object, so thatElm.fullscreen
won't fail with a bunch of undefined references.In the
componentWillMount
hook of the mainAppWrapper
component, I'm patchingElm.Native.VirtualDom.make
to override therender
andupdateAndReplace
functions with an implementation that just callssetState
on theAppWrapper
instance, sending in the new root virtual-dom node.After patching the runtime,
componentWillMount
callsElm.fullscreen
.Then a
vdomToReactElement
function inAppWrapper.render
recursively converts from virtual-dom node to react element, just likevtreeToReactElement
did before.There's still plenty of ways this could be improved, but at least now we know it's possible :)
Hopefully I'll have time to pull the rendering hacks out into their own module and wire up Android support tomorrow.