2015-12-09 9 views
6

属性を使用して変更する別のコントロールを指定するカスタムディレクティブがあります。単体テスト外部要素にアクセスする角ディレクティブ

指令定義オブジェクト:

{ 
    restrict: 'E', 
    templateUrl: 'myTemplate.html', 
    scope: { 
     targetId: '@' 
    }, 
    controller: MyController, 
    controllerAs: 'vm', 
    bindToController: true 
} 

機能指令のコントローラは、ターゲット要素(入力フィールド)の内容変更に関する:

function onSelection (value) { 
    var $element = $('#' + vm.targetId); 

    $element.val('calculated stuff'); 
    $element.trigger('input'); 
} 

ユニットテスト(ジャスミン/カルマ/ PhantomJSを)は、現在要素をページに追加しています。これは動作しますが、コードの匂いのようです。

beforeEach(inject(function($rootScope, $compile) { 
    var elementHtml = '<my-directive target-id="bar"></my-directive>' + 
     '<input type="text" id="bar">'; 

    scope = $rootScope.$new();  
    angularElement = angular.element(elementHtml); 
    angularElement.appendTo(document.body); // HELP ME KILL THIS! 
    element = $compile(angularElement)(scope); 
    scope.$digest(); 
})); 

afterEach(function() { 
    angularElement.remove(); // HELP ME KILL THIS! 
}); 

jQueryを回避するためにコントローラ関数を書き直そうとしました。これは役に立たなかった。

appendTo/removeを削除するために、ディレクティブまたはテストを修正するにはどうすればよいですか?

+1

あなたのディレクティブにIDを使用すると、はるかに大きなコードの匂いになります。それは非常にjQueryスタイルであり、 "角度的な方法"ではありません。 DOMツリーの同じブランチ上のディレクティブや 'common-parent 'や' global service'アプローチのようなディレクティブで 'require'を使うことを検討してください(あなたはそれをやる方法がわからなければ検索や質問をすることができます - –

+0

@ValentynShybanov私は、ディレクティブの改訂を含む解決策には門を開いています。このディレクティブの実際の目的は、入力またはテキストエリアのメールマージプレースホルダを挿入することです(ディレクティブテンプレートの一部ではないはずです)。あなたが推奨する方法の例やこれへのリンクを投稿したいのであれば、それを答えとして受け入れることを検討するでしょう。 – TrueWill

+2

'onSelection()'関数をリファクタリングせずにコードからadd/remove要素を削除することはできません。 'ng-model'を#bar入力にバインドできない理由はありますか?もしあなたがそれを行うことができれば、jQueryで要素を取得する必要なしにコントローラの値を設定することができます。 – jperezov

答えて

2

最も適切なのは、ディレクティブを要素ではなく属性に移行することです。これにより、target-id属性の必要性がなくなり、ターゲット要素を捜す必要はありません。ターゲットを作成するために第2のディレクティブを使用します。

は、ここで同じほぼ正確にあなたのロジックを保持し、別の提案だhttp://jsfiddle.net/morloch/621rp33L/

指令

angular.module('testApp', []) 
    .directive('myDirective', function() { 
    var targetElement; 
    function MyController() { 
     var vm = this; 
     vm.onSelection = function() { 
     targetElement.val('calculated stuff'); 
     targetElement.trigger('input'); 
     } 
    } 
    return { 
     template: '<div></div>', 
     restrict: 'A', 
     scope: { 
     targetId: '@' 
     }, 
     link: function postLink(scope, element, attrs) { 
     targetElement = element; 
     }, 
     controller: MyController, 
     controllerAs: 'vm', 
     bindToController: true 
    }; 
    }); 

テスト

describe('Directive: myDirective', function() { 
    // load the directive's module 
    beforeEach(module('testApp')); 

    var element, controller, scope; 

    beforeEach(inject(function($rootScope, $compile) { 
    scope = $rootScope.$new(); 
    element = angular.element('<input my-directive type="text" id="bar">'); 
    $compile(element)(scope); 
    scope.$digest(); 
    controller = element.controller('myDirective'); 
    })); 

    it('should have an empty val', inject(function() { 
    expect(element.val()).toBe(''); 
    })); 

    it('should have a calculated val after select', inject(function() { 
    controller.onSelection(); 
    expect(element.val()).toBe('calculated stuff'); 
    })); 
}); 
+0

ありがとうございます - これは間違いなく1つのアプローチです。 1つの欠点は、「ページ上の指示制御をここで行いたい」と「ページ上のこの位置の制御に影響を与えたい」と言うのが難しいということです。 – TrueWill

+1

もう1つのオプションは、入力タグに直接アクセスする必要があるため、コントロール(ユーザーが選択する場所)と結果を適用する別の場所(入力上)の2つのディレクティブを使用して、行動が起こったこと。 – morloch

1

を参照してください。 elementをご利用いただけます彼はあなたが、処理のためのあなたの主なディレクティブに渡すことができ、コントローラ:http://jsfiddle.net/morloch/p8r2Lz1L/

getElement

.directive('getElement', function() { 
    return { 
     restrict: 'A', 
     scope: { 
     getElement: '=' 
     }, 
     link: function postLink(scope, element, attrs) { 
     scope.getElement = element; 
     } 
    }; 
    }) 

myDirective

.directive('myDirective', function() { 
    function MyController() { 
     var vm = this; 
     vm.onSelection = function() { 
     vm.targetElement.val('calculated stuff'); 
     vm.targetElement.trigger('input'); 
     } 
    } 
    return { 
     template: '<div></div>', 
     restrict: 'E', 
     scope: { 
     targetElement: '=' 
     }, 
     controller: MyController, 
     controllerAs: 'vm', 
     bindToController: true 
    }; 
    }) 

テスト

describe('Directive: myDirective', function() { 
    // load the directive's module 
    beforeEach(module('testApp')); 

    var element, controller, scope; 

    beforeEach(inject(function($rootScope, $compile) { 
    scope = $rootScope.$new(); 
    element = angular.element('<input get-element="elementBar" type="text" id="bar"><my-directive target-element="elementBar"></my-directive>'); 
    $compile(element)(scope); 
    scope.$digest(); 
    controller = $(element[1]).controller('myDirective'); 
    })); 

    it('should have an empty val', inject(function() { 
    expect($(element[0]).val()).toBe(''); 
    })); 

    it('should have a calculated val after select', inject(function() { 
    controller.onSelection(); 
    expect($(element[0]).val()).toBe('calculated stuff'); 
    })); 
}); 
関連する問題