从另一个指令

时间:2016-08-25 09:46:18

标签: javascript angularjs

我有以下两个指令。在DirectiveA中,我从远程服务器获取一些数据,然后将包含该数据的模板呈现为anchor标记。现在,当用户点击任意链接时,我broadcast会在DirectiveB中收听该事件。在DirectiveB中,我想制作另一个ajax请求,当我收到数据响应时,我想渲染DirectiveB模板。我当前的方法不起作用,因为它在开始时执行两个指令,那时我没有DirectiveB的任何数据。以下是代码

DirectiveA

angular.module('app').directive('DirectiveA', function ($http) {
    'use strict';

    return {
        restrict: 'E',
        templateUrl: '/templates/templateA.html',
        controller: function ($scope) {
            $scope.showDetails = function (num) { // <-- this executes in ng-click in template
                $scope.$broadcast('season', num);
            };
        },

        link: function (scope, element, attributes) {
            $http.get(attributes.resource).then(function (response) {
                scope.rows = response.data;
                scope.seasons = [];

                for (var i = 0; i < scope.rows.length; i++) {
                    var num = parseInt(scope.rows[i].data);

                    if (year >= 2005 && year <= 2015) {
                        scope.seasons.push({ 'data': scope.rows[i].data });
                    }
                }
            });
        }
    };
});

这是DirectiveB

angular.module('app').directive('DirectiveB', function() {
    return {
        restrict: 'E',
        templateUrl: 'templates/templateB.html',
        controller: function($scope, $http) {
            $scope.$on('season', function ($scope, num) { // I listen to that event
                $http.get('http://demo.com/api/' + num).then(function (response) {
                    $scope.standings = response.data;
                });
            });
        }
    };
});

更新

以下是我在HTML中使用它的方法

<directive-a resource="http://demo.com/api/>
</directive-a>

<directive-b></directive-b>

更新

我还在等待解决方案。

更新3

templateA

<ul>
    <li ng-repeat="row in seasons"> 
        <a href="#" ng-click="showDetails(row.season)">{{ row.season }}</a>
    </li>
</ul>

模板B

<h3>Standings</h3>
<table>
    <thead>
        <th ng-repeat="standing in standings" ng-if="state">{{ position  }}</th>
    </thead>
    <tbody>
        <tr ng-repeat="row in standings">
            {{ row.Driver  }}
        </tr>
    </tbody>
</table>

7 个答案:

答案 0 :(得分:4)

您的问题归结为:

  1. 如何确保DirectiveADirectiveB

    之间的沟通

    由于您的指令使用相同的范围,因此您的事件广播解决方案可行。将DDO内的scope设置更改为false以外的任何内容时,这会中断。

    这就是为什么我会$broadcast$rootScope为您提供DirectiveB活动的原因。

  2. 数据准备好后如何呈现ngIf的模板

    包装指令模板的简单$rootScope.$broadcast可以完成这项工作。我们不需要任何花哨的东西。

  3. 贝娄是一个简单的工作示例。请注意两个指令如何使用隔离范围 - 这只是为了展示您需要$timeout的原因。我也使用angular .module('myApp', []) .directive('directiveA', function() { return { templateUrl: 'tempA.html', controller: function($rootScope, $scope) { $scope.sendMessage = function(message) { $rootScope.$broadcast('eventName', message); }; }, scope: {} }; }) .directive('directiveB', function() { return { templateUrl: 'tempB.html', controller: function($rootScope, $scope, $timeout) { $scope.messages = []; $rootScope.$on('eventName', function(event, message) { $timeout(function() { $scope.messages.push(message); }, 50); }); }, scope: {} }; });来模拟服务器的延迟。

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
    
    <div ng-app="myApp">
      <directive-a></directive-a>
      <directive-b></directive-b>
    
      <script type="text/ng-template" id="tempA.html">
        <h2>Hello from DirectiveA</h2>
        <label>
          <p>Message to send to DirectiveB</p>
          <input type="text" ng-model="toSend">
        </label>
        <button type="button" ng-click="sendMessage(toSend); toSend = '';">Send</button>
      </script>
      <script type="text/ng-template" id="tempB.html">
        <div ng-if="messages.length > 0">
          <h3>Hello from DirectiveB</h3>
          <ul>
            <li ng-repeat="message in messages track by $index">{{ message }}</li>
          </ul>
        </div>
      </script>
    </div>
    $sql = "SELECT * FROM tablename WHERE condition";
    
    $res = mysql_query($sql);
    
    while ($row = mysql_fetch_assoc($res)) {
    
        foreach($row as $key => $field) {  
    
            echo "<br>";
    
            if(empty($row[$key])){
    
                echo $key." : empty field :"."<br>"; 
    
            }else{
    
            echo $key." =" . $field."<br>";     
    
            }
        }
    }
    

答案 1 :(得分:2)

使用API​​(角度服务)在您的指令之间进行通信。在我的例子中,我称之为MyAPIService:

angular.module('app').service('MyAPIService', function () {
   this.display = false;
   this.seasons = [];
});

然后在两个指令和指令A中注入API的实例,当你点击你的锚点时,你必须填充你的季节并更新你的“显示”变量。在你的directiveB上,只需看“显示”变量

angular.module('app').directive('DirectiveA', function ($http, MyAPIService) {
'use strict';

return {
    restrict: 'E',
    templateUrl: '/templates/templateA.html',
    controller: function ($scope) {
        $scope.showDetails = function () { 
           MyAPIService.display = true;
    },

    link: function (scope, element, attributes) {
        $http.get(attributes.resource).then(function (response) {
            scope.rows = response.data;
            MyAPIService.seasons = [];

            for (var i = 0; i < scope.rows.length; i++) {
                var num = parseInt(scope.rows[i].data);

                if (year >= 2005 && year <= 2015) {
                    MyAPIService.seasons.push({ 'data': scope.rows[i].data });
                }
            }
        });
    }
};

});

angular.module('app').directive('DirectiveB', function() {
   return {
       restrict: 'E',
       templateUrl: 'templates/templateB.html',
       controller: function($scope, $http, MyAPIService) {
           // WATCH "DISPLAY" VARIABLE AND CALL YOUR AJAX
       }
   };
});

答案 2 :(得分:1)

我为您提供了一个有效的解决方案,与您的广播方式略有不同。正如您所提到的,您只想在单击指令A之后加载指令B的数据。因此,我们将放置一个条件来从DOM中删除指令B.当指令A的点击触发时,我们将在DOM中加载指令B。这样你的指令B的控制器将在稍后加载。

以下是支持我的论点的代码

指令A

angular.module('app').directive('DirectiveA', function ($http) {
'use strict';
return {
    restrict: 'E',
    templateUrl: '/templates/templateA.html',
    controller: function ($scope) {
        $scope.showDetails = function (num) { // <-- this executes in ng-click in template
            $scope.isDirectiveBVisible = true;
        };
    },

    link: function (scope, element, attributes) {
        $http.get(attributes.resource).then(function (response) {
            scope.rows = response.data;
            scope.seasons = [];

            for (var i = 0; i < scope.rows.length; i++) {
                var num = parseInt(scope.rows[i].data);

                if (year >= 2005 && year <= 2015) {
                    scope.seasons.push({ 'data': scope.rows[i].data });
                }
            }
        });
    }
};
});

指令B

angular.module('app').directive('DirectiveB', function() {
return {
    restrict: 'E',
    templateUrl: 'templates/templateB.html',
    controller: function($scope, $http) {

            $http.get('http://demo.com/api/' + num).then(function (response) {
                $scope.data = response.data;
            });

    }
};
});

HTML

<directive-a resource="http://demo.com/api/>
</directive-a>

<directive-b ng-if="isDirectiveBVisible"></directive-b>

答案 3 :(得分:1)

  

很久以前我创建了this solution就像实验一样

<div ng-directive="{{directive}}"></div>
     

您可以在其中动态呈现指令。

但你问的不是动态渲染。 关于改变组件状态(指令)

您已经提供了关于使用ngIf的答案,事情只是在哪里放置它。我建议将它放在你的指令中并为整个模板制作包装器:

<div ng-if="isShown">
  <h3>Standings</h3>
  ...
</div>

并将isShown范围属性设置在您需要的位置,例如在事件之后:

 $scope.$on('season', function ($scope, num) { 
    $scope.isShown = true;   
 });

或在ajax电话之后:

$http.get('http://demo.com/api/' + num).then(function (response) {
    $scope.isShown = true;
});

此外,我建议不仅要播放能见度,还要使用加载指示来表示它的加载效果:

<div ng-if="isShown">
   <loading-indicator ng-if="isLoading">
   <div ng-if="!isLoading">
     <h3>Standings</h3>
     ...
   </div>
</div>

答案 4 :(得分:0)

您可以尝试在指令A中使用ng-if和某种标志来指示ajax请求已完成。 ng-if将隐藏元素包装在注释中,这样它们就不会出现在dom中,直到条件为真。

答案 5 :(得分:0)

来自"AngularJs $scope documentation"

  

将事件名称向下调度到所有子范围(及其子级),通知已注册的$ rootScope.Scope侦听器。

这意味着你的directiveB必须是你的directiveA的子代,才能捕获广播事件。例如:

<directive-A>
    <directive-B></directive-B>
</directive-A>

指令的控制器功能代码将在html渲染之前执行,链接的功能代码将在html渲染之后执行。这意味着您无法处理directiveB的执行。当您从directiveA中进行的调用得到响应时,可以简单地创建它。例如:

scope.data={resolved:false};
$http.get(attributes.resource).then(function (response) {
                scope.rows = response.data;
                scope.seasons = [];
                scope.data.resolved = true;

                for (var i = 0; i < scope.rows.length; i++) {
                    var num = parseInt(scope.rows[i].data);

                    if (year >= 2005 && year <= 2015) {
                        scope.seasons.push({ 'data': scope.rows[i].data });
                    }
                }
            });

在上面的HTML示例中:

<directive-A>
    <directive-B ng-if="data.resolved"></directive-B>
</directive-A>

通过这种方式,您的protocolB将在您的呼叫解决后创建。

注意但是:如果您使用ngIf,您可能会丢失$ broadcast事件,因为您的directiveB&#39; $ on将在$ broadcast

之后创建

答案 6 :(得分:0)

我认为最好的解决方案,如果你想保留两个指令,就是在你的指令B模板中使用ng-if指令,只有在有结果时才显示表格

您只需修改指令B模板以向当前表格添加条件,并添加<div>或类似内容,以便在用户尚未选择任何项目时显示消息。< / p>

您的指令B模板类似于:

<h3>Standings</h3>
<!-- Add ng-if here -->
<table ng-if="standings.length > 0">
    <thead>
        <th ng-repeat="standing in standings" ng-if="state">{{ position  }}</th>
    </thead>
    <tbody>
        <tr ng-repeat="row in standings">
            {{ row.Driver  }}
        </tr>
    </tbody>
</table>

<!-- and also here -->
<!-- If standings is not an array -->
<div ng-if="!standings.length">
    <p>Please, select one of the items above to show standings.</p>
</div>

您可以根据需要添加尽可能多的div。例如,您可以在所选项目没有结果时添加消息:

<!-- If standings is an array but it is empty -->
<div ng-if="standings.length && standings.length > 0">
    <p>There are no standings to show.</p>
</div>