Jeffrey Roosendaal
Jeffrey Roosendaal

Reputation: 7157

How to properly use the same AngularJS 1.5 component multiple times in a view?

I'm creating a set of widgets with AngularJS 1.5's new components. The problem is, when using the same widget multiple times, they somehow share their controller or scope. I thought one of the things about components was that their scope is completely isolated?


My main html template which hold the widgets:

<widget-list
    title="Books"
    class="col-xs-12 col-md-4">
</widget-list>

<widget-list
    title="Movies"
    class="col-xs-12 col-md-4">
</widget-list>

<widget-list
    title="Albums"
    class="col-xs-12 col-md-4">
</widget-list>

My widget template:

<div class="widget widget-list">
    <div class="panel b-a">

        <div class="panel-heading b-b b-light">
            <h5>{{$widget.title}}</h5>
            <div class="pull-right">
                <button type="button" class="btn btn-default btn-sm" ng-click="$widget.doSomething()">
                    Do something
                </button>
            </div>
        </div>

        <div class="panel-content">
            {{$widget.content || 'No content'}}
        </div>

    </div>
</div>

My widget component:

app.component('widgetList', {
    templateUrl: 'template/widget/widget-list.html',
    bindings: {
        title   : '@',
    },
    controllerAs: '$widget',
    controller: function($timeout) {

        $widget = this;

        console.log('Title on init: ', $widget.title)

        $timeout(function() {
            console.log('Title after 3 seconds: ', $widget.title)
        }, 3000)

        $widget.doSomething = function() {
            $widget.content = "Something";
        }
    }
});

When running my code, this is what my console looks like:

Title on init: Books
Title on init: Movies
Title on init: Albums
(3) Title after 3 seconds: Albums

Also after rendering, all three widgets display No content in their template. But, when clicking the doSomething() button in either one of the three widgets, only the content of the last widget updates to Something.

What is happening here? Why are my components not 'isolated'?

Upvotes: 0

Views: 1423

Answers (2)

lenilsondc
lenilsondc

Reputation: 9800

The problem with your code is that you are declaring the $widget on window scope, that's why your controller prints the last value, bacause it was being overridden every time the controller was getting instantiated. Use a var $widget instead and your code will work fine.

The following snippet solves this issue:

angular.module('app', [])
  .component('widgetList', {
    templateUrl: 'template/widget/widget-list.html',
    bindings: {
      title: '@',
    },
    controllerAs: '$widget',
    controller: WidgetListController
  });

function WidgetListController($timeout) {

  var $widget = this;

  console.log('Title on init: ', $widget.title)

  $timeout(function() {
    console.log('Title after 3 seconds: ', $widget.title)
  }, 3000)

  $widget.doSomething = function() {
    $widget.content = "Something";
  }
}

angular.element(document).ready(function() {
  angular.bootstrap(document, ['app']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.min.js"></script>
<widget-list title="Books" class="col-xs-12 col-md-4">
</widget-list>

<widget-list title="Movies" class="col-xs-12 col-md-4">
</widget-list>

<widget-list title="Albums" class="col-xs-12 col-md-4">
</widget-list>

<script type="text/ng-template" id="template/widget/widget-list.html">
  <div class="widget widget-list">
    <div class="panel b-a">

      <div class="panel-heading b-b b-light">
        <h5>{{$widget.title}}</h5>
        <div class="pull-right">
          <button type="button" class="btn btn-default btn-sm" ng-click="$widget.doSomething()">
            Do something
          </button>
        </div>
      </div>

      <div class="panel-content">
        {{$widget.content || 'No content'}}
      </div>

    </div>
  </div>
</script>

Upvotes: 2

janusz
janusz

Reputation: 848

Looks like you have a global variable called $widget here, try this:

var $widget = this;

instead of

$widget = this;

It creates a mess since the $widget variable holds a reference to the controller that has been recently initialized, in this case to the controller of the third component.

Upvotes: 3

Related Questions