erikringsmuth / app-router

Router for Web Components
https://erikringsmuth.github.io/app-router/
MIT License
610 stars 83 forks source link

[Feature] Allow setting element attributes on <app-route> tag #34

Open nsatragno opened 9 years ago

nsatragno commented 9 years ago

It would be really cool if we could set element attributes on the <app-route> tag, like this:

<app-route path="/post/first" import="components/x-post.html" element-attributes="post-id: 1"></app-route>
<app-route path="/post/latest" import="components/x-post.html" element-attributes="post-id: 10"></app-route>

components/x-post.html:

<polymer-element name="x-post" attributes="post-id">

Besides being a good feature on its own, this would make working with the new core-animated-pages integration much nicer if animations in which relative section position matters are used, such as slide-from-right.

erikringsmuth commented 9 years ago

This is a tricky one that I've tried to come up with a solution to before.

Here's a partial workaround.

<app-route path="/post/latest" import="components/x-post.html" element-attributes="post-id: 10"></app-route>

This could be written like this with the current API.

<app-route path="/post/latest" import="components/x-post.html">
  <template><x-post post-id="10"></x-post></template>
</app-route>

The problem is other path variables would never be passed through (post-num in this example).

<app-route path="/post/:post-num" import="components/x-post.html">
  <template><x-post post-id="10" post-num="???"></x-post></template>
</app-route>

The tricky part becomes the syntax. How would you pass multiple attributes? How would you determine the type of the attribute? In polymer you can even bind JS objects as attributes.

<app-route path="/post/latest" import="components/x-post.html" element-attributes="post-id: 10, other-attr=hello world, yet-another={'property': '123'}"></app-route>

or separate them out like this.

<app-route path="/post/latest" import="components/x-post.html" post-id="10" other-attr="hello world" yet-another="{'property': '123'}"></app-route>

But then how do you know which attributes to pass, or just pass all attributes...

In short, it's tricky and I haven't come up with a simple solution.

nsatragno commented 9 years ago

Thanks for looking into this. What if you used plain old JSON and relied on the browser's native parser to infer types?

<app-route [...] element-attributes="{post-id: 5, some-other-attribute: {some-arg: 'string', some-array: ['1', '2']}}"></app-route>

Arguments that are on the element-attributes tag should probably be overwritten by query or route parameters if these do come with the request.

This would be somewhat similar to what Polymer does to let you to pass objects and arrays through attributes. Granted, the syntax is certainly not the prettiest, but it would still be a very useful feature.

What do you think?

erikringsmuth commented 9 years ago

Polymer actually has an entire library dedicated to data binding called Node.bind() https://www.polymer-project.org/docs/polymer/node_bind.html.

In reality the router doesn't do proper data binding. All it does is parse the URL and calls element.setAttribute('attr', value).

Node.bind() goes a lot further.

Basically there's a lot of stuff going on there that the router is ignoring because it's only dealing with the URL. Once you dive into real data binding it gets complicated quick!

nsatragno commented 9 years ago

Wow, the actual issue is much more complex than I had expected. The router doesn't use Node.bind() because it also has to work without Polymer, right? In this case, maybe it's worth adding it as a dependency?

I haven't delved deeply into WebComponents, so perhaps I'm missing important details.

In any case, I really appreciate the fact you are being so open to discussing your project. Cheers!

erikringsmuth commented 9 years ago

Yeah, right now there are no external dependencies. I've considered the possibility of making a Polymer specific router but I really don't want to maintain two forks. With Node.bind() there's still the issue of determining which attributes to bind. Maybe something like this...

<app-route path="/customer/:customerId" element="customer-pages" bind-attributes="one two three" one="{{myValue}}" two="test string" three="{{objectReference}}"></app-route>
erikringsmuth commented 9 years ago

I'm adding this feature as a follow up to another PR https://github.com/erikringsmuth/app-router/issues/45. I'm hoping to get it released in v2.1.0 this weekend.

It'd be a partial workaround for this issue. You could achieve the same thing but with JS instead of HTML. It's not quite as clean but it's a start.

erikringsmuth commented 9 years ago

2.1.0 is up. Take a look at the before-data-binding event. This might get you close enough. https://erikringsmuth.github.io/app-router/#/events#before-data-binding

nsatragno commented 9 years ago

That's great! After merging #47, it's the perfect solution for us as we build the router with JS.

itsMattShull commented 7 years ago

@nicolassatragno @erikringsmuth How would you use before-data-binding to have it set the attributes to app-route? I'm trying to do data-binding with native web components. I can get the partial to appear but the path variables aren't being tied to anything. Would using before-data-binding help me set those attributes on app-route or something where I can access the attributes?