AngularJS Как принудительно обновить DOM после setTimeout

Блок кода ниже ожидает тайм-аута, затем выполняет HTTP-запрос с обещанием, а затем изменяет переменную области Angular Material на основе ответа. Он вызывается во время с ng-change в поле ввода. У меня также есть элемент с ng-hide="showStuff" в моем HTML, и, очевидно, когда $scope.showStuff становится ложным, я хочу, чтобы он сразу исчез. К сожалению, на самом деле он не исчезает, пока я не выберу что-нибудь еще в DOM.

Раньше я использовал промисы для изменения вещей в DOM, и они отлично работают. Почему DOM не обновляется сам по себе и как мне обойти это?

$scope.checkSomething = function() {
    // Use a timeout to prevent a checks from going off too rapidly
    if (queryTimeout) {
        clearTimeout(queryTimeout);
    }
    queryTimeout = setTimeout(function() {
        bluebird.bind({}).then(function() {
            return makeHttpRequest();
        }).then(function(res) {
            $scope.showStuff = res.data.length > 0;
        })
    }, 500);
}

person Christopher Waugh    schedule 02.08.2018    source источник
comment
Как вы делаете HTTP-запрос? Вы используете сервис AngularJS $http?   -  person Pop-A-Stash    schedule 03.08.2018


Ответы (2)


Используйте сервис $timeout, он возвращает обещание, интегрированное с контекстом выполнения AngularJS и его циклом дайджеста:

$scope.checkSomething = function() {
    // Use a timeout to prevent a checks from going off too rapidly
    if (queryTimeout) {
        $timeout.cancel(queryTimeout);
    }
    queryTimeout = $timeout(function() {
        return makeHttpRequest();
    }, 500);

    queryTimeout.then(function(res) {
        $scope.showStuff = res.data.length > 0;
    })
}

Это задержит выполнение makeHttpRequest на 500 миллисекунд. Служба $timeout возвращает обещание, которое будет использоваться для разрешения данных с сервера.

AngularJS изменяет обычный поток JavaScript, предоставляя собственный цикл обработки событий. Это разделяет JavaScript на классический и контекст выполнения AngularJS. Только операции, которые применяются в контексте выполнения AngularJS, получат выгоду от привязки данных AngularJS, обработки исключений, наблюдения за свойствами и т. д.

При использовании сервиса $timeout AngularJS завернутый setTimeout будет выполняться в контексте выполнения AngularJS.

Для получения дополнительной информации см.

person georgeawg    schedule 02.08.2018
comment
Все эти ответы хороши, но я отмечаю этот как правильный, потому что в нем упоминается отмена тайм-аута, поэтому я хотел использовать тайм-аут вместо обещаний. - person Christopher Waugh; 03.08.2018

Обнаружение изменений AngularJs (называемое «циклом дайджеста») не знает о запуске, если код не начал выполняться в контексте angular. Так что асинхронные вещи не так хорошо работают с этим. Вот почему angular предоставляет такие сервисы, как $timeout, $interval и $q, для выполнения асинхронных действий таким образом, чтобы он обрабатывал запуск цикла дайджеста за вас. Используйте их, и этот тип проблем будет чрезвычайно редким.

Если вы хотите использовать обещание другого типа, чем $q, вам нужно запустить цикл дайджеста вручную. Вы можете сделать это с помощью $scope.$apply(), если вы хотите, чтобы это произошло немедленно, или $scope .$applyAsync, если вы хотите, чтобы это произошло очень скоро, но не синхронно (полезно, если у вас может быть несколько вещей, вызывающих это, и вы хотите, чтобы они были объединены вместе).

Поэтому, если вы не хотите использовать $timeout и $q, вам нужно сделать что-то вроде этого:

setTimeout(function() {
    bluebird.bind({}).then(function() {
        return makeHttpRequest();
    }).then(function(res) {
        $scope.showStuff = res.data.length > 0;
        $scope.$apply();
    })
}, 500);
person Nicholas Tower    schedule 02.08.2018
comment
Я бы предпочел проверить, что @Christopher Waugh использует для создания HTTP-запроса, чем принудительно использовать $digest с помощью $apply(). Промисы сами по себе не должны запускать грязную проверку, независимо от того, какая библиотека используется. - person skyboyer; 02.08.2018
comment
AngularJS имеет собственный метод тайм-аута $timeout, поэтому вам не нужно использовать $scope.$apply(). Но да, меня больше интересовал бы HTTP-запрос - person Pop-A-Stash; 03.08.2018
comment
Используйте сервис $timeout вместо setTimeout, тогда $apply не нужен. Имейте в виду, что в большинстве мест (контроллеры, сервисы) $apply уже вызывали за вас. Явный вызов $apply необходим только при реализации пользовательских обратных вызовов событий или при работе с обратными вызовами сторонних библиотек. - person georgeawg; 03.08.2018