directive – Stepan Suvorov Blog https://stepansuvorov.com/blog Release 2.0 Thu, 22 Oct 2015 08:07:20 +0000 en-US hourly 1 https://wordpress.org/?v=6.3.1 AngularJS: How to pass a method into an isolated scope https://stepansuvorov.com/blog/2015/07/angularjs-how-to-pass-a-method-into-an-isolated-scope/ https://stepansuvorov.com/blog/2015/07/angularjs-how-to-pass-a-method-into-an-isolated-scope/#respond Wed, 29 Jul 2015 17:58:42 +0000 http://stepansuvorov.com/blog/?p=2780 Continue reading ]]> I came to conclusion that it’s not very obvious how to pass a method into an isolated scope. And that’s why my junior AngularJS developers just do direct call:

[javascript]
$scope.$parent.methodOfParentScope();
[/javascript]

but it’s more like a hack. It’s more correct to pass it via attribute:

[html]
<my-directive say="sayHello()"></my-directive>
[/html]

and inside directive:

[javascript]
{
say: ‘&’
}
[/javascript]

and now you can use it in isolated scope:

[javascript]
scope.say()
[/javascript]

And when you have method with param it’s little bit more tricky.

For example you have a method in a parent scope:

[javascript]
scope.sayHello = function(name){
//…
}
[/javascript]

in this case you should also specify it in the attribute:

[html]
<my-directive say="sayHello(name)"></my-directive>
[/html]

and for method call use specific format:

[javascript]
scope.say({name: ‘Alice’})
[/javascript]

Code is here.

Also highly recommend to watch episode from Egghead – Isolate Scope “&” .

]]>
https://stepansuvorov.com/blog/2015/07/angularjs-how-to-pass-a-method-into-an-isolated-scope/feed/ 0
AngularJS Directive attribute binding options https://stepansuvorov.com/blog/2014/11/angularjs-directive-binding/ https://stepansuvorov.com/blog/2014/11/angularjs-directive-binding/#comments Sun, 16 Nov 2014 06:43:59 +0000 http://stepansuvorov.com/blog/?p=2099 Continue reading ]]> This example explicitly shows the difference between directive attribute binding types. Let’s say you have directive:

[javascript]
function myDirective() {
return {
scope: {
x: ‘@’, // String, interpolated with {{ }}
y: ‘=’, // Expression
z: ‘&’ // Function
}
};
}
[/javascript]

it means that you will do such operations with attributes to pass the directive:

[javascript]
function MyDirectiveController($scope, $element, $attrs, $interpolate, $parse) {
$attrs.$observe(‘x’, function(value) {});
$interpolate($attrs.x)($scope);
$scope.$eval($attrs.y);
var fn = $parse($attrs.z);
$element.on(‘click’, function() {
fn($scope);
});
}
[/javascript]

]]>
https://stepansuvorov.com/blog/2014/11/angularjs-directive-binding/feed/ 2
Специфика тестирования AngularJs директив https://stepansuvorov.com/blog/2014/06/%d1%81%d0%bf%d0%b5%d1%86%d0%b8%d1%84%d0%b8%d0%ba%d0%b0-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-angularjs-%d0%b4%d0%b8%d1%80%d0%b5%d0%ba%d1%82%d0%b8%d0%b2/ https://stepansuvorov.com/blog/2014/06/%d1%81%d0%bf%d0%b5%d1%86%d0%b8%d1%84%d0%b8%d0%ba%d0%b0-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-angularjs-%d0%b4%d0%b8%d1%80%d0%b5%d0%ba%d1%82%d0%b8%d0%b2/#respond Wed, 11 Jun 2014 20:59:32 +0000 http://stepansuvorov.com/blog/?p=1715 Continue reading ]]> Тезисно выкладываю некоторые ключевые моменты по тестированию AngularJS директив.

Подготовительные действия

  • ставим тест-драйвер Karma
  • определяемся и подключаем к проекту тест-фреймворк (варианты Mocha, Jasmine)
  • подключаем к проекту вспомогательную библиотеку angular-mocks

Написание сценария

подключаем Angular-модуль:

[javascript]beforeEach(module(‘myapp’));[/javascript]

подключаем шаблон (предварительно скомпилированный с помощью html2js):

[javascript]beforeEach(module(‘path/to/template’));[/javascript]

инжектим сервисы:

[javascript]
var $timeout, $rootScope;
beforeEach(inject(function(_$timeout_, _$rootScope_){
$timeout = _$timeout_;
$rootScope= _$rootScope_;
}));[/javascript]

прошу обратить внимание на нижнее подчеркивание, которое используется в Ангуляре для удобства и инжектит одноименный сервис.

либо инжектим инжектор и с помощью него уже получаем сервисы:

[javascript]
var $timeout, $rootScope;
beforeEach(inject(function($injector){
$timeout = $injector.get(‘$timeout’);
$rootScope= $injector.get(‘$rootScope’);
}));[/javascript]

для создания нового $scope делаем:

[javascript]
var $scope = $rootScope.$new()
[/javascript]

компиляция директивы для тестирования:

[javascript]
var element = $compile(‘<my-directive></my-directive>’)($scope);
[/javascript]

при этом не забываем инжектить сервис $compile

получение изолированного $scope:

[javascript]
var isolatedScope = element.children().scope()
[/javascript]

либо (работает начиная с версии 1.2):

[javascript]
var isolatedScope = element.isolateScope()
[/javascript]

чтобы запустить все колбэки в таймАут-сервисах:

[javascript]
$timeout.flush()
[/javascript]

чтобы промисы внутри приложения отрезолвились необходимо вручную делать:

[javascript]
scope.$apply();
[/javascript]

если что-то забыл – пиши в комментариях – дополню.

UPD:

подмена сервисов (замена мок-вариатом):
[javascript]
beforeEach(module(function ($provide){
$provide.service(‘SameNameWithRealService’, function(){
//код вашего мок-сервиса
});
}));
[/javascript]

использование “шпионов” для проверки вызова методов:
[javascript]
//переопределение внутреннего метода
$scope.someMethod = sinon.spy();
//вызов внешнего метода, который должен вызвать первый
$scope.someOtherMethod()
//проверка
expect($scope.someMethod.called).equal(true);
[/javascript]
 

]]>
https://stepansuvorov.com/blog/2014/06/%d1%81%d0%bf%d0%b5%d1%86%d0%b8%d1%84%d0%b8%d0%ba%d0%b0-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-angularjs-%d0%b4%d0%b8%d1%80%d0%b5%d0%ba%d1%82%d0%b8%d0%b2/feed/ 0
AngularJs: альтернативный синтаксис регистрации директив https://stepansuvorov.com/blog/2014/02/angularjs-%d0%b0%d0%bb%d1%8c%d1%82%d0%b5%d1%80%d0%bd%d0%b0%d1%82%d0%b8%d0%b2%d0%bd%d1%8b%d0%b9-%d1%81%d0%b8%d0%bd%d1%82%d0%b0%d0%ba%d1%81%d0%b8%d1%81/ https://stepansuvorov.com/blog/2014/02/angularjs-%d0%b0%d0%bb%d1%8c%d1%82%d0%b5%d1%80%d0%bd%d0%b0%d1%82%d0%b8%d0%b2%d0%bd%d1%8b%d0%b9-%d1%81%d0%b8%d0%bd%d1%82%d0%b0%d0%ba%d1%81%d0%b8%d1%81/#respond Sun, 16 Feb 2014 20:07:18 +0000 http://stepansuvorov.com/blog/?p=1462 Continue reading ]]> Вместо привычного нам:

app.directive('directiveName', function() { ...

можно использовать групповой синтаксис:

app.directive(directives)

где directives – объект содержащий в себе директивы (ключ – название директивы, значение – функция конструктор).

Может быть удобно для использования пространства имен (namespaces):

User.directives = {};
User.directives.myDirective = function () { ...
angular.directive(User.directives);

данный синтаксис работает также и для контроллеров.

]]>
https://stepansuvorov.com/blog/2014/02/angularjs-%d0%b0%d0%bb%d1%8c%d1%82%d0%b5%d1%80%d0%bd%d0%b0%d1%82%d0%b8%d0%b2%d0%bd%d1%8b%d0%b9-%d1%81%d0%b8%d0%bd%d1%82%d0%b0%d0%ba%d1%81%d0%b8%d1%81/feed/ 0
AngularJS: Использование контроллера директивы в другой директиве https://stepansuvorov.com/blog/2014/02/angularjs-directive-controller-reuse/ https://stepansuvorov.com/blog/2014/02/angularjs-directive-controller-reuse/#respond Fri, 14 Feb 2014 08:59:26 +0000 http://stepansuvorov.com/blog/?p=1448 Continue reading ]]> Опция require в описании директивы дает нам возможность использовать контроллер другой директивы. Мы также можем указать массив контроллеров.

Объяснение можно посмотреть на egghead.io, код можно взять тут.

Важно: если поставить в начале значения символ ‘^’,  то интерпретатор будет пытаться найти контроллер директивы в родительских элементах. Если не поставить – только на текущем елементе(как это было в примере на egghead.io).

]]>
https://stepansuvorov.com/blog/2014/02/angularjs-directive-controller-reuse/feed/ 0
ngDirective – директива для загрузки директив https://stepansuvorov.com/blog/2014/02/ng-directive/ https://stepansuvorov.com/blog/2014/02/ng-directive/#respond Mon, 10 Feb 2014 22:11:29 +0000 http://stepansuvorov.com/blog/?p=1404 Continue reading ]]> Суть:

директива типа “атрибут”, значением идет имя поддирективы, которую необходимо динамически подгрузить. Может быть удобно в случае, когда в зависиомти от условия мы должны выводить различные компоненты.

Предистория:

В проекте (на AngularJS) стала задача вывести список сущностей, каждая из которых имеет свойства и представление (директивы). Но сложность заключается в том, что сущности могут буть разных типов, совершенно разных: с различным представлением и поведением. Вопрос: “что делать, если хочется все вывести через ng-repeat?”. Одну директиву в духе god-antipattern, где будет сразу все возможные варианты поведения и отображения, лепить не хотелось. Хотелось использовать разные, чтобы потом еще можно было делать новые и они автоматически подхватывались. Но как их подгружать?

Решение:

Для динамической сборки директивы нам нужен сервис $compile. В итоге получилось такое( выкладываю упрощенный вариант):

html:

<div ng-repeat="item in items">
  <itemDirective></itemDirective>
</div>

а внутри директивы itemDirective:

element.append($compile(angular.element('<' + $scope.item.type + '/>'))($scope));

После чего пришла мысль “а почему бы это не оформить в виде директивы, которая будет подгружать в себя в зависимости от параметра другую директиву“.

И код преобразился следующим образом:

<div ng-repeat="item in items">
  <itemDirective ng-directive="{{item.type}}"></itemDirective>
</div>

А в директиве:

element.append($compile(angular.element('<' +  attrs.ngDirective + '/>'))($scope));
Так же не помешает наблюдение за состояние атрибута, чтобы было в духе Ангуляра, поэтому добавим $observe:
attrs.$observe('ngDirective', function(directive) {
    element.html('');
    element.append($compile(angular.element('<' + directive + '/>'))($scope));
});

 

Вот код и демо.

Очень бы хотелось услышать конструктивную критику.

Идея пришла под вдохновлением от прочтения этой статьи.

 

UPD: как подсказали в сообществе:

  1. лучше не смешивать логику с представлением(хранить имя директивы в контролере)
  2. следить за утечками памяти, ибо в данном случае новая директива создает много вотчеров, которые потом никто не зачищает
  3. использовать для данной задачи ngSwitch

 

]]>
https://stepansuvorov.com/blog/2014/02/ng-directive/feed/ 0