angular js directives

Directives are often mentioned as one of the "3 Ds" of angular (the other 2 being Data Binding and Dependency Injection). So Directives are a very important part of angular.
Yet at the same time, mention of Directives can cause even experienced angular developers to acquire a haunted look (think Frodo and the Dark Riders and you have the idea ;-) )

A classic case for a directive is creating a reusable element for displaying customer information. It's part of the angular directive tutorial docs and what I implemented for our site since we need to display the same contact info in multiple places.

Steps to create:
  1. create the directive js
  2. create the directive html partial (optional but if have html then make partial)
  3. add directive name to app list of dependencies
  4. include the js file
  5. create the test
  6. use in your app

Attribute vs Element
There are 4 ways to include directives but the most common are as attribute or element as defined by the restrict setting:
  1.  'E' - as element only
  2.  'A' - attribute only
  3.  'AE' - either
When to choose element vs attribute?
The docs say: When should I use an attribute versus an element? Use an element when you are creating a component that is in control of the template. The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.
Building a contact info is a good case for element. But say adding custom behavior to the input directive may be a good case for attribute e.g. like the required directive.

Scope
Scope is a big deal in angular and so too for Directives. The Directive scope options are:
  1. false - reuse scope from where widget is defined
  2. true  - create a new child scope which inherits prototypically from scope widget is used
  3. {}    - create isolated scope, does not prototypically inherit;  can still access parent scope via $parent but thats bad practice;  within isolated scope you can pass params as attributes with 3 mapping choices:
    • @name - 1 way binding;  changes to property name flow from parent to directive but any changes in directive do not show in parent. Only meant for strings, not complex objects.
      • you can't pass on object with @....but see & to pass object
        •  
    •  =name - 2 way binding, changes  to property name flow from parent to directive and directive to parent (e.g. if you want to pass an object such as a contact with properties such as name, title, phone etc)
      • if name was shared across directives who all define as =name then a change to one will change for all
    • &name - expression function callback; pass something which gets wrapped in a fn to a directive; you can pass a fn or an object 
      • I want to be able to call  existing function "downloadTheDocument" which is on scope from my-directive with local scope
        1. <my-directive download-doc="downloadTheDocument">  
        2. directive scope:    scope: { downloadDoc:'&'  }
        3. from directive controller: $scope.downloadDoc()(documentId)
        4. notes: step 3 directive param "download-doc" and step 4 double parens
        5. more from Dan Wahlin here
      • "'&' evaluates an expression to a function accessible on the directive's scope, but the expression is evaluated relative to the _parents's_ scope, so it doesn't have access to the parent's scope property;"
      • You can also use & to pass an object!!! as follows: to pass a complex object with one way binding you could use & i.e.: see good example here   some notes on my use
        • html: <a hover-tip="this is a tip">
        • directive scope: scope: {  getHoverTip: '&hoverTip'  }    Important &hoverTip must match the directive hover-tip (with angulars wierd case rules)
        • directive code: scope.getHoverTip()  will return "this is a tip"
        • you could also pass an object e.g. <a hover-tip="{val1: 55, val2:99}"> and get that object using  scope.getHoverTip().val2
    • Interesting comment from forums: " if data binding (=) is not required, then '@' or '&' are the way to go, philosophically."
Option 3, create isolated scope is preferred to get most reusability from a Directive.
the isolate scope of the directive isolates everything except models that you've explicitly added to the scope: {} hash object. This is helpful when building reusable components because it prevents a component from changing your model state except for the models that you explicitly pass in.

Angular docs say that the & fn callback is a way to provide an api  from the directive. This feels kinds backwards to me, but I see how it allows the directive to call out by providing some hooks for callback effectively.
Interestingly directives can have controllers which can provide apis too, but in this case as a means to truly call into the directives controller. See the last example on the angular directive docs the tabbed panes.

Plus we should not forget tranclusion which flips the scope on a directive...more laters....



replace: true/false - replace element or not with directive contents


FYI Here's my final directive in this gist which also includes a handler to trigger on click

Comments

Popular posts from this blog

deep dive into Material UI TextField built by mui

angular js protractor e2e cheatsheet

react-router v6.4+ loaders, actions, forms and more