在服务更改时更新角JS指令。

时间:2023-01-20 19:43:05

I'm attempting to follow this SO answer explaining how to render a recursive JSON structure using a directive. However, unlike the answer provided, my data is not known when the DOM is loaded and Angular runs for the first time.

我尝试遵循这个答案,解释如何使用指令呈现递归JSON结构。然而,与提供的答案不同的是,我的数据不知道何时加载DOM并第一次运行角度。

Instead, my data is retrieved from an HTML input field and stored in an Angular Service (when the user submits the form).

相反,我的数据从HTML输入字段检索并存储在一个有棱角的服务中(当用户提交表单时)。

How can I keep an Angular Directive up-to-date when the Service's data is modified?

当服务的数据被修改时,我如何保持一个角度指示是最新的?


Update in response to answer

回复更新

@musically_ut provided an excellent answer that has helped, but revealed a related problem, preventing an implementation (updated here).

@musically_ut提供了一个很好的答案,这对您有所帮助,但也揭示了一个相关的问题,阻止了实现(在这里更新)。

The directive renders HTML that contains Angular {{expressions}} which access data stored in $scope. Since the original solution was to $watch when the service had it's data ready. How can I ensure the 'new' data is added to $scope before the directive renders?

该指令呈现的HTML包含有角的{{{{表达式},用于访问存储在$scope中的数据。因为最初的解决方案是在服务有数据准备时看表。如何确保在指令呈现之前,新数据被添加到$scope中?

An overview of the architecture and flow is:

体系结构和流程的概述如下:

  1. ControllerA -> Get input from user
  2. ControllerA ->获取用户输入
  3. ControllerA -> Use service to transform data
  4. ControllerA ->使用服务转换数据
  5. ControllerB -> $watch for changes in service
  6. ControllerB -> $监视服务更改。
  7. Directive -> $watch for changes in service
  8. 指令-> $监视服务的变化
  9. ControllerB -> Add data to $scope
  10. ControllerB ->将数据添加到$scope。
  11. Directive -> Display transformed data (from service) using directives
  12. 指令->显示转换数据(从服务)使用指令

The problem is between steps 5 and 6. The directive renders {{expressions}} before ControllerB has added the data to $scope. Even if this did work, it feels way too complex and 'hacky'.

问题在步骤5和步骤6之间。该指令在ControllerB将数据添加到$scope之前呈现{{{{表达式}}。即使这样做了,也会让人觉得太复杂,太“粗糙”了。

In fact, to regress, I'm using $watch in ControllerB to listen for when the transformed data is ready in a service. Even this feels a little overkill (the service makes no asynchronous calls).

实际上,为了回归,我在ControllerB中使用$watch来侦听转换后的数据何时在服务中就绪。即便如此,这也有点过了头(该服务不进行异步调用)。

2 个解决方案

#1


17  

You can inject the service while defining the directive and then set up a $watch on the data. So in your case:

您可以在定义指令时注入服务,然后在数据上设置$watch。所以在你的情况中:

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

This will watch the data for change and run the code each time the data changes.

这将监视数据的更改,并在每次数据更改时运行代码。


However, if the data does not change after being set once, a better way (more efficient) would be to return a promise from the service ($http methods returns a promise directly or you can create one using the $q service) and then add your computation as a continuation to the data.

然而,如果数据不改变设置一次,后一种更好的方式(效率)会返回一个从服务承诺(http方法返回一个承诺美元直接或您可以创建一个使用美元问服务),然后添加你作为一个延续的计算数据。

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

#2


0  

I have written a short function that let you put data in Object without replace it. You can use it in you service. This way youu don't need to use watch. You can use the normal digest loop of Angular. For example, see this simple controller & service:

我写了一个简短的函数,让你把数据放在对象中而不替换它。你可以在你的服务中使用它。这样你就不用手表了。你可以使用正常的角化循环。例如,看看这个简单的控制器和服务:

Demo: JSFidde - http://jsfiddle.net/y09kx7f7/1/ with assert tests

演示:JSFidde - http://jsfiddle.net/y09kx7f7/1/带有断言测试

Controller:

控制器:

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

The controller will be chnage automatically without watch every time the user name changed!

每次用户名更改时,控制器将自动进行检查,不受监视!

Service

服务

.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
})

The cloneInto function that I written:

我写的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]
        }
    }
}

#1


17  

You can inject the service while defining the directive and then set up a $watch on the data. So in your case:

您可以在定义指令时注入服务,然后在数据上设置$watch。所以在你的情况中:

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

This will watch the data for change and run the code each time the data changes.

这将监视数据的更改,并在每次数据更改时运行代码。


However, if the data does not change after being set once, a better way (more efficient) would be to return a promise from the service ($http methods returns a promise directly or you can create one using the $q service) and then add your computation as a continuation to the data.

然而,如果数据不改变设置一次,后一种更好的方式(效率)会返回一个从服务承诺(http方法返回一个承诺美元直接或您可以创建一个使用美元问服务),然后添加你作为一个延续的计算数据。

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

#2


0  

I have written a short function that let you put data in Object without replace it. You can use it in you service. This way youu don't need to use watch. You can use the normal digest loop of Angular. For example, see this simple controller & service:

我写了一个简短的函数,让你把数据放在对象中而不替换它。你可以在你的服务中使用它。这样你就不用手表了。你可以使用正常的角化循环。例如,看看这个简单的控制器和服务:

Demo: JSFidde - http://jsfiddle.net/y09kx7f7/1/ with assert tests

演示:JSFidde - http://jsfiddle.net/y09kx7f7/1/带有断言测试

Controller:

控制器:

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

The controller will be chnage automatically without watch every time the user name changed!

每次用户名更改时,控制器将自动进行检查,不受监视!

Service

服务

.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
})

The cloneInto function that I written:

我写的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]
        }
    }
}