Страницы

Поиск по вопросам

воскресенье, 7 июля 2019 г.

Почему $watch не всегда срабатывает при изменении объекта?

var app = angular.module('testApp', ['ui.bootstrap', 'ui.select']); app.decorator('uiSelectMatchDirective', function($rootScope, $delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { elm.bind('click', function() { $rootScope.$broadcast('customSelect', scope.$selectMultiple.activeMatchIndex); }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.decorator('uiSelectMultipleDirective', function($delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { scope.$on('uis:select', function(event, item) { scope.$selectMultiple.activeMatchIndex = controller[0].selected.length; }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.controller('detailsController', function($scope, toolService) { $scope.assembly = { tools: [] }; $scope.$on('customSelect', function(event, value) { $scope.tool = $scope.assembly.tools[value]; console.log('from handler custom select'); }); $scope.$watch('tool', function(value) { console.log('from watch') }); $scope.onSelectTool = function(tool) { $scope.tool = $scope.assembly.tools[$scope.assembly.tools.indexOf(tool)]; }; $scope.tools = toolService.getTools(); }); app.factory('assemblyService', function() { var assemblies = [{ "Position": 1.00, "Tools": [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00, }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00 } ], "Runtime": 1.00, "Note": null }, { "Position": 5.00, "Tools": [{ "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 14, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true, "Sequance": 12, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, "Sequance": 6, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 9.00, "Tools": [{ "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 6.00, "Note": null }, { "Position": 11.00, "Tools": [{ "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 15.00, "Tools": [{ "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 } ], "Runtime": 3.00, "Note": null } ]; return { getAssemblies: () => { return assemblies.slice(); } }; }); app.factory('toolService', function() { var tools = [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, }, { "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false } ]; return { getTools: () => { return tools.slice(); } }; });

{{$item.OrderCode}}


В исходную директиву при помощи декоратора внесены незначительные изменения(создание события).
$rootScope.$broadcast('customSelect', scope.$selectMultiple.activeMatchIndex);
В контроллере обрабатываю это событие
$scope.$on('customSelect', function(event, value){ console.log('from custom select'); $scope.tool = $scope.assembly.tools[value]; });
так же в контроллере слежу за изменением tool при помощи $watch
$scope.$watch('tool', function(value){ console.log('from watch'); });
Выбираем любое кол-во инструментов в выпадающем списке, каждая добавленная позиция становится активной, применение класса btn-primary из бутстрапа.
При первом клике по одной из выбранных позиций в консоле видно что сработал обработчик события customSelect который в свою очередь меняет $scope.tool, но почему то не срабатывает обработчик из $watch, а если повторно кликнуть тот же элемент то в консоли видно что сработало оба обработчика.
Подскажите почему так происходит.


Ответ

Проблема в декораторе.
Если глянуть в исходники того же ngClick, можно отметить, что внутри обработчика jqLite вызывается $evalAsync, либо $apply
В твоем случае ты делаешь $broadcast, который, как видно из исходников не запускает $digest, следовательно нет проверки watch
Поэтому его нужно запускать самому, например с помощью .$apply()
var app = angular.module('testApp', ['ui.bootstrap', 'ui.select']); app.decorator('uiSelectMatchDirective', function($rootScope, $delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { elm.on('click', function(e) { $rootScope.$broadcast('customSelect', scope.$selectMultiple.activeMatchIndex); scope.$apply(); }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.decorator('uiSelectMultipleDirective', function($delegate) { var originalLinkFn = $delegate[0].link; $delegate[0].compile = function(element) { return function(scope, elm, attrs, controller) { scope.$on('uis:select', function(event, item) { scope.$selectMultiple.activeMatchIndex = controller[0].selected.length; }); originalLinkFn.apply($delegate, arguments); }; }; return $delegate; }); app.controller('detailsController', function($scope, toolService) { $scope.assembly = { tools: [] }; $scope.$on('customSelect', function(event, value) { $scope.tool = $scope.assembly.tools[value]; console.log('from handler custom select'); }); $scope.$watch('tool', function(value) { console.log('from watch') }); $scope.onSelectTool = function(tool) { $scope.tool = $scope.assembly.tools[$scope.assembly.tools.indexOf(tool)]; }; $scope.tools = toolService.getTools(); }); app.factory('assemblyService', function() { var assemblies = [{ "Position": 1.00, "Tools": [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00, }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 40.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 200.00 } ], "Runtime": 1.00, "Note": null }, { "Position": 5.00, "Tools": [{ "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 14, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true, "Sequance": 12, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, "Sequance": 6, "Overhang": 43.00, "AmountCuttingEdge": 2, "AmountPlates": 5, "Durability": 90.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 9.00, "Tools": [{ "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 70.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 6.00, "Note": null }, { "Position": 11.00, "Tools": [{ "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true, "Sequance": 14, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 55.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 180.00 } ], "Runtime": 40.00, "Note": null }, { "Position": 15.00, "Tools": [{ "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true, "Sequance": 14, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false, "Sequance": 6, "Overhang": 25.00, "AmountCuttingEdge": 0, "AmountPlates": 0, "Durability": 100.00 } ], "Runtime": 3.00, "Note": null } ]; return { getAssemblies: () => { return assemblies.slice(); } }; }); app.factory('toolService', function() { var tools = [{ "Id": 104, "Type": "Центровочное сверло D10 ∠90°", "OrderCode": "D5306100", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 53, "Type": "Термопатрон ", "OrderCode": "50 10 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 889, "Type": "Корпус фрезы D42 z5", "OrderCode": "5 42 367", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 750, "Type": "Пластина r2", "OrderCode": "04 67 896 R20 M40", "Vendor": "POKOLM", "IsConsumables": true }, { "Id": 890, "Type": "Патрон ", "OrderCode": "HSK 63-16-50", "Vendor": "KEMMLER", "IsConsumables": false, }, { "Id": 891, "Type": "Фреза D20 r1", "OrderCode": "ф20R1z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 223, "Type": "Термопатрон ", "OrderCode": "60 20 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 1241, "Type": "Фреза D12 r2", "OrderCode": "ф12R2z4 nACRo ТП", "Vendor": "ТЕХНОПОЛИС", "IsConsumables": true }, { "Id": 51, "Type": "Термопатрон ", "OrderCode": "50 12 A63 S", "Vendor": "POKOLM", "IsConsumables": false }, { "Id": 547, "Type": "Сверло D5.60", "OrderCode": "DH451056", "Vendor": "YG-1", "IsConsumables": true }, { "Id": 78, "Type": "Термопатрон ", "OrderCode": "50 06 A63 S", "Vendor": "POKOLM", "IsConsumables": false } ]; return { getTools: () => { return tools.slice(); } }; });

{{$item.OrderCode}}


Почему это срабатывает при последующих переключениях: внутри элемента с классом ui-select-match есть элемент с классом ui-select-match-item - у которого прописан ng-click
Таким образом при клике, сначала вызывается клик внутреннего элемента, проходит digest цикл, затем шлется твой broadcast и на этом все заканчивается. При следующем клике при проверке watch определяет, что tools изменился с предыдущего запуска digest - и отрисовывает, а затем в обработчике твоего события ты снова меняешь реальное значение tools.

Комментариев нет:

Отправить комментарий