Выполнить/выдать директиву из другой директивы
У меня есть 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>
Ответы
Ответ 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>