Подтвердить что ты не робот

Angularjs обещает не привязываться к шаблону в 1.2

После обновления до 1.2, promises, возвращаемый моими службами, ведет себя по-другому... Простой сервис myDates:

getDates: function () {
           var deferred = $q.defer();

            $http.get(aGoodURL).
                 success(function (data, status, headers, config) {
                     deferred.resolve(data);  // we get to here fine.
            })......

В более ранней версии я мог просто сделать, в моем контроллере:

$scope.theDates = myDates.getDates();

и promises, возвращенный из getDates, может быть привязан непосредственно к элементу Select. Теперь это не работает, и я вынужден предоставить обратный вызов по обещанию в моем контроллере или данные не свяжутся:

$scope.theDates = matchDates.getDates();
$scope.theDates.then(function (data) {
      $scope.theDates = data;  // this wasn't necessary in the past

Документы все еще говорят:

$q promises распознаются движком шаблонов в angular, что означает, что в шаблонах вы можете рассматривать promises, прикрепленные к области видимости, как если бы они были результирующими значениями.

Они (promises) работали в более старых версиях Angular, но в автоматическом привязке 1.2 RC3 не удалось во всех моих простых сервисах... любые идеи о том, что я могу делать неправильно.

4b9b3361

Ответ 1

В версии 1.2.0-rc3 есть изменения, в том числе вы упомянули:

AngularJS 1.2.0-rc3 жестокое-дергание фиксирует ряд высокоприоритетных проблемы в $compile и $animate и прокладывают путь для 1.2.      В этом выпуске также представлены некоторые важные изменения, которые в некоторых случаях могут нарушать ваши директивы и шаблоны. пожалуйста обязательно прочитайте журнал изменений, чтобы понять эти изменения и узнать как перенести код, если это необходимо.      Полную информацию в этом выпуске см. В changelog.

В журнале изменений есть описание:

$синтаксического анализа:

  • из-за 5dc35b52, $parse и шаблоны вообще будут больше не разворачиваться promises. Эта функция устарела и если это абсолютно необходимо, оно может быть повторно включено в переходный период через $parseProvider.unwrapPromises(true) api.
  • из-за b6a37d11, функция добавлена ​​в rc.2, которая разворачивает возвращаемые значения из функций, если значения promises (если обезвреживание обещаний включен - см. предыдущий пункт), был отменен из-за нарушения популярности шаблон использования.

Ответ 2

Как уведомления @Nenad, promises больше не разыменовываются автоматически. Это одно из самых причудливых решений, которые я когда-либо видел, поскольку он молча удаляет функцию, на которую я полагался (и это была одна из уникальных точек продажи angular для меня, меньше - больше). Поэтому мне потребовалось немало времени, чтобы понять это. Тем более, что ресурсная структура по-прежнему работает нормально. Вдобавок ко всему, это также кандидат на выпуск. Если им действительно нужно было осуждать это (аргументы звучат очень слабо), они могли бы хотя бы дать льготный период, когда были предупреждения, прежде чем они молча закрыли его. Хотя обычно он очень впечатлен angular, это большой минус. Я не удивлюсь, если это действительно будет отменено, хотя до сих пор существует относительно небольшой протест.

В любом случае. Каковы решения?

  • Всегда используйте then() и назначьте $scope в методе then

    function Ctrl($scope) {
       foo().then( function(d) { $scope.d = d; });
    )
    
  • вызов значения через функцию разворота. Эта функция возвращает поле в обещании и устанавливает это поле через метод then. Поэтому он будет undefined, пока обещание не будет разрешено.

    $rootScope.unwrap = function (v) {
      if (v && v.then) {
        var p = v;
        if (!('$$v' in v)) {
          p.$$v = undefined;
          p.then(function(val) { p.$$v = val; });
        }
        v = v.$$v;
      }
      return v;
    };
    

    Теперь вы можете назвать это:

    Hello {{ unwrap(world) }}.
    

    Это от http://plnkr.co/edit/Fn7z3g?p=preview, у которого нет связанного с ним имени.

  • Установите $parseProvider.unwrapPromises(true) и живите с сообщениями, которые вы можете отключить с помощью $parseProvider.logPromiseWarnings(false), но лучше знать, что они могут удалить функциональные возможности в следующей версии.

Sigh, 40 лет Smalltalk имел сообщение become, которое позволяло вам переключать ссылки на объекты. promises, как они могли бы быть...

UPDATE:

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

Предполагая, что мне нужен объект 'x', и есть какой-то способ получить этот объект удаленно. Сначала я проверю кеш на "x". Если есть объект, я возвращаю его. Если такой объект не существует, я создаю фактический пустой объект. К сожалению, это требует, чтобы вы знали, будет ли это массив или хэш/объект. Я помещаю этот объект в кеш, поэтому будущие вызовы могут его использовать. Затем я запускаю удаленный вызов, а при обратном вызове я копирую данные, полученные из удаленной системы в созданном объекте. Кэш гарантирует, что повторные вызовы метода get не создают много удаленных вызовов для одного и того же объекта.

 function getX() {
   var x = cache.get('x');
   if ( x == undefined) {
      cache.put('x', x={});
      remote.getX().then( function(d) { angular.copy(d,x); } );
   }
   return x;
 }

Еще одна альтернатива - предоставить метод get с назначением объекта:

 function getX(scope,name) {
   remote.getX().then( function(d) { 
       scope[name] = d;
   } );
 }       

Ответ 3

Вы всегда можете создать общую службу angular и поместить туда метод распаковки, который воссоздает работу старого promises. Вот пример метода:

var shared = angular.module("shared");

shared.service("Common", [
    function () {

        // [Unwrap] will return a value to the scope which is automatially updated. For example,
        //      you can pass the second argument an ng-resource call or promise, and when the result comes back
        //      it will update the first argument. You can also pass a function that returns an ng-resource or
        //      promise and it will extend the first argument to contain a new "load()" method which can make the
        //      call again. The first argument should either be an object (like {}) or an array (like []) based on
        //      the expected return value of the promise.
        // Usage: $scope.reminders = Common.unwrap([], Reminders.query().$promise);
        // Usage: $scope.reminders = Common.unwrap([], Reminders.query());
        // Usage: $scope.reminders = Common.unwrap([], function() { return Reminders.query(); });
        // Usage: $scope.reminders.load();
        this.unwrap = function(result, func) {
            if (!result || !func) return result;

            var then = function(promise) {
                //see if they sent a resource
                if ('$promise' in promise) {
                    promise.$promise.then(update);
                }
                //see if they sent a promise directly
                else if ('then' in promise) {
                    promise.then(update);
                }
            };

            var update = function(data) {
                if ($.isArray(result)) {
                    //clear result list
                    result.length = 0;
                    //populate result list with data
                    $.each(data, function(i, item) {
                        result.push(item);
                    });
                } else {
                    //clear result object
                    for (var prop in result) {
                        if (prop !== 'load') delete result[prop];
                    }
                    //deep populate result object from data
                    $.extend(true, result, data);
                }
            };

            //see if they sent a function that returns a promise, or a promise itself
            if ($.isFunction(func)) {
                // create load event for reuse
                result.load = function() {
                    then(func());
                };
                result.load();
            } else {
                then(func);
            }

            return result;
        };
    }
]);

Это в основном работает так, как старый promises сделал и автоматически разрешает. Однако, если второй аргумент является функцией, он имеет дополнительное преимущество добавления метода ".load()", который может перезагрузить значение в область.

angular.module('site').controller("homeController", function(Common) {
    $scope.reminders = Common.unwrap([], Reminders.query().$promise);
    $scope.reminders = Common.unwrap([], Reminders.query());
    $scope.reminders = Common.unwrap([], function() { return Reminders.query(); });
    function refresh() {
        $scope.reminders.load();
    }
});

Ответ 4

Это были хорошие ответы и помогли мне найти мою проблему при обновлении angular, и моя автоматическая разворачивание promises перестала работать.

Рискуя быть избыточным с Питером Криенсом, я нашел, что этот шаблон работает для меня (это простой пример простого размещения на странице нескольких цитат известных людей).

Мой контроллер:

angular.module('myModuleName').controller('welcomeController',
function ($scope, myDataServiceUsingResourceOrHttp) {

    myDataServiceUsingResourceOrHttp.getQuotes(3).then(function (quotes) { $scope.quotes = quotes; });
}
);

Моя страница:

...
<div class="main-content" ng-controller="welcomeController">
...
<div class="widget-main">
    <div class="row" ng-repeat="quote in quotes">
        <div class="col-xs-12">
            <blockquote class="pull-right">
                <p>{{quote.text}}</p>
                <small>{{quote.source}}</small>
            </blockquote>
                </div>
    </div>
</div>
...