Angular - Передача объекта из одной директивы в другую директиву
Я новичок в angular, поэтому извиняюсь перед тем, если вопрос слишком новичок. Я пытаюсь создать пользовательскую директиву, и поскольку я уже использую директиву angular-youtube-embed, в моей новой директиве мне нужно передать объект player
из youtube-video
, в мою новую директиву, для функции playVideo
в моей возможности использовать ее. Интересно, как это сделать?
Вот как выглядит моя директива:
angular.module('coop.directives')
.directive('youtubePlayer', function () {
return {
restrict: 'E',
scope: {
videoPlaying: '=videoPlaying',
playVideo: '&playVideo',
playerVars: '=playerVars',
article: '=article'
},
templateUrl : 'templates/youtube-player.html'
};
});
Это мой файл youtube-player.html:
<img ng-hide='videoPlaying' ng-src='http://i1.ytimg.com/vi/{{ article.external_media[0].video_id }}/maxresdefault.jpg' class='cover'>
<youtube-video ng-if='videoPlaying' video-url='article.external_media[0].original_url' player='player' player-vars='playerVars' class='video'></youtube-video>
<div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo({player: player})'>
<img ng-hide='videoPlaying' class='play' src='icons/play.svg'/>
<img ng-hide='videoPlaying' class='playButton' src='icons/playRectangle.svg'/>
</div>
И это функция от контроллера, которую я хотел бы использовать в моей директиве:
$scope.playVideo = function(player) {
$scope.videoPlaying = true;
player.playVideo();
};
Где player
является объектом директивы youtube-video
, которую я использую из angular -youtube-embed package.
Таким образом, всякий раз, когда пользователь нажимает на элемент ниже, $scope.videoPlaying
должен стать true
, а функция playVideo()
должна запустить видео:
<div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo(player)'>
Вот как я вызываю свою директиву в представлении:
<youtube-player video-playing="videoPlaying" play-video="playVideo()" player-vars="playerVars" article="article"></youtube-player>
Мне нужно каким-то образом передать объект игрока из видео youtube в мою новую директиву, потому что теперь я получаю сообщение об ошибке:
ionic.bundle.js: 26794 TypeError: Не удается прочитать свойство 'playVideo' undefined:
Ответы
Ответ 1
Вы можете использовать '&' тип для передачи функции в директивах:
angular.module('coop.directives')
.directive('youtubePlayer', function () {
return {
restrict: 'E',
scope: {
action: '&', //<- this type of parameter lets pass function to directives
videoPlaying: '@videoPlaying',
...
чтобы директива принимала параметр как функцию:
<coop.directives action="playVideo" videoPlaying="video" ...> </coop.directives>
и вы сможете нормально вызвать эту функцию:
article: '=article'
},
template : "<img ng-hide='videoPlaying' ng-src='http://i1.ytimg.com/vi/{{ article.external_media[0].video_id }}/maxresdefault.jpg' class='cover'><youtube-video ng-if='videoPlaying' video-url='article.external_media[0].original_url' player='player' player-vars='playerVars' class='video'></youtube-video><div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo(player)'><img ng-hide='videoPlaying' class='play' src='icons/play.svg'/><img ng-hide='videoPlaying' class='playButton' src='icons/playRectangle.svg'/></div>",
link: function (scope, element) {
scope.action();
}
Изменить 1:
Если ни одно из этих предложений не работает, вы можете попробовать скобки добавить() к вашему параметру действия action="playVideo()"
или использовать '=' тип параметра (но таким образом, ваша функция будет дважды привязана. В большинстве случаев вам не придется беспокоиться об этом для функций, так или иначе).
Вы можете найти несколько примеров в этом старом посту: попробуйте либо решить, либо найти, какой из них работает для вашего дела.
Ответ 2
Вы можете использовать $broadcast для достижения этого.
Ниже приведена диаграмма, поясняющая концепцию.
![введите описание изображения здесь]()
В директиве youtubePlayer используется широковещательная передача -
$rootscope.$broadcast('player-object', $scope.player);
И получите его в своей пользовательской директиве.
$scope.$on('player-object', function (event, player) {
$scope.videoPlaying = true;
player.playVideo();
});
Пример примера - http://jsfiddle.net/HB7LU/10364/
Ответ 3
Измените префиксы следующим образом
@videoPlaying to = videoПланирование и
@playVideo to & playVideo
Параметры @перед переменными оцениваются как строковые значения angular, и в этом случае вам необходимо использовать двустороннюю привязку.
Ответ 4
Посмотрите на свою кнопку в своей директиве:
<div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo({player: player})'>
Вы не передаете player
функции, вы фактически передаете player
как значение свойства объекта, который вы создаете в вызове функции: {player: player}
Поэтому, когда вы переходите к вызову функции .playVideo()
в объекте player
, вы на самом деле пытаетесь вызвать его на объекте, созданном в вызове функции: {player: player}
, который, очевидно, не имеет функции в он.
Чтобы исправить это, вам нужно либо изменить свою функцию, либо изменить объект игрока, проходящий в функцию. Вместо этого:
$scope.playVideo = function(player) {
$scope.videoPlaying = true;
player.playVideo();
};
Вам нужно будет изменить его на следующее:
$scope.playVideo = function(player) {
$scope.videoPlaying = true;
player.player.playVideo();
};
Или, иначе, оставьте функцию в покое и измените объект, в котором вы проходите:
<div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo(player)'>
JSFiddle
Я также создал JSFiddle, показывающий общую концепцию работы вашей директивы.
Ответ 5
Прежде всего, ваш вопрос противоречит. В вашем youtube-player.html вы используете playVideo({player: player})
<div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo({player: player})'>
и чуть ниже того, что вы говорите, что используете его как playVideo(player)
.
<div ng-hide='videoPlaying' class='iframe-overlay' ng-click='playVideo(player)'>
Предполагая, что это вторая версия, проблема здесь может заключаться в том, что ссылка player
на самом деле равна undefined, и поэтому директива youtube-video пытается присвоить значения недоступному объекту. Чтобы решить эту проблему, назначьте пустой объект в player
в директивном контроллере youtube-player.
angular.module('coop.directives').directive('youtubePlayer', function () {
return {
restrict: 'E',
scope: {
videoPlaying: '=videoPlaying',
playVideo: '&playVideo',
playerVars: '=playerVars',
article: '=article'
},
templateUrl : 'templates/youtube-player.html',
controller: function($scope) {
$scope.player = {};
}
};
});
Ответ 6
проще всего использовать директиву $rootScope в директиве и назначить игрока в корнеплодах, а затем использовать его в контроллере.
или лучший подход будет использовать директиву.
директива:
в действии вы назначите функцию с параметром.
rootApp.directive('ListTemplate', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
scope: {
list: '=',
action: '='
},
template: ' <div ng-click="bindSelectedGuest(guest.guid)" class="ct-clearfix info" ng-repeat="guest in list track by $index" data-tag="{{activeUser.guestId}}" ng-class="{ active : guest.guid==activeUser.guestId}">' +
'<label class="col-md-6 col-lg-7 ct-pull-left" data-tag="{{action}}" title="{{guest.firstName}}">{{guest.firstName}}</label>' +
'<label class="col-md-6 col-lg-5 ct-pull-right"><span class="fr" ng-if="guest.mobile" title="{{guest.displayMobile}}">{{guest.displayMobile}}</span>' +
'<span class="fr" ng-if="!guest.mobile">{{"N/A"}}</span>' +
'</label>' +
'<div class="info" ng-show="list.length==0"><div class="detail_alert message">No Record found</div></div></div>',
link: function ($scope, e, a) {
$scope.$watch('list', function () {
//console.log(list);
});
}
}
});
контроллер:
вы запишете функцию, определенную вами в действии (директиве) здесь.
> $scope.bindSelectedGuest($scope.selectedGuest.guid);
Ответ 7
Вы можете создать для этого службу angular и использовать ее в любом месте проекта. Эта служба содержит все типы функций, которые вам нужны в нескольких директивах.
Ответ 8
Лучший способ передать объект в директиву angular - это использовать &.
В angular Документах:
И и привязка позволяет директиве инициировать оценку выражение в контексте исходной области, в определенное время. Любое юридическое выражение допускается, включая выражение, которое содержит вызов функции
Когда вы используете &, angular компилирует строку как выражение и устанавливает переменную области видимости в вашей директиве на функцию, которая при вызове будет оценивать выражение в контексте родительской области директивы.
Я сделаю небольшое изменение в вашей директиве, чтобы помочь пояснить мои объяснения.
angular.module('coop.directives')
.directive('youtubePlayer', function () {
return {
restrict: 'E',
scope: {
videoPlaying: '=videoPlaying',
foo: '&playVideo',
playerVars: '=playerVars',
article: '=article'
},
templateUrl : 'templates/youtube-player.html'
};
});
Я изменил имя переменной области действия директивы от playVideo до foo. Отсюда вперед, playVideo - это свойство родителя, а foo - свойство, связанное с привязкой к свойству директивы. Надеемся, что разные имена сделают вещи более ясными (они, фактически, полностью разделяют свойства/методы.
В вашем случае объект, который вы пытаетесь передать, является функцией. В этом случае есть два варианта: оба они отличаются друг от друга и зависят от того, как вы хотите, чтобы потребитель директивы использовал его.
Рассмотрим это использование:
<youtube-player video-playing="videoPlaying" foo="playVideo()" player-vars="playerVars" article="article"></youtube-player>
В этом случае выражение "playVideo()". Директива и будет создавать свойство в вашей области действия под названием "foo", которое является функцией, которая при вызове оценивает это выражение в родительской области. В этом случае оценка этого выражения приведет к вызову метода playVideo родительского объекта без аргументов.
В этом использовании ваша директива может вызывать только метод родительской области, как есть. Никакие параметры не могут быть переопределены или переданы функции.
Итак:
foo() -> parent.playVideo()
foo(123) -> parent.playVideo() argument ignored
foo({player: 'xyz'}) -> parent.playVideo() argument ignored
Вероятно, предпочтительный метод, если ваш родительский метод (playVideo) не принимает никаких аргументов.
Теперь рассмотрим небольшое изменение выражения:
<youtube-player video-playing="videoPlaying" foo="playVideo(player)" player-vars="playerVars" article="article"></youtube-player>
Обратите внимание на введение в выражение локальной переменной "player". Функция, созданная в области директивы, будет делать то же самое, что и в предыдущем примере, но теперь она может быть вызвана двумя разными способами. Переменная "игрок" считается локальной переменной в выражении.
Функция foo, сгенерированная с помощью angular, принимает аргумент, который позволяет директиве переопределять значение локальных переменных в выражении. Если переопределение не предусмотрено, оно ищет свойство родительской области с этим именем, если такое свойство не существует, оно передаст функции undefined. Итак, в этом случае:
$scope.foo() -> parent.playVideo(parent.player)
$scope.foo(123) -> parent.playVideo(parent.player)
$scope.foo({player: 'xyz'}) -> parent.playVideo('xyz')
Если вы хотите передать игрока из директивы родительскому, это странный способ сделать это (IMHO), потому что вам нужно знать имя локальной переменной в выражении. Это создает ненужное требование о том, что директива и выражение согласуются с именем аргумента.
Последний способ, которым может быть привязана функция playVideo:
<youtube-player video-playing="videoPlaying" foo="playVideo" player-vars="playerVars" article="article"></youtube-player>
В этом случае выражение, вычисленное против родителя, возвращает функцию playVideo родителя. В директиве, чтобы вызвать функцию, вы должны вызвать ее.
$scope.foo() -> noop (you now have a pointer to the parent.playVideo function
$scope.foo()() -> parent.playVideo()
$scope.foo()('xyz') -> parent.playVideo('xyz')
Этот последний способ, по моему очень скромному мнению, является правильным способом передать указатель функции, который принимает аргумент в директиву и использует его в директиве.
Есть некоторые эзотерические побочные эффекты, которые могут быть использованы (но не должны). Например,
$scope.foo({playVideo: function(){
alert('what????')
})();
Это не вызовет функцию parent.playVideo, поскольку вы переопределили локальную переменную выражения "playVideo" с пользовательской версией в директиве. Вместо этого появится диалоговое окно предупреждения. Странно, но так, как это работает.
Итак, почему бы не использовать @или =?
Если вы используете @, вам, по сути, нужно что-то делать и делать вручную в директиве. Зачем это делать и делать это за вас? '=' фактически устанавливает двустороннюю привязку, позволяя директиве изменять значение родительского свойства (потенциально меняя саму функцию!) и наоборот. Нежелательный побочный эффект. Это двухстороннее связывание также требует двух часов, которые по существу ничего не делают, кроме взятия циклов процессора, поскольку вы вряд ли используете их для обновления элементов интерфейса.
Я надеюсь, что это поможет разобраться.