ef4/ember-elsewhere

Block form of to-elsewhere?

yankeeinlondon opened this issue ยท 23 comments

I find myself wanting to use ember-elsewhere in a situation where I have some inline content that I want to "transport" to a sidebar. I could make that content a component but the sidebar and main content are so contextually tied I'm not really super keen on doing that.

Is it possible to have an block component version of to-elsewhere:

{{#to-elsewhere named='sidebar'}}
   lorem ipsum sidebar
{{/to-elsewhere}}

lorem ipsum main content window

ef4 commented

Yes, I would like to do this. Right now it requires messing with ember internals, which would make this addon fragile.

There is definitely interest in adding a block-capture feature to Ember itself that would make this possible.

I could finally deprecate https://github.com/knownasilya/ember-named-yields if this was added.

I would really love to see this as well. It would also allow to be a real replacement for ember-wormhole (which is failing in a number of cases due of its tight coupling to private APIs) for many addons that still have to use it for things like modals.

@ef4 is there already an RFC out there that would enable this?

From my naive understanding, it is not so much related to the few named block/yield proposals, but more to being able to assign a block template to a component property (send in this case) that can be rendered somewhere else (not within the to-elsewhere), so emberjs/rfcs#203 would enable that, but seems to have been dropped.

any movement on this?

ef4 commented

The most relevant work is https://github.com/emberjs/rfcs/blob/master/text/0226-named-blocks.md.

The RFC is merged, and I'm pretty sure the implementation is already working in glimmer-vm. I don't know what the next step is to turn it on in ember.

cbou commented

Is there an alternative way to achieve this? ember-wormhole is not an option for me because I want to test if the a {{to-elsewhere}} is declare.

e.g.:

{{#from-elsewhere name="page-title" as |title|}}
  {{if title title model.name}}
{{/from-elsewhere}}

is there any update on whether this is possible or could be added as a feature?

No movement yet. You can see if movement happens with named blocks here: emberjs/rfc-tracking#44

I think we can totally do this with AST transforms.

Transform from this.

// Component.hbs
{{#to-elsewhere named="my-right-sidebar"}}
  {{this.model}}
{{/to-elsewhere}}

To this

// Component.hbs
{{to-elsewhere named="my-right-sidebar" send=(component "Component$1" model=this.model)}}

// Component$1.hbs
{{this.model}}}
ef4 commented

I think this could work, but it requires some careful handling of all the names in scope.

ef4 commented

Also we need to figure out what to do if we encounter special forms like {{yield}} and {{outlet}}. I suppose they can become static errors.

Any thought to using the now public {{in-element}} helper?

ef4 commented

This addon could be refactored to use in-element, but it would need to carefully manage the timing between the two sides and only call in-element after creating a destination element for it.

from-elsewhere would render a list of elements corresponding to each block that it wants to contain. to-elsewhere would wait to hear which element is waiting for it and then render with in-element.

It all sounds like it could work and if somebody is up for trying it that would be great.

basz commented

aware of ember-stargate? seem to do excatly this...

I am aware of ember-stargate and would have preferred not to change addons which is why Im asking for this addon to be upgraded, unless the preferred course is to deprecate this addon in favor or that one.

ef4 commented

I am open to deprecating in favor of ember-stargate. We could give people an easy upgrade by updating this addon to use stargate under the hood. It would make ember-elsewhere into a tiny compatibility layer.

ef4 commented

(it's not like I need more addons to maintain! ๐Ÿ˜‚)

on ember-observer elsewhere doesnt say how far back the ember version is supported, stargate says 3.16 and is also unrated at this time. Probably pre mature to layer on top of that without more research.

I guess I have to make a demo app and play with stargate a bit

Chiming in here as the author of ember-stargate... so the reason I started this addon was indeed to replace ember-elsewhere, because of its lack of support of block form, and not being able to pass HTML attributes to the obligatory component helper. (otherwise a great piece of software, especially as these limitations have their origin upstream in Ember!)

The reasons for not even trying to create a PR here instead of building something new:

  • I wanted to keep it really simple and lightweight, without the "burden" of an existing (pre-Octane) codebase and caring about backwards compatibilty (took me literally just a few hours to build and publish)
  • I don't think it's necessarily the "better" approach, especially as not all use cases of ember-elsewhere are supported (see below), so having some competition of concepts seems healthy ๐Ÿ˜€
  • passing arbitrary values to the target is not supported in stargate, same as a block-form of from-elsewhere, so use cases like passing additional state or wrapping in liquid-fire are not possible

The concept of a compatibility layer sounds interesting, but not sure how feasible that would be given the limitations mentioned above!?

I think the real issue with the supported feature is not the passing of additional state but that the {{in-element}} target does not support a block form so can not support a block form of {{from-elsewhere}}.

Basically {{to-elsewhere}} doesn't support a block form, but {{from-elsewhere}} does, if we do {{in-element}} then {{from-elsewhere}} can only support a block form when a component is sent ( {{to-elsewhere}} non block), but not support a from block if a to block is defined. You only get the block form on one side or the other.

I see a way forward as supporting what you currently support, but if you use a block form of {{to-elsewhere}} the block form of {{from-elsewhere}} would not be used. The liquid fire stuff in {{from-elesewhere}} can be accomplished by a helper component that you use in the {{to-elsewhere}}

That being said, eventually the end result would be something like ember-stargate with one exception, it would care about backward compatibility. While I understand your want to a start clean, a few changes to stargate could support father back than 3.16. As an ember community we have an obligation not to leave people behind, and since the polyfill for {{in-element}} goes back to 2.12, I think we owe the community and {{in-element}} addon that supports as far back as we can get.

So @simonihmig if you were willing to change the addon to get more backward compatibility I would gladly help you do so, if you wish to keep the clean post octane view, @ef4 Ill take a stab at changing this addon to support a block form for {{to-elsewhere}} but the {{from-elsewhere}} target will ignore its block if its defined. I will still support of block in the {{from-elsewhere}} as long as the {{to-elsewhere}} does not have a block for maximum compatibility with existing usage.

Well, I actually got it working, completely, supporting block form on from-elsewhere and to-elsewhere (had a flash of insight).

multiple-from-elsewhere does not support block-form of to-elsewhere because the multiple-from-elsewhere is trying to do the rendering, however now from-elsewhere does support append=true (multiple content) but ordering on to-elsewhere is not supported because {{in-element}} does not support ordering.

All in all though, look for a PR once I get the docs updated. FYI it supports back to 2.12.2

So yes it works, all the tests have been passing, but when it comes to the demo app there are a few edge cases.

I would assume that with in-element, the send component version would be deprecated and eventually removed, you dont need to send a component, just put the component in the block form, but I wanted to support it for that transition period.

With elsewhere, the target (from) is doing the rendering and the source (to) is just registering a component to be shown. This gives all the control to the target, and there are samples showing animation. With in-element, the target is just an element, the source pushes the DOM to the target. So no animation at the target. I assume that you might be able to do animation from the source, but this is probably a breaking change.

If a component isnt sent, I substitute an element target that allows you to still do the below and specify the target in the middle of your supplied HTML

{{#from-elsewhere name="modal" as |modal outsideParams|}}
  {{#liquid-bind modal as |currentModal|}}
    <div class="modal-container">
      <div class="modal-background" onclick={{action outsideParams.onOutsideClick}}></div>
      <div class="modal-dialog" >
        <div class="modal-title">{{outsideParams.title}}</div>
        {{component currentModal}}
      </div>
    </div>
  {{/liquid-bind}}
{{/from-elsewhere}}

Animations dont work because in-element pushes DOM into the target bypassing liquid-bind and it makes current modal always true even there currently isnt a source, so the action becomes a problem since its undefined.. I can come up with a few ways around it, but they all are breaking changes, not so much with the addon itself, but with the example usages provided, which I assume a lot of people have followed.

Its a point of view, but I always thought that from and to were named backwards, so if theres going to be a breaking change, maybe just creating two new components that support block form may be the way to go. But at that point you could just use ember-stargate. My one app is at 3.18 so no problem, but my other app is at 2.18. In-element works back that far, I want it for both apps, but stargate has that min 3.16 limitation.

I guess whats the way forward here. What are consumers of this addon willing to do

  1. Use ember-stargate, if your at the 3.16 mark, else not an option
  2. Write another addon like stargate, but support 2.12 (2.18 would be better if acceptable),
  3. Two new components in elsewhere that support the block form of the source and block form of the target with some limitations (how to handle animation, may be doable from source, just havent tried)
  4. Make from and to elsewhere support block but with breaking changes that will require you to modify your apps source and animations will have to be reworked.

Option 3 seems the least intrusive, you can convert from one usage to another without adding another addon, it works back to 2.12 (would like to up this to 2.18, makes the internal code a little cleaner), no breaking changes with the current usage.