Подтвердить что ты не робот

Выполнить/выдать директиву из другой директивы

У меня есть 2 директивы. В 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 });
                    }
                }
            });
        }
    };
});

Вот директиваB

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

UPDATE

Вот как я использую его в HTML

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

<directive-b></directive-b>

UPDATE

Я все еще жду его решения.

ОБНОВЛЕНИЕ 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>
4b9b3361

Ответ 1

Ваш вопрос сводится к следующему:

  • Как обеспечить связь между DirectiveA и DirectiveB

    Как и ваши директивы используют одну и ту же область, ваше решение с трансляцией событий работает. Это сломается, если вы измените настройку scope внутри DDO на все, кроме false.

    Вот почему я бы посоветовал вам $broadcast событие на $rootScope.

  • Как сделать шаблон DirectiveB после того, как данные готовы

    Простая ngIf упаковка шаблона директивы будет выполнять задание. Нам не нужно ничего для этого.

Bellow - простой рабочий пример. Обратите внимание, что обе директивы используют изолированную область действия - это просто чтобы показать, зачем вам нужно $rootScope.$broadcast. Я также использую $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>

Ответ 2

Используйте службу API (angular) для связи между вашими директивами. В моем примере я называю это MyAPIService:

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

Затем введите экземпляр вашего API как в директивы, так и внутри вашей директивы A, вы должны заполнить свои сезоны и обновить переменную "display", когда вы нажмете на свой якорь. На вашей директиве B просто просмотрите переменную "display"

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

Ответ 3

У меня есть рабочее решение для вас, немного отличное от вашего широковещательного подхода. Как вы упомянули, вы хотите загрузить данные для директивы B только после щелчка директивы A. Итак, мы поставим одно условие для удаления Директивы B из DOM. И когда нажатие кнопки "А" запускается в этот момент, мы будем устанавливать нагрузку на Директиву B в DOM. Этот способ Ваш контроллер 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>

Ответ 4

Я давно создал это решение, как эксперимент

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

в котором вы можете динамически отображать директивы.

Но вы задаетесь вопросом не о динамическом рендеринге. Об изменении состояния компонента (директивы).

Вы уже дали ответ об использовании ngIf, дело только в том месте, где его можно было поставить. Я бы предложил разместить его внутри вашей директивы и сделать оболочку для всего шаблона:

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

И установите свойство isShown scope, где оно вам нужно, например, после события:

 $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>

Ответ 5

Вы можете попробовать использовать флаг ng-if и какой-либо флаг в директиве A, чтобы указать, что запрос ajax завершен. ng-if обертывает скрытый элемент в комментариях, поэтому они отсутствуют в dom до тех пор, пока условие не будет истинным.

Ответ 6

От Документация по областям с помощью AngularJs $

Отправляет имя события вниз во все дочерние области (и их дочерние элементы), уведомляя зарегистрированных зарегистрированных пользователей $rootScope.Scope.

Это означает, что ваша директива B должна быть дочерней из вашей директивыA, чтобы поймать широковещательное событие. Например:

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

Код функции контроллера директивы будет выполняться до html-рендеринга и кода функции post post html render. Это означает, что вы не можете справиться с выполнением директивы B. Вы можете просто создать его, когда у вас есть ответ от вызова, сделанного вами в директиве A. Например:

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>

Таким образом, ваша директива B будет создана, когда ваш вызов будет разрешен.

ВНИМАНИЕ. Если вы используете ngIf, вы можете потерять событие $broadcast, так как ваш directiveB $будет создан после $broadcast

Ответ 7

Я думаю, что лучшим решением, , если вы хотите сохранить две директивы, является использование директивы ng-if в вашем шаблоне Diretive B, чтобы показать таблицу только тогда, когда в ней есть результаты.

Вам нужно изменить шаблон вашей директивы B, чтобы добавить условие в текущую таблицу, а также добавить <div> или подобное, чтобы показать сообщение, когда пользователь еще не выбрал какой-либо элемент.

Шаблон вашей директивы 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>