app-router icon indicating copy to clipboard operation
app-router copied to clipboard

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

Open nsatragno opened this issue 11 years ago • 9 comments

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.

nsatragno avatar Nov 05 '14 19:11 nsatragno

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.

erikringsmuth avatar Nov 05 '14 22:11 erikringsmuth

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?

nsatragno avatar Nov 05 '14 23:11 nsatragno

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.

  • HTML attributes are actually only strings.
  • A DOM object's properties are JavaScript and can accept any type of object including references to other objects.
  • When you do something like this <x-input value="{{name}}"></x-input> you are actually binding to the value property, not the value attribute. It only binds to the attribute if you publish the property as "reflected" in Polymer.
  • If you want to bind a full object, the Polymer syntax let's you bind a reference rather than passing a JSON serialized version of the object.

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!

erikringsmuth avatar Nov 05 '14 23:11 erikringsmuth

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!

nsatragno avatar Nov 06 '14 21:11 nsatragno

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 avatar Nov 07 '14 02:11 erikringsmuth

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 avatar Dec 05 '14 16:12 erikringsmuth

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

erikringsmuth avatar Dec 06 '14 17:12 erikringsmuth

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

nsatragno avatar Dec 15 '14 01:12 nsatragno

@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?

itsMattShull avatar Nov 22 '16 19:11 itsMattShull