javascript 角度指令 + $observe

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19363583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 15:19:34  来源:igfitidea点击:

Angular Directive + $observe

javascriptangularjs

提问by Quibblesome

I seem to have mis-understood how $observe works. From the following example (plunker link).

我似乎误解了 $observe 的工作原理。从以下示例(plunker 链接)。

HTML

HTML

<html ng-app="record">

  <head>
    <script data-require="angular.js@*" data-semver="1.2.0-rc2" src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="IndexController">
  <input type="text" value="{{src}}"/>
  <input type="text" value="{{RecordAddress}}"/>
  <input type="text" value="{{FlashVars}}"/>
  <input type="button" ng-click="handleClick()" value="click me"/>
    <record src="{{src}}"></record>
  </body>

</html>

JS

JS

angular.module('record',[])
.controller("IndexController", function($scope)
{
  console.log('controller called')
  $scope.handleClick = function()
  {
    console.log('handleClick - called');
    $scope.RecordAddress = 'rtmp://thisIsAnAddress';
    $scope.FlashVars = 'userid=SomeId&filename=http://localhost/Content/ThisIsAnAddress/player.swf&mediaFormat=_video.mp4&mediaServerAddress=rtmp://ThisIsAnAddress&culture=en-GB'
    $scope.src = $scope.RecordAddress+ '`' + $scope.FlashVars;
  }
})
.directive('record', function ($location) {
            return {
                restrict: 'E',
                scope: {
                    current: '=current'
                },
                link: function ($scope, element, attr) {
                    console.log('record directive - called');
                    console.log(attr);

                    attr.$observe('src', function(value)
                        {
                            console.log('record observe callback called!');
                            console.log('The value is: '+ value);
                            if(value && value !='recordValues')
                            {
                                var values = value.split('`');
                                console.log('video values:')
                                console.log(values);
                                element.html('<object position="relative" width="519px" height="520px" data="'+values[0]+'" type="application/x-shockwave-flash"><param name="FlashVars" value="'+values[1]+'" /><param name="AllowScriptAccess" value="always" /></object>');
                            }
                            else
                            {
                                element.html("<div>Please wait</div>")
                            }
                        }
                    );
                }
            }
        });

I'd expect the "object" to be rendered when the button is clicked as opposed to the "please wait" that should appear when the settings are not ready. However it is not called when the "src" attribute is seemingly updated. Could somebody explain to me why this example doesn't work?

我希望在单击按钮时呈现“对象”,而不是在设置未准备好时应出现的“请稍候”。然而,当“src”属性似乎更新时,它不会被调用。有人可以向我解释为什么这个例子不起作用吗?

回答by kseb

For directives with isolated scope the element you're applying directive to doesn't inherit scope of the parent. So you won't see any {{src}}variable there - it will be always empty.

对于具有独立作用域的指令,您应用指令的元素不会继承父级的作用域。所以你不会{{src}}在那里看到任何变量——它总是空的。

There are two ways you can communicate with external world using attributes for directive with isolated scope:

您可以通过两种方式使用具有隔离作用域的指令的属性与外部世界进行通信:

  • Use src="{{$parent.src}}"but it's a little hackish
  • Use @ attribute mapping for one-way binding
  • 使用src="{{$parent.src}}"但它有点hackish
  • 使用@属性映射进行单向绑定
scope: {
    src: '@src'
}

And then following code will work:

然后以下代码将起作用:

$attrs.$observe('src', function(value) {
  //some action
});

回答by KayakDave

I think you have a few things going on at once, which always makes for fun debugging. I suspect $watch might work better for you. But also, using the element.html()calls was overwriting the variable within the html that you were trying to watch.

我认为您同时进行了几件事,这总是让调试变得有趣。我怀疑 $watch 可能更适合你。而且,使用element.html()调用会覆盖您试图观看的 html 中的变量。

I've made a few changes (all contained within your directive) and have some working code below (where I added a template to place your message in- of course there's a variety of other ways to do the same thing).

我做了一些更改(都包含在您的指令中)并在下面有一些工作代码(我在其中添加了一个模板来放置您的消息 - 当然还有多种其他方法可以做同样的事情)。

directive('record', function ($location,$sce) {
        return {
            restrict: 'E',
            template: '<div ng-bind-html="trustedMsg"></div>',
            link: function ($scope, element, attr) {
                console.log('record directive - called');
                console.log(attr);

                $scope.$watch('src', function(value)
                    {
                        console.log('record observe callback called!');
                        console.log('The value is: '+ value);
                        if(value && value !='recordValues')
                        {
                            var values = value.split('`');
                            console.log('video values:')
                            console.log(values);
                            $scope.message='<object position="relative" width="519px" height="520px" data="'+values[0]+'" type="application/x-shockwave-flash"><param name="FlashVars" value="'+values[1]+'" /><param name="AllowScriptAccess" value="always" /></object>';
                            $scope.trustedMsg = $sce.trustAsHtml($scope.message);
                        }
                        else
                        {
                            $scope.message="<div>Please wait</div>";
                            $scope.trustedMsg = $sce.trustAsHtml($scope.message);
                        }
                    }
                );
            }
        }
    });

Also, if you prefer to stick with your attr.$observe call then you could change your html from:

此外,如果您更喜欢坚持使用 attr.$observe 调用,那么您可以将 html 更改为:

<record src='{{src}}'></record> 

to:

到:

<record src='src'></record>

Edit: I updated (and tested) the code so message now supports html, as your code does. (http://plnkr.co/edit/K18Djio1NfSaHIWOXpeJ)

编辑:我更新(并测试)了代码,所以消息现在支持 html,就像你的代码一样。( http://plnkr.co/edit/K18Djio1NfSaHIWOXpeJ)

回答by tennisgent

I think you're misusing the $observe function, though I may just not understand 100% of what you're trying to do. I think a better way to do what you want would to be set the proper srcvalue on your $scopewithin your controller, and then use $watchto detect changes to that value. The following is how I would suggest you do that:

我认为您滥用了 $observe 函数,尽管我可能无法 100% 理解您正在尝试做的事情。我认为做你想做的更好的方法是在你的控制器中设置正确的src$scope,然后$watch用来检测该值的变化。以下是我建议您这样做的方式:

In your controller:

在您的控制器中:

$scope.handleClick = function(){
    console.log('handleClick - called');
    $scope.RecordAddress = 'rtmp://thisIsAnAddress';
    $scope.FlashVars = 'userid=SomeId&filena...';

    //Now set the src on the scope as you already had:
    $scope.source = $scope.RecordAddress+ '`' + $scope.FlashVars;
}

In your html view:

在您的 html 视图中:

<!-- remove the binding braces so you're just passing a regular attribute -->
<record src="source"></record>

In your directive:

在您的指令中:

//add a simple update function (for cleanliness)
function update(value){
    if(value && value !='recordValues'){
        var values = value.split('`');
        element.html('INSERT YOUR LONG HTML STRING HERE');
    }else{
        element.html("<div>Please wait</div>")
    }
}

// get any initial value for the 'source' variable
var value = scope.$eval(attrs.src);   // this will return the scope's source variable
if(value) update(value);

// watch for any changes to that variable on the scope
scope.$watch(attrs.src, update);

NOTE: Even though I believe the above code will work for what you want, I think its not really the best way to do it. You are really passing two values to your directive from your controller: 1) RecordAddressand 2) FlashVars. I would suggest that you add two separate attributes to your recordelement:

注意:尽管我相信上面的代码可以满足您的需求,但我认为这并不是最好的方法。您实际上是将两个值从控制器传递给您的指令: 1)RecordAddress和 2) FlashVars。我建议您为record元素添加两个单独的属性:

<record address="addr" flash-vars="flashVars"></record>

And then you would bind to them separately from your directive as:

然后你将与你的指令分开绑定到它们:

function update(addr, flashVars){ ... }

scope.$eval(addrs.address, function(value){ ... });
scope.$eval(addrs.flashVars, function(value){ ... });

scope.$watch(addrs.address, ...);
scope.$watch(addrs.flashVars, ...);

This would save you from having to split the values in the directive.

这将使您不必拆分指令中的值。