Search code examples
angularjsangularjs-directiveangularjs-scopeangularjs-components

Generic AngularJS component


I am trying to make a generic component to display variables and maybe call methods from its scope.

It felt quite natural to write <component config-name></component>, so I got stuck with the idea of filling the generic component's scope with variables from an attribute directive.

This directive would provide variables to display, methods to call, etc.

I ended up with something like this:

var app = angular.module('app', []);

app.component('component', {
  template: '<li>{{data}}</li>'
});

app.directive('dirA', function() {
  return {
    restrict: 'A',
    controller: function($scope) {
      $scope.data = 'First directive!';
    }
  };
});

app.directive('dirB', function() {
  return {
    restrict: 'A',
    controller: function($scope) {
      $scope.data = 'Second directive!';
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.min.js"></script>

<h2>Expectations</h2>
<ul>
  <li>First directive!</li>
  <li>Second directive!</li>
</ul>

<h2>Reality</h2>
<ul ng-app="app">
  <component dir-a></component>
  <component dir-b></component>
</ul>

However, the component's scope stays empty. How could I make the directives fill the scope of their component element?

Is this even the AngularJS way of doing things? If not, what would be the best way of doing something similar?


Solution

  • You are doing it wrong, a component is more like a directive but also like a replacement for the directive beginning with angular 1.5.

    Note: A component is just an improved version of directive so you don't pass directives into components as attributes. To pass values to a component, use bindings:

    angular.module('myApp')
        .component('component', {
            templateUrl: 'component.html',
            controller: function ComponentCtrl(){
                this.innerProp = "inner";  //Tied to controller scope
            },
            controllerAs: 'vm',   // Replaces scope, by default components use `$ctrl`
            bindings: {
               input: '=?'
            }
    });
    

    In component.html you can do:

    <div>
        {{vm.innerProp}} /*Scope variable from controller*/
        {{vm.input}} /*Attribute from bindings*/
    </div>
    

    And in a parent component/template you can do:

    <component input="someModel"></component>
    

    The value passed to input in the parent template will be passed to component.html and replace vm.input