Open dagnelies opened 6 years ago
This is an interesting edge case for links. What's happening is the outer context is creating a link, which the inner context is then linking to - a link to a link to some keypath. When the outer context changes the link destination, the inner context can observe the change, but as soon as you link the inner context, the link to the outer link is broken. I'm not sure that it makes sense to be able to change the outer context link from within the inner context.
Since fs symlinks are the go to analogous construct here, is there a way to link to a file, link to that link, and then change the middle link's target by changing the outer link? There could be an option added to link
to do so, but given that this is in a nested context where @keypath
doesn't exactly line up with the source path that may lead to more issues. You can kinda skirt that with a @rootpath
, but that falls apart as soon as there's data mapped in that's not on the root. You can also kinda skirt it by resolving the keypath from a context and giving it an anchor instance (@context.resolve('.', @.parent)
), but at that point you've still locked yourself into a tightly coupled template structure, which may or may not be acceptable.
My solution to this is usually to use a yield
or fire an instance event and let the controlling context handle what happens.
Well, that the two grids link to the same selectedBis
item was to show that it works in the outside -> inside direction. I agree it's an edge case.
However, the much more general case doesn't work either. If you pick a completely different name like <grid selected="{{~/selectedFoo}}" ... />
and want to show it outside the component {{JSON.stringify({{~/selectedFoo}})
, it will not be linked/displayed.
I can work on a smaller / more tailored example tomorrow if you want.
So you're looking for a sort of a reverse link? I'm gonna lean on the symlink example for clarification:
cd /
mkdir list
cd list
touch 0 1 2 3
cd ..
ln -s list /mnt/component/list
# let the component selected point to selectedFoo, which doesn't actually exist at this point
ln -s selectedFoo /mnt/component/selected
# change to the component context
cd /mnt/component
# from component, selected is a symlink pointing at /selectedFoo
ln -s list/0 selected
# now selected is a symlink to /mnt/component/list/0, which is a symlink to /list/0
What you're after is a way to say ln -s /mnt/component/selected /selectedFoo
, which currently doesn't exist as a template construct, however, you could set it up during init e.g. <grid on-init="@.parent.link('selected', 'selectedBis', { instance: @this })">
, which would create the link from the parent into the child (the syntax may not be exactly right, as I haven't tried it, but the gist is there).
Well, you're deeper in this than I. I only have the black box view and can only speak for myself about the behavior I would intuitively expect.
From reading <grid selected="{{selectedFoo}}"
, I would intuitively assume that the selectedFoo
outside the component is the same as the selected
inside the component. As such, I would indeed expect that the link inside the component is propagated outside.
However, links are a bit tricky, because what happens when you set('mylink', ...)
is conceptually unclear. (actually, I have no idea if it overrides the link or updates the linked data).
<grid selected="{{selectedFoo}}"
creates a link in the grid that points selected
to selectedFoo
in the parent instance, so that when selectedFoo
is updated (set
, add
, etc), it notifies selected
and when selected
is updated, it passes the change upstream to its source (selectedFoo
) and lets the change flow back through to the links for the actual update. Calling get
on a link also passes the request to the link's source.
Links are not the same data, they're shadow modesl that manage their own deps, but pass all data access requests through to the source (like a symlink). The reason it's not the same data directly is if the link source changes, there would be no way to determine which deps were supposed to be on the link and which were supposed to be on the source when moving them around. As such, relinking a link just points it at a new source, but doesn't modify the source in any way.
I assumed linked data worked like normal data with some kind of two-way observers ...which they do not. I have to get used to how they "tick" ...but still, it's slightly misleading. A warning would be appropriate when you try to attempt to use them the "wrong" way, like data-binding a link through a component attribute.
There are legitimate uses for linking to a link, but it is something you have to be careful with if you're doing anything with further manual linking rather than just data manipulation. I can see a pretty good cause to add support for creating a reverse link (from parent into child data, as opposed to the current child into parent data), but I haven't come up with a good way to express it yet. I think that would solve both of the cases you presented here. You can accomplish it manually with inline lifecycle hooks on the component, but it's not nearly as pretty as automatic mapping lifecycle management.
Hi,
the example is here:
https://dagnelies.github.io/ractive-examples/editor4panes.html?url=widgets/simple-grid.html
There are two tables. The first is plainly in the template. With an
on-click="@.link(@keypath, 'selectedBis')"
and the other basically the same but wrapped inside a component. You can see that clicking on the first table propagates the binding to the second table (it's the same data), but if you click on the second table wrapped in a component, theon-click="@.link(@keypath, 'selectedBis')"
appears to break the "link".