2

Here is a website, on which user can control device via browser. The communication between the browser and middlewire is via signalr. The problem here is: when new message from signalr hub reached, I rebind the data to the collection, but the page didn't get refreshed.

Here is the directive:

//主框架
app.directive('mainPart',['$compile',function ($compile) {
return {
    restrict: 'AE',
    replace: true,
    scope: { options: "=" },
    link: function(scope, element, attrs) {
        scope.$watchCollection('options', function (newValue, oldValue) {
                console.log("I see a data change!");
        });
    },
    template: '<div class="panel panel-info" style="margin:10px 10px 10px 10px">'
            + '  <div class="panel-heading">'
            + '    <h3 class="panel-title glyphicon glyphicon-user"><strong>{{options.title}}</strong></h3>'
            + '  </div>'
            + '  <div class="panel-body">'
            + '    <controller-part options="options.controller"></controller-part>'
            + '    <led-part options="options.led"></led-part>'
            + '  </div>'
            + '</div>'
};
}]);

//可控制的控制器部分
app.directive('controllerPart', function () {
return {
    restrict: 'AE',
    replace: true,
    scope: { options: "=options" },
    template: '<div class="panel panel-default">'
            + '  <div class="panel-heading">'
            + '    <h3 class="panel-title">控制器部分</h3>'
            + '  </div>'
            + '  <div class="panel-body">'
            + '    <controller-instance options="options"></controller-instance>'
            + '  </div>'
            + '</div>'
};
});

//不可控制的指示灯部分
app.directive('ledPart', function () {
return {
    restrict: 'AE',
    replace: true,
    scope: { options: "=options" },
    template: '<div class="panel panel-default">'
            + '  <div class="panel-heading">'
            + '    <h3 class="panel-title">指示灯部分</h3>'
            + '  </div>'
            + '  <div class="panel-body">'
            + '    <led-instance options="options"></led-instance>'
            + '  </div>'
            + '</div>'
};
});

//控制器具体的路数
app.directive('controllerInstance', function () {
return {
    restrict: 'AE',
    replace: true,
    scope: { options: "=options" },
    template: '<div class="panel panel-success" style="float:left;margin-left:20px;" ng-repeat="controller in options">'
             + '   <div class="panel-heading">'
             + '       <h3 class="panel-title">{{controller.title}}(在线)</h3>'
             + '   </div>'
             + '   <div class="panel-body">'
             + '       <div class="btn-group" role="group">'
             + '           <button type="button"  ng-repeat="function in controller.functionlist" tag="{{function.functionorder}}" class="btn {{function.state|onlineConverter}} glyphicon {{function.functionicon}}">{{function.functionname}}</button>'
             //+ '           <button type="button" class="btn btn-default glyphicon glyphicon-off">停止</button>'
             //+ '           <button type="button" class="btn btn-default glyphicon glyphicon-chevron-up btn-success">正转</button>'
             //+ '           <button type="button" class="btn btn-default glyphicon glyphicon-chevron-down">反转</button>'
             + '       </div>'
             + '   </div>'
             + '</div>'
};
});

//指示器具体的路数
app.directive('ledInstance', function () {
return {
    restrict: 'AE',
    replace: true,
    scope: { options: "=options" },
    template: '<div class="panel panel-success" style="float:left;margin-left:20px;" ng-repeat="led in options">'
            + '  <div class="panel-heading">'
            + '     <h3 class="panel-title">{{led.title}}(在线)</h3>'
            + '  </div>'
            + '  <div class="panel-body">'
            + '     <div class="btn-group" role="group">'
            + '         <button type="button"  ng-repeat="function in led.functionlist" tag="{{function.functionorder}}" class="btn {{function.state|onlineConverter}} glyphicon {{function.functionicon}}">{{function.functionname}}</button>'
            //+ '         <button type="button" class="btn btn-success glyphicon glyphicon-eye-close">灭</button>'
            //+ '         <button type="button" class="btn btn-default glyphicon glyphicon-eye-open">亮</button>'
            + '     </div>'
            + ' </div>'
            + '</div>'
};
});

//此过滤器主要是为了过滤工作状态的,将true和false转变为具体的css样式。
app.filter('onlineConverter', function () {
return function (input) {
    if (input) {
        return "btn-success";
    }
    else {
        return "btn-default";
    }
}
});

Here is the Service part:

app.service('dataService', function () {
var getData = function () {
    /*
    数据结构中的state代表当前路是否是工作状态
    */
    var controllerData =
        {
            title: '中国联通对接设备',
            controller: [{
                title: '风机',
                functionlist: [{ functionname: '停止', functionicon: 'glyphicon-off', functionorder: '0', state: true }, { functionname: '正转', functionicon: 'glyphicon-chevron-up', functionorder: '1', state: false }, { functionname: '反转', functionicon: 'glyphicon-chevron-down', functionorder: '2', state: false }]
            }, {
                title: '湿帘',
                functionlist: [{ functionname: '停止', functionicon: 'glyphicon-off', functionorder: '0', state: false }, { functionname: '正转', functionicon: 'glyphicon-chevron-up', functionorder: '1', state: false }, { functionname: '反转', functionicon: 'glyphicon-chevron-down', functionorder: '2', state: true }]
            }, {
                title: '暖灯',
                functionlist: [{ functionname: '停止', functionicon: 'glyphicon-off', functionorder: '0', state: false }, { functionname: '高光', functionicon: 'glyphicon-chevron-up', functionorder: '1', state: true }, { functionname: '低光', functionicon: 'glyphicon-chevron-down', functionorder: '2', state: false }]
            }],
            led: [{
                title: '电源',
                functionlist: [{ functionname: '灭', functionicon: 'glyphicon-eye-close', functionorder: '0', state: false }, { functionname: '亮', functionicon: 'glyphicon-eye-open', functionorder: '1', state: true }]
            }, {
                title: '转轴',
                functionlist: [{ functionname: '正转', functionicon: 'glyphicon-eye-close', functionorder: '0', state: true }, { functionname: '反转', functionicon: 'glyphicon-eye-open', functionorder: '1', state: false }]
            }, {
                title: '浇灌',
                functionlist: [{ functionname: '关闭', functionicon: 'glyphicon-eye-close', functionorder: '0', state: true }, { functionname: '打开', functionicon: 'glyphicon-eye-open', functionorder: '1', state: false }]
            }, {
                title: '电压',
                functionlist: [{ functionname: '正常', functionicon: 'glyphicon-eye-close', functionorder: '0', state: true }, { functionname: '异常', functionicon: 'glyphicon-eye-open', functionorder: '1', state: false }]
            }]
        };

    return controllerData;
}

return {
    controllerData: getData,
};
});

app.service('hubService', ['dataService', function (dataService) {

//添加对自动生成的Hub的引用
var chat = $.connection.chatHub;

//启动链接
$.connection.hub.start().done(function () { });

var getData = function () {
    return dataService.controllerData();
}

return {
    commandReceived: function(callback)
    {
        if(callback)
        {
            //调用Hub的callback回调方法
            chat.client.printCommand = function (command) {
                var data = getData();
                var obj = { data: data, command: command };
                return callback(obj);
            }
        }
    },
    controllerData:getData
};

}]);

Here is the controller part:

var mainController = app.controller('dsController', ['$scope', 'hubService', function ($scope, hubService) {

var currentData = hubService.controllerData();

$scope.controllerData = currentData;

hubService.commandReceived(function (result) {
    debugger;

    var command = result.command;
    var data = result.data;

    if (command != undefined) {
        if (command.localeCompare("aaaa")==0)
            data.controller[1].title = "哈哈哈";
        else if (command.localeCompare("bbbb") == 0)
            data.controller[0].title = "呵呵呵";
    }

    $scope.controllerData = data;
});

}]);

Here is the screen shot: enter image description here

When there is new message reached, then I can see that the hubService.commandReceived was triggered, then I re-bind the data to the collection. Here is the screen shot: enter image description here

But the problem is, after re-binding the data,the page is not changed at all!

I have checked many articles on the stackoverflow, seems many problems were solved by adding link function in the directive, so I did. But it didn't work for me.

Maybe I think the problem should be placed in the directive I used? Or the service method I used to handle the signalr callback message?

Anyone who can help me ?

Edit:2015-12-28 22:06:01

Thx for Walt's response, I will have a try, meantime, hope you guys' can provide more advice. I will wait online. thx again.

CharlieShi
  • 888
  • 3
  • 17
  • 43

1 Answers1

3

You need to call

 $scope.$apply(function {
     $scope.controllerData = data;
 })

to notify angular that your data has changed as the notification from signalR is out of the angularjs scope life cycle.

$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

You can read up on it here

You also need to check if the $digest is in progress in some cases not to receive an error.

Community
  • 1
  • 1
Tjaart van der Walt
  • 5,149
  • 2
  • 30
  • 50
  • So It means that sometimes even we watched the collection, then angularjs will have the chance to ignore the change until we have to use $apply or $digest to consume it again, right? – CharlieShi Dec 28 '15 at 14:00
  • So I think maybe the commandReceived method in hubservice is not correctly placed, Maybe I shouldn't use this callback method. – CharlieShi Dec 28 '15 at 14:04
  • Have checked with some guys for the async callback, I need to add $scope.$digest(); in the end of the commandReceived to trigger its auto-check mechanism. – CharlieShi Dec 28 '15 at 14:25