@thekip
Php/C#/Js Developer

AngularJS: Как отловить событие после изменения скопа и его рендеринга?

Начал изучать этот фрэймворк, столкнулся с банальнейшей задачей которую не могу реализовать.

Необходимо считать данные из DOM после того как было изменено значение в scope и отрендерено в представление.
Если считывать значение из DOM сразу после изменения скопа, то естественно мы ничего не получаем, так как DOM еще не отрендерен.

Задача в целом звучит следующим образом:
Есть scope в котором есть свойство (длинна промежутка времени) по которому строится горизонтальный грид. Задача такова, что если длинна этого грида становится больше видимой области экрана, нужно сделать его горизонтальную прокрутку.

Грид идет не в таблице а на DIVах. колонки выстраиваются друг за дружкой справа налево и имеют float:left.
Соответственно что бы они не съезжали друг под друга при нехватке места в ширину, нужно задать родительскому контейнеру статичную ширину в которую влезут все колонки грида.

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

Как я только не пробовал это делать… Я наткнулся на такой топик на SO stackoverflow.com/questions/12102366/angularjs-fire-an-event-immediately-after-scope-digest

И начал реализовывать через directives и $evalAsync. Но правильного результата добиться не могу.
Вот пример кода:
angular.module('mailingDirectives', [])
    .directive('scroller', function($timeout) {
        return {
            restrict: 'A',
            link: function(scope, element, attrs) {
                scope.$watch(attrs.scroller, function(){
                    var width=0;
                    
                    scope.$evalAsync(function(){
                        element.find('>div').each(function(){
                            width += $(this).outerWidth(true);
                        })
                        
                        var wrapper = element.parent();
                     
                        if (wrapper.width() < width) {
                            wrapper.css('overflow-x', 'scroll');
                        } else {
                            wrapper.css('overflow-x', 'hidden');
                        }
                    
                   
                     element.width(width);
                    })
                })
                
            }
        } 
    });

    <div class="quotesGridWrapper span9">
            <div scroller="dateSpan" class="quotesGrid">
                <div class="month" ng-repeat="month in quotesGrid">

                    <span class="monthName">{{month.name}} {{month.year}}</span>

                    <div class="day" ng-repeat="day in month.days">
                        <div class="date">
                            {{day.date.getDate()}} <br /> <span title="">{{day.dowShot}}</span>
                        </div>

                        <div class="mailBtn">
                            <a class="btn btn-small"><i class="icon-envelope"></i></a>
                        </div>

                        <div class="quotes">

                            <div class="full">
                                60
                            </div>

                            <div class="free">
                                10
                            </div>

                            <div class="busy">
                                50
                            </div>

                        </div>
                    </div>
                </div>
            </div>
    </div>


В итоге сейчас имеем то что при считывании ширины колонок в гриде оно считывает их каждый раз по разному. Т.е. добавление новых и процесс считывания чередуются между собой. Раз считает что там 5 колонок, в другой раз что 7 и т.п.
Т.е. $evalAsync в реальности не вызывается после $digest.
  • Вопрос задан
  • 7178 просмотров
Пригласить эксперта
Ответы на вопрос 4
@aretmy
Может быть, просто воспользоваться директивой ngStyle?
Ответ написан
@thekip Автор вопроса
Php/C#/Js Developer
Изучая теоретическую базу по AngularJs попробую сформировать по точнее что мне необходимо.

Мне необходимо отловить событие (если оно вообще есть) которое происходит после того как отработала функция $digest и отработали все слушатели этой функции (полностью приготовили DOM).

Пост на SO, который я привел в начале топика как раз об этом же, но решение указанное там, в моем случае не работает. Я предпологаю что проблема в том что AngularJs выполняет «слушателелей» асинхронно, и сам не знает когда они закончат работать.

К сожалению как решить эту проблему не имею понятия.
Ответ написан
Комментировать
lusever
@lusever
Поэкспериментируйте с setTimeout:
scope.$watch(attrs.scroller, function(){
  setTimeout(function() {
    ...
  }, 1);
});

Функция внутри добавится в конец стека исполнения, что вам и нужно.
Если бы код был размещен на jsFiddle, то было бы проще проверить догадку.
Ответ написан
maxaon
@maxaon
В scope.$watch(attrs.scroller, function(){..., в качестве условия для наблюдения (watchExpression) вы используете "attrs.scroller" и видимо его значение не изменяется во время digest цикла. Ангуляр вызывает этот обработчик единожды, для инициализации.

В качестве watchExpression может быть либо выражение, либо вызываемая функция.
Используйте название изменяемой переменной в качестве watchExpression или функцию, которая возвращает какое-либо значение. Т.е. если размер зависит от 'quotesGrid' используйте его в качестве значения атрибута. (Не забывайте, что сравнение в watchExpression идет по $$hashKey для объектов)

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

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы