Improve this Doc  View Source

ngRef

  1. - directive in module ng

Overview

The ngRef attribute tells AngularJS to assign the controller of a component (or a directive) to the given property in the current scope. It is also possible to add the jqlite-wrapped DOM element to the scope.

If the element with ngRef is destroyed null is assigned to the property.

Note that if you want to assign from a child into the parent scope, you must initialize the target property on the parent scope, otherwise ngRef will assign on the child scope. This commonly happens when assigning elements or components wrapped in ngIf or ngRepeat. See the second example below.

Directive Info

Usage

Arguments

Param Type Details
ngRef string

property name - A valid AngularJS expression identifier to which the controller or jqlite-wrapped DOM element will be bound.

ngRefRead
(optional)
string

read value - The name of a directive (or component) on this element, or the special string $element. If a name is provided, ngRef will assign the matching controller. If $element is provided, the element itself is assigned (even if a controller is available).

Examples

Simple toggle

This example shows how the controller of the component toggle is reused in the template through the scope to use its logic.

<my-toggle ng-ref="myToggle"></my-toggle>
<button ng-click="myToggle.toggle()">Toggle</button>
<div ng-show="myToggle.isOpen()">
  You are using a component in the same template to show it.
</div>
angular.module('myApp', [])
.component('myToggle', {
  controller: function ToggleController() {
    var opened = false;
    this.isOpen = function() { return opened; };
    this.toggle = function() { opened = !opened; };
  }
});
it('should publish the toggle into the scope', function() {
  var toggle = element(by.buttonText('Toggle'));
  expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false);
  toggle.click();
  expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true);
});

ngRef inside scopes

This example shows how ngRef works with child scopes. The ngRepeat-ed myWrapper components are assigned to the scope of myRoot, because the toggles property has been initialized. The repeated myToggle components are published to the child scopes created by ngRepeat. ngIf behaves similarly - the assignment of myToggle happens in the ngIf child scope, because the target property has not been initialized on the myRoot component controller.

<my-root></my-root>
angular.module('myApp', [])
.component('myRoot', {
  templateUrl: 'root.html',
  controller: function() {
    this.wrappers = []; // initialize the array so that the wrappers are assigned into the parent scope
  }
})
.component('myToggle', {
  template: '<strong>myToggle</strong><button ng-click="$ctrl.toggle()" ng-transclude></button>',
  transclude: true,
  controller: function ToggleController() {
    var opened = false;
    this.isOpen = function() { return opened; };
    this.toggle = function() { opened = !opened; };
  }
})
.component('myWrapper', {
  transclude: true,
  template: '<strong>myWrapper</strong>' +
    '<div>ngRepeatToggle.isOpen(): {{$ctrl.ngRepeatToggle.isOpen() | json}}</div>' +
    '<my-toggle ng-ref="$ctrl.ngRepeatToggle"><ng-transclude></ng-transclude></my-toggle>'
});
<strong>myRoot</strong>
<my-toggle ng-ref="$ctrl.outerToggle">Outer Toggle</my-toggle>
<div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
<div><em>wrappers assigned to root</em><br>
<div ng-repeat="wrapper in $ctrl.wrappers">
  wrapper.ngRepeatToggle.isOpen(): {{wrapper.ngRepeatToggle.isOpen() | json}}
</div>

<ul>
  <li ng-repeat="(index, value) in [1,2,3]">
    <strong>ngRepeat</strong>
    <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
    <my-wrapper ng-ref="$ctrl.wrappers[index]">ngRepeat Toggle {{$index + 1}}</my-wrapper>
  </li>
</ul>

<div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen()}} // This is always undefined because it's
  assigned to the child scope created by ngIf.
</div>
<div ng-if="true">
      <strong>ngIf</strong>
   <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
   <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
   <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
</div>
ul {
  list-style: none;
  padding-left: 0;
}

li[ng-repeat] {
  background: lightgreen;
  padding: 8px;
  margin: 8px;
}

[ng-if] {
  background: lightgrey;
  padding: 8px;
}

my-root {
  background: lightgoldenrodyellow;
  padding: 8px;
  display: block;
}

my-wrapper {
  background: lightsalmon;
  padding: 8px;
  display: block;
}

my-toggle {
  background: lightblue;
  padding: 8px;
  display: block;
}
var OuterToggle = function() {
  this.toggle = function() {
    element(by.buttonText('Outer Toggle')).click();
  };
  this.isOpen = function() {
    return element.all(by.binding('outerToggle.isOpen()')).first().getText();
  };
};
var NgRepeatToggle = function(i) {
  var parent = element.all(by.repeater('(index, value) in [1,2,3]')).get(i - 1);
  this.toggle = function() {
    element(by.buttonText('ngRepeat Toggle ' + i)).click();
  };
  this.isOpen = function() {
    return parent.element(by.binding('ngRepeatToggle.isOpen() | json')).getText();
  };
  this.isOuterOpen = function() {
    return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
  };
};
var NgRepeatToggles = function() {
  var toggles = [1,2,3].map(function(i) { return new NgRepeatToggle(i); });
  this.forEach = function(fn) {
    toggles.forEach(fn);
  };
  this.isOuterOpen = function(i) {
    return toggles[i - 1].isOuterOpen();
  };
};
var NgIfToggle = function() {
  var parent = element(by.css('[ng-if]'));
  this.toggle = function() {
    element(by.buttonText('ngIf Toggle')).click();
  };
  this.isOpen = function() {
    return by.binding('ngIfToggle.isOpen() | json').getText();
  };
  this.isOuterOpen = function() {
    return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
  };
};

it('should toggle the outer toggle', function() {
  var outerToggle = new OuterToggle();
  expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
  outerToggle.toggle();
  expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
});

it('should toggle all outer toggles', function() {
  var outerToggle = new OuterToggle();
  var repeatToggles = new NgRepeatToggles();
  var ifToggle = new NgIfToggle();
  expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
  expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): false');
  expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): false');
  expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): false');
  expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
  outerToggle.toggle();
  expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
  expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): true');
  expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): true');
  expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): true');
  expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): true');
});

it('should toggle each repeat iteration separately', function() {
  var repeatToggles = new NgRepeatToggles();

  repeatToggles.forEach(function(repeatToggle) {
    expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): false');
    expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
    repeatToggle.toggle();
    expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): true');
    expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
  });
});