Одноразовая привязка компонента angular из $http показывает неопределенное значение

Я новичок в Ангуляре. Я пытаюсь использовать компоненты (1.6). В родительском элементе у меня есть $http.get, который получает данные от службы, а затем присваивает ответ переменной $scope. Эта переменная области действия передается дочернему компоненту с помощью односторонней привязки <. В JavaScript, если я предупреждаю переданную переменную, я получаю «неопределенное», однако шаблон html в дочернем элементе показывает переменную. Это похоже на то, что происходит состояние гонки, и я не знаю, как сказать ему подождать, пока данные из службы не будут загружены.

В моем parent.js:

(function (angular) {
    'use strict';
    $http.get("http://localhost:52422/api/PayOffYourCc")
    .then(function mySucces(response) {
        $scope.baseline = response.data;

    }
    ,
    function myError(respone) {
        $scope.baseline = response.statusText;

    }
    );
})(window.angular);

В моем родительском HTML-шаблоне:

<thermometer baseline="baseline"></thermometer>

В моем дочернем компоненте:

(function (angular) {
    'use strict';

    function drawChart(baselineVal) {
        alert(baselineVal);
    }

    function ThermometerController($scope) {
        var ctrl = this;
        ctrl.$onInit = function () {
            drawChart(ctrl.baseline);
        };


    }

    angular.module('payOffYourCcApp').component('thermometer', {
        templateUrl: '../PayOffYourCC/partials/thermometer.html',
        transclude: true,
        controller: ThermometerController,
        bindings: {
            baseline: '<'
        }
    });
})(window.angular);

В моем дочернем html-шаблоне:

<div>
    baseline:{{$ctrl.baseline}}
</div>

В html {{$ctrl.baseline}} отображается нормально, но когда я предупреждаю об этом в .js, это undefined. Почему это? Как я могу убедиться, что {{$ctrl.baseline}} находится в области действия до загрузки javascript?


person Leon Gentle    schedule 19.12.2016    source источник
comment
Как только функция обратного вызова http завершается и базовому уровню присваивается значение, она запускает цикл дайджеста, который соответствующим образом обновляет ваше представление. Однако операция оповещения происходит синхронно - до того, как операция http сможет завершиться.   -  person IAmDranged    schedule 19.12.2016
comment
Большое спасибо за все решения. Теперь я понимаю это намного лучше, однако я решил попробовать react.js. У меня все еще есть проблемы с обратными вызовами http и условиями гонки, но я дам вам знать, как я справлюсь.   -  person Leon Gentle    schedule 23.12.2016
comment
На самом деле, были и другие подобные проблемы с React. Все дело в жизненном цикле компонентов. Я вернулся к использованию Angular - благодаря ответу, приведенному ниже.   -  person Leon Gentle    schedule 12.01.2017


Ответы (3)


Используйте хук жизненного цикла $onChanges:

function ThermometerController($scope) {
    var ctrl = this;
    /* REPLACE THIS
    ctrl.$onInit = function () {
        drawChart(ctrl.baseline);
    }; */
    // WITH THIS
    ctrl.$onChanges = function (changesObj) {
        if (changesObj.baseline && changesObj.baseline.currentValue) {
            drawChart(changesObj.baseline.currentValue);
        };
    };
}

Контроллеру необходимо дождаться поступления данных с сервера. Используя хук жизненного цикла $onChanges, функция drawChart будет вызываться, когда данные станут доступными, и будет вызываться при последующих обновлениях.

Для получения дополнительной информации см. Справочник API всеобъемлющей директивы AngularJS — Life- Циклические хуки.

person georgeawg    schedule 19.12.2016

С компонентом Angular вы должны привилегировать связь от дочернего элемента к родительскому, поскольку он позволяет очень дешевую привязку (&)

Вы можете общаться от родителя к ребенку, но это дороже (=).
Я приведу вам пример того, как это сделать.
Это не проверенное решение, но у вас должна быть идея.

Вы должны изменить своего родителя, чтобы иметь дочерний API для передачи данных:

JS-родитель:

(function (angular) {
    'use strict';
    $http.get("http://localhost:52422/api/PayOffYourCc")
    .then(function mySucces(response) {
        $scope.baseline = response.data;
        $ctrl.apiChild.transmit(response.data);
    }
    ,
    function myError(respone) {
        $scope.baseline = response.statusText;
    }
    );
})(window.angular);

Родитель HTML:

<thermometer api="$ctrl.apiChild"></thermometer>

Измените своего ребенка, чтобы он имел функцию для получения данных от родителя, а также измените привязку на "=" :

JS-ребенок:

(function (angular) {

'use strict';

function drawChart(baselineVal) {
    alert(baselineVal);
}

function ThermometerController($scope) {
    var ctrl = this;
    ctrl.$onInit = function () {
        drawChart(ctrl.baseline);
        ctrl.api = {};
        ctrl.api.transmit = ctrl.transmitData;
    };

   this.transmitData = function transmitData(data){
        // here you get the data from the parent to the child
   }

}

angular.module('payOffYourCcApp').component('thermometer', {
    templateUrl: '../PayOffYourCC/partials/thermometer.html',
    transclude: true,
    controller: ThermometerController,
    bindings: {
        api : '='
    }
});
})(window.angular);
person davidxxx    schedule 19.12.2016

Это результат того, что запросы $http являются асинхронными. Предупреждение, которое происходит, когда дочерний компонент инициализируется, печатает undefined, потому что в этот момент запрос $http, который извлекает данные, еще не вернулся. Однако, поскольку вы используете привязку >, шаблон в вашем дочернем компоненте будет обновлен с правильным значением, как только запрос будет разрешен (что чертовски быстро), поэтому вы никогда не увидите, что undefined действительно напечатано в шаблоне. На самом деле, я не думаю, что angular будет печатать undefined, я думаю, что он просто пустой. Итак, на ваш взгляд, это выглядит так, как будто оно имеет правильное значение сразу, когда на самом деле оно было на мгновение неопределенным во время разрешения запроса $http.

person Aaron Pool    schedule 19.12.2016