условия гонки с директивой angularjs

0

У меня есть директива, которая имеет 2 атрибута:
1. объект заголовка, переданный от контроллера.
2. Функция обмена, переданная с контроллера.

scope: {
  title: '=',
  change: '&'
}


//on the link function I update the object, and call the function.

link: function(scope) {
    scope.setValue = function() {
      scope.title = "Yaniv"
      scope.change();
    };
},

так или иначе, хотя я написал это противоположным образом, кажется, что сначала он вызывает функцию, и только тогда он обновляет заголовок. Каков наилучший способ преодолеть это? Я уже думал об использовании setTimeout, и он действительно работал над этим. но, интересно, почему эта проблема произошла, и есть ли здесь более чистое решение. прикрепленный скрипт JS: http://jsfiddle.net/sz82r7pg/

Теги:

2 ответа

1

Первое, что нужно отметить, это то, что директива имеет свой собственный объект области выделения, из которых атрибут title привязан к заголовку области управления. Эта привязка данных в основном выполняется с помощью часов, и вы можете знать, что обнаружение изменений с использованием часов происходит не мгновенно, происходит внутри цикла дайджеста, что всегда происходит после внесения изменений, а стек вызовов вызывается (в следующей задаче микро/макрос Я точно не знаю, что, но не имеет значения здесь). Однако вызов функции происходит мгновенно. Итак, что происходит, это следующее:

  • Установить заголовок области действия директивы
  • Изменение вызова, которое вызывает foo, заголовок контроллера - это старый
  • После того, как угловой вызов вызывает цикл дайджеста, замечает изменение в области действия директивы и распространяет его на область контроллера

Это вполне ожидаемое поведение. Самый простой способ в вашем случае - не использовать свое собственное обнаружение изменений, но использовать встроенный в угловой:

angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
  $scope.title = 'Ori';
  $scope.$watch("title", function(newValue, oldValue) {
    if (newValue !== oldValue) {
      alert(newValue);
    }
  });
})
.directive('zippy', function() {
  return {
    restrict: 'AE',
    scope: {
      title:'=zTitle'
    },
    template: '<button ng-click="setValue()">What would be the value of title??</button>',
    link: function(scope) {
      scope.setValue = function () {
        scope.title = "Yaniv";
      };
    }
  }
});
button {
  font-size:24px;
  margin:40px;
  padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    <div zippy z-title="title"></div>
  </div>
</div>

При написании этого фрагмента я понял, что вы используете древнюю версию angularjs в скрипке (1.0.2). Мне пришлось сделать небольшую модификацию для работы с 1.5.x.

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

angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
  $scope.title = 'Ori';
  $scope.foo = function(newTitle) {
    alert(newTitle);
  };
})
.directive('zippy', function() {
  return {
    restrict: 'AE',
    scope: {
      change: '&'
    },
    template: '<button ng-click="setValue()">What would be the value of title??</button>',
    link: function(scope) {
      scope.setValue = function () {
        scope.change({title:"Yaniv"});
      };
    }
  }
});
button {
  font-size:24px;
  margin:40px;
  padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    <div zippy change="foo(title)"></div>
  </div>
</div>

ОБНОВИТЬ

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

angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
  $scope.title = 'Ori';
  $scope.fooApiImpl = {
    title: "Ori",
    change: function() {
      alert($scope.fooApiImpl.title);
    }
  };
})
.directive('zippy', function() {
  return {
    restrict: 'AE',
    scope: {
      fooApi: '='
    },
    template: '<button ng-click="setValue()">What would be the value of title??</button>',
    link: function(scope) {
      scope.setValue = function () {
        scope.fooApi.title = "Yaniv";
        scope.fooApi.change();
      };
    }
  }
});
button {
  font-size:24px;
  margin:40px;
  padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    <div zippy foo-api="fooApiImpl"></div>
  </div>
</div>
  • 0
    Спасибо, Тамас, за твой добрый ответ. Использование $ watch на контроллере - это то, что я делал в первую очередь, но для меня это выглядело не слишком элегантно. поэтому я подумал, что вызов функции изменения будет лучшим, но приемлемым решением. Другой способ - это просто обходной путь, так как я хотел изменить исходный объект заголовка в родительском контроллере, а не отправлять его в качестве параметра. Благодарю.
  • 0
    @Yaniv Есть способы сделать это, но это признак плохих дизайнерских решений. Смотрите мое обновление (через минуту)
Показать ещё 2 комментария
1

title двусторонняя привязка обновляется в родительской области в конце дайджеста. До тех пор изменение значения title не распространяется из области действия в родительскую область.

setValue вызывается ng-click и запускается во время дайджеста. Это означает, что change следует вызывать после текущего дайджеста с помощью setTimeout или $imeout:

   scope.setValue = function () {
                scope.title = "Yaniv"
                $imeout(() => scope.change());
   };

Что указывает на плохой запах и проблему XY, потому что обновление title уже привязано к перевариванию, а change избыточно. Это работа для рамки. изменения title следует отслеживать с помощью

$scope.$watch('title', (newValue, oldValue) => {
  if (newValue === oldValue) return;
  ...
});

в родительской области.

  • 0
    Спасибо за ваш четкий ответ. Я не хотел использовать $ watch, поэтому я придумал функцию change и решил эту проблему.
  • 0
    @Yaniv Если у вас есть change: '&' , новое значение может быть передано с помощью scope.change(val) , `title: '='` является избыточным и наоборот. В основном это либо тот, либо другой. Есть ли причина вашего решения? Потому что $scope.$watch - идиоматическое, более чистое решение .

Ещё вопросы

Сообщество Overcoder
Наверх
Меню