Обновление директивы JS Angular при изменении Сервиса

Я пытаюсь выполнить этот ответ SO, объясняя, как рендерировать рекурсивную структуру JSON с помощью директивы. Однако, в отличие от предоставленного ответа, мои данные не известны при загрузке DOM и Angular выполняется в первый раз.

Вместо этого мои данные извлекаются из поля ввода HTML и сохраняются в службе Angular (когда пользователь отправляет форму).

Как сохранить актуальную директиву Angular при изменении данных службы?


Обновление в ответ на ответ

@musically_ut предоставил отличный ответ, который помог, но выявил связанную с этим проблему, предотвратив реализацию (обновленный здесь).

Директива отображает HTML, содержащий Angular {{expressions}}, которые обращаются к данным, хранящимся в $scope. Поскольку исходное решение было $watch, когда служба предоставила данные. Как я могу обеспечить добавление "новых" данных в $scope до отображения директивы?

Обзор архитектуры и потока:

  • ControllerA → Получить вход от пользователя
  • ControllerA → Использовать службу для преобразования данных
  • ControllerB$watch для изменений службы
  • Directive$watch для изменений службы
  • ControllerB → Добавить данные в $scope
  • Directive → Отобразить преобразованные данные (из службы), используя директивы

Проблема заключается в шагах 5 и 6. Директива отображает {{expressions}}, прежде чем ControllerB добавит данные в $scope. Даже если это сработало, оно кажется слишком сложным и "взломанным".

На самом деле, чтобы регрессировать, я использую $watch в ControllerB для прослушивания, когда преобразованные данные готовы в службе. Даже это кажется немного переполненным ( сервис не выполняет асинхронные вызовы).

Ответы

Ответ 1

Вы можете ввести службу при определении директивы, а затем настроить $watch на данные. Итак, в вашем случае:

.directive('tree', function ($compile, myService) {
    return {
        // ...
        link: function (scope, element, attrs) {
            scope.$watch(function () {
                return myService.getData();
            },
            function (newVal) {
                if (typeof newVal !== 'undefined') {
                    // ...
                }
            }
        });
    }
});

Это будет следить за данными для изменения и запускать код при каждом изменении данных.


Однако , если данные не изменяются после установки один раз, лучший способ (более эффективный) - вернуть обещание от службы (методы $http возвращают обещание напрямую или вы может создать его с помощью службы $q), а затем добавить ваши вычисления в качестве продолжения данных.

.directive('tree', function ($compile, myService) {
    return {
        // ...
        link: function (scope, element, attrs) {
            myService.getData().then(function (data) {
                // ...
            }, function (err) {
                // Handle error
            });
        }
    }
});

Ответ 2

Я написал короткую функцию, которая позволяет помещать данные в Object без замены. Вы можете использовать его в своем сервисе. Таким образом, вам не нужно использовать часы. Вы можете использовать обычный цикл дайджеста Angular. Например, посмотрите этот простой контроллер и службу:

Демо: JSFidde - http://jsfiddle.net/y09kx7f7/1/ с утверждать тесты

Контроллер:

app.controller('dem',function($scope,me){
    $scope.user=me.user;   // {name:'user name'}
})

Контроллер будет автоматически переключаться без просмотра при каждом изменении имени пользователя!

Сервис

.factory('me',function($timeout){
    var obj={}
    obj.user={name:'hello'}
    $timeout(function(){  //Example of change the data
        newUser={name:'israel'}
        cloneInto(obj.user,newUser)
    },1000)
    return obj
})

Функция cloneInto, которую я написал:

// The function it is like a=b, 
// but keeping the object in his original memory position.
function cloneInto(a,b){
    var i
    for (i in a){
        if(typeof a[i]!='object') //For supporting deep-copy
           delete a[i]
    }
    for (i in b){
        if (typeof b[i]=='object'){
            if(!a[i]) a[i]={}
            cloneInto(a[i],b[i])
        }
        else{
            a[i]=b[i]
        }
    }
}