2016-05-10 7 views
26

Обновление: Я поставил щедрость на этот вопрос. Я не ищу хаки или обходные пути. Я ищу официальный способ доступа к dom в угловом компоненте и объяснение, почему поведение, которое я вижу ($ postLink работает до начала), кажется, противоречит официальным документам.
Официальный документы государственного (here):

$ postLink() - Вызывается после того, как элемент этого контроллера и его детей были связаны между собой. Аналогично функции пост-связи этот крюк может быть использован для создания обработчиков событий DOM и делать прямые манипуляции DOM

Оригинальный вопрос: У меня есть пример задачи здесь ->http://plnkr.co/edit/rMm9FOwImFRziNG4o0sg?p=preview

I я использую угловой компонент, и я хочу изменить dom в функции post link, но он не работает, кажется, что функция запускается слишком рано, прежде чем шаблон будет фактически готов в dom после всей угловой обработки.

На странице HTML, у меня есть это:

<my-grid grid-id="'foo'"></my-grid> 

компонент определяется как:

appModule.component('myGrid',{ 
    controller: gridController, 
    bindings: { 
     "gridId": "<", 
    }, 
    templateUrl: 'gridTemplate' 
}); 

В компоненте шаблона у меня есть это:

<table id='{{$ctrl.gridId}}'> 
... 

(The Несомненно, сам связывание. Несомненно, в html идентификатор таблицы «foo», как и ожидалось).

В контроллере, у меня есть что-то вроде этого:

function gridController($scope, $compile, $attrs) { 
    console.log ("grid id is: " + this.gridId); // 'foo' 

    this.$postLink = function() { 
     var elem = document.getElementById(this.gridId); 
     // do something with elem, but elem is null 
    } 
} 

То, что я вижу при отладке, что когда функция $ postLink выполняется, таблица в йот, но его атрибут ID еще {{$ctrl.gridId}} вместо foo, поэтому document.getElementById() ничего не находит. Это похоже на документацию.
Что мне не хватает? Есть ли другой способ доступа к dom в компоненте?

Обновление 2: Сегодня я понял, что такая же проблема возникает с регулярной ссылкой на директивы, она не ограничивается компонентами. Поэтому, по-видимому, я неправильно понял смысл «прямое манипулирование DOM» - функция ссылки работает на элементе, который отделен от dom, поэтому использование объекта document с селекторами бесполезно.

+0

[ссылка тайминги] (http://www.bennadel.com/blog/2603-directive-controller-and-link-timing-in-angularjs.htm) надеюсь, что это поможет –

ответ

14

документация относительно $postLink() правильно. Он называется после элемента контроллера, и его дочерние элементы связаны. Это не означает, что вы сразу увидите результат директивы. Возможно, он звонит $http и вставляет результат после его поступления. Возможно, он регистрирует наблюдателя, который по очереди устанавливает результат, , поскольку большинство встроенных директив Angular делают.

Основная проблема в вашем случае состоит в том, что вы хотите выполнять манипуляции с DOM после , интерполяции были скомпилированы, или еще лучше, после того, как их зарегистрированный наблюдатель успел запустить один раз.

К сожалению, для этого не существует . Тем не менее, есть способы сделать это, но вы не найдете их в документации.

Два популярных способами бегущих функцию после того, как интерполяция была собраны являются:

  • использования $timeout без задержки (как это по умолчанию 0): $timeout(function() { /* Your code goes here */ });

  • с помощью .ready() который предоставляется Angular's jqLite

В вашем случае, вы гораздо лучше использовать Бодрствующего запустить функцию после того, как элемент с указанным идентификатором существует:

var deregistrationFn = $scope.$watch(() => { 
    return document.getElementById(this.gridId); 
}, (newValue) => { 
    if (newValue !== null) { 
     deregistrationFn(); 

     // Your code goes here 
    } 
}); 

Наконец, на мой взгляд, я считаю, что всякий раз, когда вам нужно ждать интерполяции, которые нужно скомпилировать, или для некоторых директив вставить их значение, вы не следуете Угловой способ построения вещей. В вашем случае почему бы не создать новый компонент, myGridTable, который требует myGrid в качестве родителя и добавит туда свою соответствующую логику. Таким образом, ответственность каждого компонента намного лучше определена, и легче тестировать вещи.

+0

Это правильный ответ, поэтому я предоставил щедрость. После моего собственного расследования я более или менее знал детали, но этот ответ объясняет это более ясными терминами. Кроме того, использование $ watch с функцией вместо тайм-аута - очень приятное дополнение :) Я попытаюсь реализовать его также с подкомпонентом, посмотрим, работает ли это ... – Yoni

2

Это похоже на понимание сценариев угловых компонентов, прежде чем давать какое-либо решение. И что я вижу из вашего примера шаблон DOM, загруженный в , выделенную область. Повторить в изолированных объем

Так,

Согласно официальному документу [1.5.6] на компонентах https://docs.angularjs.org/guide/component

Если не использовать компоненты:

  • для директив, которые полагаются на манипуляции DOM, добавление слушателей событий и т.д.,потому что компиляции и компоновки функций недоступны
  • , когда вам нужны расширенные параметры определения директивы, такие как приоритет, терминал, многоэлементный
  • , когда вы хотите директиву, когда вызывается с помощью атрибута или CSS класса, а не элемент

Если вы просмотрите раздел «Сравнение между определением директивы и определением компонентов» от дока, вы увидите

  • особенность ------------ директива ----------- компонент
  • привязок ---------- - Нет ------------------- Да (привязывается к контроллеру)
  • bindToController ---- Да (по умолчанию: false) -Нет (вместо этого используйте привязки)
  • функция компиляции ---- Да ------------------ Нет
  • Контроллер ---------- Да ------------------ Да (функция по умолчанию() {})
  • controllerAs ----- --- Да ------------------ (по умолчанию: false) Да (по умолчанию: $ ctrl)
  • Функции ссылок ------ Да ---- -------------- Нет

Теперь

Компоненты никогда не должны изменять какие-либо данные или DOM, которые находятся вне их собственных scope. Как правило, в Угловом можно изменять данные в любом месте в приложении через scope Наследование и часы. Это практично, но также может привести к проблемам, когда неясно, какая часть приложения отвечает за изменение данных. То есть почему в директивах компонентов используется isolate scope, поэтому весь класс управления размером невозможен. REF: нг-док 1.5.6

Но когда вы видите, Там это объявление $ postLink() крючок доступен, вы можете интересно, что причина?

Однако, если вы просмотрите описание этого крючка, вы найдете ...

Примечание, что дочерние элементы, содержащие templateUrl директивы не были составлены и связаны, так как они ждут их шаблон для загрузки асинхронно и их собственной компиляции и компоновки не было приостановлено до тех пор, что имеет место.

Пожалуйста, обратитесь к Стивен Ламберт отвечает ниже ...

+0

О последнем комментарии, Я думал, что дочерние элементы означают другие угловые директивы внутри текущего компонента. В моем шаблоне в настоящее время существует только базовый html. Если templateUrl не был скомпилирован и не связан, почему они называют эту функцию обратного вызова $ postLink? – Yoni

+0

Я также изучил и нашел 2 возможных обходных пути, 1 - использовать службу compileProvider, а другой - скомпилировать templateUrl в postlink и назначить свойство в компонент. вы также можете попробовать этот путь или любую другую перспективу. Надежда скоро придет с решением –

+0

Я изменил его на шаблон вместо templateUrl, и он по-прежнему не работает. Заметка об асинхронных URL-адресах не связана с этой проблемой, она ссылается только на дочерние директивы – Yoni

10

Непонимание заключается в том, как цикл углового цикла $ работает в сочетании с событиями жизненного цикла контроллера (и событиями жизненного цикла директивы).

Каждый из событий жизненного цикла контроллера/директивы вызывается внутри приложения $ apply. Из-за этого DOM обновляется только для отражения изменений после $digest cycle is completed.

Функция пост-ссылки запускается после того, как все связано, но это at the end of this function, что DOM обновляется, чтобы отражать любые привязки, из-за того, что это последняя функция, выполняемая внутри цикла $ digest. Это означает, что попытка запросить DOM для чего-либо, затронутого привязкой, не будет работать (например, ng-repeat или в вашем случае связанное значение). Но, как указано в документах, связь DOM уже запущена, поэтому исходный шаблон был создан и может быть обработан.

Поскольку вам необходимо запросить DOM после того, как были применены привязки, вам нужно запустить ваш код после того, как закончится текущий цикл $ digest. Это означает использование $timeout с задержкой 0, которая будет executed right after the current $digest cycle has ended.

function gridController($scope, $compile, $attrs, $timeout) { 
    console.log ("grid id is: " + this.gridId); // 'foo' 

    this.$postLink = function() { 
     $timeout(function() { 
      var elem = document.getElementById(this.gridId); 
      // do something with elem now that the DOM has had it's bindings applied 
     }); 
    } 
} 
+0

Спасибо, что ясно, я был поиск таких правильных намеков. –

Смежные вопросы