What is a good way for a Angular directive to act as an facade to other elements? What is a good way for a Angular directive to act as an facade to other elements? angularjs angularjs

What is a good way for a Angular directive to act as an facade to other elements?


Possible #6. Dirty queries.

What if we will use directive definition as a query expression for the case when we should switch off default implementation?

You write something like:

  .directive('ngClick', function() {  return {    restrict: 'A',    priority: 100, // higher than default    link: function(scope, element, attr) {      // don't do that magic in other cases      if (element[0].nodeName !== 'X-MENU-ITEM') return;       element.bind('click', function() {        // passthrough attr value to a controller/scope/etc      })      // switch off default implementation based on attr value      delete attr.ngClick;    }  }})

This will switch off default implementation of ng-click at your tags. Same job for ng-hide/ng-show/etc.

Yep, it looks terrible by sense, but result is closer to your idea. Of course it will slow down linking process of compile a bit.


P.S. According to your list I prefer #2 but with custom directive namespace. Something like:

<app-menu-item app-click="..." app-hide="..."/>

And add a convention to docs to use app prefix for all custom things and behaviour. Where app is an abbr of project name usually.


Update: Rewrite based on Comments

This is largely rewritten based on the clarification within the comments. As a pure component-based framework, the idea behind Polymer Elements is give developers a simple way to accomplish two tasks. The first is the ability to make simple Elements with previously un-programmed functionality. The second is the ability to make complex controls with simple custom markup.

This is a considerably different focus than Angular, which consolidates functionality into directives that translate custom markup into native markup and couples the functionality to the translated markup.

Notes on Polymer:

Polymer is primarily a framework polyfill until Web Components, Shadow DOM, Scoped CSS and HTML Imports becomes standardized and implemented. HTML Templates is already implemented pretty well in modern browsers and is worth reading up on. That said, Polymer won't just go away when those are standard, as it provides convenience functions and attributes, as well as a good set of components to start with. The polyfills are considered to be slower than native support, but that is to be expected.

Differences between Polymer and Angular:

Unlike Angular, when you create an element, you are not 'translating' it from one markup-set to another. You are actually defining its functionality and presentation. Even though it may provide additional markup within its Template(s) and Shadow DOM, that additional markup are all functional elements (whether custom or native).

This also means that your elements can have their own CSS Classes and IDs are are handled by the browser allowing for different presentation quite easily. Selector Engines will get the actual custom element and can get the properties and methods associated with them.

Simple Demonstration:

<polymer-element name="x-menu">    <template>        <style>        /* Scoped Style rules */        </style>        <content></content>    </template>    <script>    /* Registers the Element with the browser. */    Polymer('x-menu', {        // Additional Element properties and methods    });    </script></polymer-element><polymer-element name="x-menuitem">    <template>        <style>        /* Scoped style rules */        </style>        <button type="button" class="{{parentElement.classList}}">            <content></content>        </button>    </template>    <script>    /* Registers the element with the browser */    Polymer('x-menuitem', {        // Additional Element properties and methods    });    </script></polymer-element>

Actual Usage

<x-menu class="cool">    <x-menuitem>Open</x-menuitem>    <x-menuitem>Edit</x-menuitem>    <x-menuitem>Create</x-menuitem></x-menu>

When you actually run this, you will see that the button actually copies the classList from the parentElement. In other words: {{parentElement.classList}} is actually shorthand for this.parentElement.classList. Understanding this, you can actually build a number of functions amd properties that are based on parent markup. In contrast, the reverse can be done, as well. You can also use document.querySelector('x-menu') and you will get the <x-menu>.

Additionally, since these are attributes that apply only your own custom element, you need not worry about namespacing the attributes. No other element will understand your attributes, and the browser won't try to do anything funny with them.

Update: Applying Polymer to your Needs

First, and foremost, using your example above, if you have two elements, then you will have two custom elements. How coupled they are depends largely on how they are programmed. It needs be understood that there is only need for native elements within your custom elements if you need multiple types of functionality or presentation. I would recommend, at first, not utilizing native elements except where you need for styling. With Polymer, it is generally best to start small...

For your functional example, there is no real need to have anything other than the content unless you need a) multiple styling blocks or b) to integrate the functionality of another element or component. Since you require click functionality and the ability to focus/style, the most one should add is a button. The rest can easily be handled in CSS.

Getting out of the mindset that a component should handle multiple elements is important, because it is only true when you need multiple elements. A simple element is there to extend the HTML Element set for your needs. Only worry about managing other elements (either custom or native) when you need an advanced component.

Update: Facading Elements

Polymer provides several mechanisms for having an element have multiple presentations or function based on properties. The simplest mechanism is template binding which changes the relevant document fragment based on conditions.

<polymer-element name="x-menuitem">    <template>        <template if="{{condition}}">            <style>Uses this set of styles</style>        </template>        <template if="{{condition2 OR !condition}}">            <style>Use this stylesheet instead</style>        </template>        <content></content>    </template>    <script>Polymer Registration</script></polymer-element>

There may be any number of conditions, and since <template> is just an element, you can actually put any elements in there, including more <template> bindings.

Update: Two examples with Disabled

In the first example, we will simply make a <x-menu-item> with the ability to disable it. We're only going to use the core element, without an <li> or <button>. To disable the item, you may either set the attribute directly (when you mark it up, or get the element via a selector query and set the disabled property.

<polymer-element name="x-menuitem">    <template>        <style>            :host { color:blue; }            :host([disabled]) { color:red; }        </style>        <content><content>    </template>    <script>         Polymer('x-menu-item', {            publish: {                disabled: {                    value: false,                    reflect: true                }            },            method: function() {                if (this.disabled) return;            }        });    </script></polymer-element>

In our second example, we will use an <x-menu-item> with child <li> and <button>. This will have some conditional template binding so that it renders with a different classes. It can still be disabled the same as the above.

<polymer-element name="x-menuitem">    <template>        <style>            :host #item {                /* styles */            }            :host([disabled]) #item {                 /* styles */            }        </style>        <template if="{{disabled}}">            <li id="item" class="disabled_class1">                <button class="disabled_class2">                    <content><content>                </button>            </li>        </template>        <template if="{{!disabled}}">            <li id="item" class="enabled_class1">                <button class="enabled_class2">                    <content><content>                </button>            </li>        </template>    </template>    <script>         Polymer('x-menu-item', {            publish: {                disabled: {                    value: false,                    reflect: true                }            },            method: function() {                if (this.disabled) return;            }        });    </script></polymer-element>

Final Note Updated:

Keep in mind that the above examples are just two declarative ways to accomplish what you need. There are other declarative techniques to accomplish the same goal. Further, you may always utilize the ES/JS to imperatively define everything as well, giving even more options.

There is a lot to Polymer that is matches default browser behavior for HTML Elements. That does not mean there isn't a lot to know. This answer was merely to demonstrate one specific desired functionality of your question. Since the development process and terminology is so different, I wasn't sure what else should be addressed. Whatever might be missing can certainly be added to the answer in updates; simply let me know which specifically you would like added.