コンテンツへスキップ
Understanding Scopes in AngularJS Custom Directives

Understanding Scopes in AngularJS Custom Directives

AngularJS でディレクティブを操作する際の共有スコープ、継承スコープ、および分離スコープの使用について説明します。

8min read

この投稿では、AngularJSカスタムディレクティブのさまざまな種類のスコープについて学習します。まず、ディレクティブの概要から始めて、スコープに焦点を当てます。

ディレクティブ

ディレクティブは、AngularJS 1.X の最も重要なコンポーネントの 1 つであり、次の目的があります。

  1. 既存の要素に特別な意味を持たせるため
  2. 新しい要素を作成するには
  3. DOM を操作するには

ng-app、ng-controller、ng-repeat以外にも、AngularJSには次のような組み込みディレクティブがたくさんあります。

  • ng-maxlength
  • ng-minlength
  • ng-pattern
  • ng-必須
  • ng-submit
  • ng-blur
  • ng-change
  • ng-checked
  • ng-click
  • ng-mouse
  • ng-bind
  • ng-href
  • ng-init
  • ng-model
  • ng-src
  • ng-style
  • ng-app
  • ng-controller
  • ng-disabled
  • ng-cloak
  • ng-hide
  • ng-if
  • ng-repeat
  • ng-show
  • NGスイッチ
  • ng-view

主に、ディレクティブは次のいずれかのタスクを実行します。

  • Manipulate DOM
  • Iterate through data
  • イベントの処理
  • Modify CSS
  • Validate data
  • Perform Data Binding  

Angularチームが提供する組み込みディレクティブは多数ありますが、独自のカスタムディレクティブを作成する必要がある場合があります。カスタムディレクティブは、要素、属性、コメント、またはクラスとして作成できます。この投稿では、以下のリストに示すように、非常に単純なカスタムディレクティブを作成できます。

MyApp.directive('helloWorld', function () {
    return {
        template: "Hello IG"
    };
});

カスタムディレクティブを作成する際には、次の点を覚えておくことが重要です。

  • ディレクティブ名はキャメルケースで指定する必要があります。
  • ビューでは、ディレクティブは、ダッシュ、コロン、アンダースコア、またはこれらの組み合わせを使用してキャメルケースの名前を区切ることによって使用できます。

Scopes in Custom Directives

スコープは、データをカスタムディレクティブに渡すときにシーンに入ります。スコープには 3 つのタイプがあり、それぞれ 2 つのタイプが画像に示されています。

  1. Shared scope
  2. Inherited scope
  3. Isolated scope
スコープには 3 つのタイプがあり、それぞれにいくつかのタイプが示されています

継承されたスコープを持つカスタムディレクティブを作成するには、以下のリストに示すように、scope プロパティを true に設定します。

MyApp.directive('studentDirective', function () {
    return {
        template: "
{{student.name}} is {{student.age}} years old !!
",
        replace: true,
        restrict: 'E',
        scope : true ,
        controller: function ($scope) {
            console.log($scope);
        }
    }
});

共有スコープと継承スコープ

共有スコープと継承スコープは、比較的理解しやすいです。共有スコープでは、ディレクティブは囲まれたコントローラーとスコープを共有します。

以下のリストに示すように、コントローラーがあると仮定します。

MyApp.controller("StudentController", [
    "$scope",
    function ($scope) {
        console.log($scope);
        $scope.student = {
            name: "dj",
            age: 32,
            subject: ["math", "geography"],
        };

        $scope.setGrade = function (student) {
            student.grade = "A+";
        };
    },
]);

Next, let’s go ahead and create a custom directive:

MyApp.directive('studentDirective', function () {
    return {
        template: "
{{student.name}} is {{student.age}} years old !!
",
        replace: true,
        restrict: 'E',
        controller: function ($scope) {
            console.log($scope);
        }
    }
});

ここでは、ビューでstudentディレクティブを使用できます。

<div ng-controller="StudentController">
     <student-directive> </student-directive>
</div>

上記のスニペットでは、ng-controller ディレクティブが StudentController に設定されている div 内のディレクティブを使用しています。ディレクティブの scope プロパティに値を設定していないため、デフォルトでは共有スコープモードで動作します。ディレクティブは、コントローラーのスコープにアタッチされたプロパティにアクセスできます。ディレクティブのプロパティに対する変更はコントローラーに反映され、その逆も同様です。また、コントローラーとディレクティブの両方のスコープ id を出力しており、両方の id が同じでなければならないことにも気付くでしょう。

ディレクティブのプロパティに対する変更はコントローラーに反映され、その逆も同様です

共有スコープには 1 つの問題があります: ディレクティブにデータを明示的に渡すことはできません。ディレクティブは、囲まれたコントローラーからデータを直接取得します。

継承されたスコープでは、ディレクティブはコントローラーのスコープを継承します。継承されたスコープを持つディレクティブを作成する方法を以下で見てみましょう。

MyApp.directive('studentDirective', function () {
    return {
        template: "
{{student.name}} is {{student.age}} years old !!
",
        replace: true,
        restrict: 'E',
        scope : true ,
        controller: function ($scope) {
            console.log($scope);
        }
    }
});

継承されたスコープは、ネストされたカスタムディレクティブで非常に便利です。

Isolated Scope

Isolated スコープでは、ディレクティブはコントローラーとスコープを共有しません。ディレクティブとコントローラーの両方に独自のスコープがあります。ただし、データは 3 つの方法でディレクティブのスコープに渡すことができます。

  1. データは、@ 文字列リテラルを使用して文字列として渡すことができます
  2. データは、= 文字列リテラルを使用してオブジェクトとして渡すことができます
  3. 3. データは関数として & string リテラルとして渡すことができます。
Isolated スコープでは、ディレクティブはコントローラーとスコープを共有しません。ディレクティブとコントローラーの両方に独自のスコープがあります。

分離されたスコープは、さまざまなデータをコントローラーに渡すことができるため、非常に重要です。それをよりよく理解するために、以下に示すようなコントローラーがあると仮定しましょう。

MyApp.controller("ProductController", function ($scope) {
    $scope.product1 = {
        name: 'Phone',
        price: '100',
        stock: true
    };
    $scope.product2 = {
        name: 'TV',
        price: '1000',
        stock: false
    };
    $scope.product3 = {
        name: 'Laptop',
        price: '800',
        stock: false
    };
 
    $scope.ShowData = function () {
        alert("Display Data");
    }
 
});

ご覧の通り、3つの異なる製品があり、それを異なる方法で渡したいと考えています。

データを文字列として渡す

分離スコープでは、@ 文字列リテラルを使用してデータを文字列として渡すことができます。以下のリストに示すように、文字列を入力パラメータとして受け入れるカスタムディレクティブを作成できます。

MyApp.directive('inventoryProduct', function () {
    return {
        restrict: 'E',
        scope: {
            name: '@',
            price:'@'
        },
        template: '
{{name}} costs {{price}} $
Change name
'
    };
});

上記のリストでは、@ literal を使用して 2 つの文字列パラメータを渡しており、これは name 変数と price 変数で文字列が渡されることを意味します。 ディレクティブは、この例では、ディレクティブで name と price の文字列値を渡しているビューで使用できます。

<div ng-controller="ProductController">
    <h1>{{product1.name}}</h1>
    <inventory-product name="{{product1.name}}" price="{{product1.price}}"></inventory-product>
</div>

アプリケーションを実行すると、product1の名前と価格が表示されるはずです。

アプリケーションを実行すると、名前と価格を確認できるはずです

[名前の変更]ボタンをクリックすると、ディレクティブの名前のみが変更され、ProductControllerのproduct1オブジェクトは分離されたスコープの影響を受けません。

[名前の変更] ボタンをクリックすると、ディレクティブの名前のみが変更され、ProductController product1 オブジェクトは分離されたスコープの影響を受けません

また、分離されたスコープでデータを文字列として渡すと、一方向で渡されるため、コントローラーのスコープの変更はディレクティブに反映されることに注意してください。ただし、ディレクティブの変更はコントローラーに反映されません。

Pass Data As an Object

分離スコープでは、= 文字列リテラルを使用してデータをオブジェクトとして渡すことができます。以下のリストに示すように、オブジェクトを入力パラメータとして受け入れるカスタムディレクティブを作成できます。

MyApp.directive('inventoryProduct', function () {
    return {
        restrict: 'E',
        scope: {
            data: '='
        },
        template: '
{{data.name}} costs {{data.price}} $
Change name
'
    };
});

上記のリストでは、= literal を使用して 1 つのオブジェクト パラメーターを渡しています。ここでは、オブジェクトがデータ変数に渡されます。このディレクティブは、以下のリストに示すように、ビューで使用できます。ご覧のとおり、ディレクティブでデータ変数のオブジェクト値を渡しています。

<div ng-controller="ProductController">
    <h1>{{product1.name}}</h1>
    <inventory-product data="product1"></inventory-product>
    <h1>{{product2.name}}</h1>
    <inventory-product data="product2"></inventory-product>
    <h1>{{product3.name}}</h1>
    <inventory-product data="product3"></inventory-product>
</div>

リストによると、ディレクティブを3回使用し、3つの異なるオブジェクトを入力として渡しています。実行すると、次のような出力が表示されます。

リストによると、ディレクティブを3回使用し、3つの異なるオブジェクトを入力として渡しています。

分離スコープでオブジェクトを渡すことは、双方向バインディング モードで機能し、ディレクティブの変更は囲まれたコントローラーに反映されます。次のリストに示すように、product1 を 2 回渡したとします。

<div ng-controller="ProductController">
    <inventory-product data="product1"></inventory-product>
    <inventory-product data="product1"></inventory-product>
</div>

アプリケーションを実行すると、ディレクティブの両方のインスタンスに同じオブジェクトが渡されます。

アプリケーションを実行すると、ディレクティブの両方のインスタンスに同じオブジェクトが渡されます。

[名前の変更] ボタンのいずれかをクリックすると、同じオブジェクトが渡され、オブジェクトの受け渡しが双方向モードで動作するため、ディレクティブの両方のインスタンスの名前が変更されます。ディレクティブの変更は、それを囲むコントローラーに反映され、その逆も同様です。

[名前の変更] ボタンのいずれかをクリックすると、同じオブジェクトが渡されるため、ディレクティブの両方のインスタンスの名前が変更されます

Calling an External Function

囲まれたディレクティブで外部関数を呼び出すには、リテラル変数 &.ProductController で述べたように、ShowData() 関数があります。この関数は、以下に示すようにディレクティブを変更することで、カスタムディレクティブで呼び出すことができます。

MyApp.directive('inventoryProduct', function () {
    return {
        restrict: 'E',
        scope: {
            data: '&',         
        },
        template: '
{{data.name}} costs {{data.price}} $
Change name
'
    };
});

ここでは、次に示すように、ディレクティブをビューで使用できます。

<div ng-controller="ProductController">
  <inventory-product data="ShowData()"></inventory-product>
</div>

結論

この投稿では、ディレクティブの基本的な理解から始めて、次にスコープに移り、次のことを学びました。

  1. 共有スコープ: ディレクティブとコントローラーはスコープとデータを共有します。ディレクティブにデータを明示的に渡すことはできません。
  2. 継承されたスコープ: ディレクティブはコントローラーのスコープを継承します。ディレクティブにデータを明示的に渡すことはできません。
  3. 分離スコープ: ディレクティブとコントローラーはデータとスコープを共有しません。データを文字列またはオブジェクトとして明示的にディレクティブに渡すことができます。

この投稿がお役に立てば幸いです。ディレクティブを使用して自分で作業した後、HTML5 と JavaScript の UI コントロールとコンポーネントの高度なセット、Ignite UIをご覧ください。今すぐ IgniteUI をダウンロードして、何ができるかを確認できます。読んでくれてありがとう!

デモを予約