読者 (私) の演習として、これを達成するためのカスタム ディレクティブを試してみました。これが私が思いついたものです(何度も失敗した後):
<ul ng-controller="ListController">
<li ng-repeat="item in items">
<div singleton-overlay>{{item.content}}</div>
</li>
</ul>
現在オーバーレイがある要素がある場合は、その要素を格納するサービスが必要です。(「サービス + ディレクティブ」の方が「コントローラー + ディレクティブ」よりも再利用可能なコンポーネントになると思うので、これにはコントローラーを使用しないことにしました。)
service('singletonOverlayService', function() {
this.overlayElement = undefined;
})
そしてディレクティブ:
directive('singletonOverlay', function(singletonOverlayService) {
return {
link: function(scope, element, attrs) {
element.bind('click', moveOrToggleOverlay);
function moveOrToggleOverlay() {
if (singletonOverlayService.overlayElement === element) {
angular.element(element.children()).remove();
singletonOverlayService.overlayElement = undefined;
} else {
if (singletonOverlayService.overlayElement != undefined) {
// this is a bit odd... modifying DOM elsewhere
angular.element(singletonOverlayService.overlayElement.children()).remove();
}
element.append('<div>overlay: tweet, share, pin</div>')
singletonOverlayService.overlayElement = element;
jsFiddle: http://jsfiddle.net/mrajcok/ya4De/
ただし、実装は少し型にはまらないと思います...ディレクティブは、独自の要素に関連付けられたDOMを変更するだけでなく、現在オーバーレイを持つ要素に関連付けられているDOMも変更する可能性があります。
スコープに $watches を設定し、シングルトンでスコープ オブジェクトを保存および変更しようとしましたが、moveOrToggleOverlay 関数内からスコープを変更したときに $watches を起動できませんでした。