0

I'm building my first Angular app, but am having a bit of trouble getting something to work. I have a video container that will be hidden until $scope.video.show = true; I'm trying to set this value when I click on a link. I'm trying to make that happen in a directive. Any help would be appreciated.

html:

<div ng-controller="AppCtrl">
    <div ng-cloak 
         ng-class="{'show':video.show, 'hide':!video.show}">
                  // youtube iframe content, for example
    </div>

    <div>
        <ul>
            <li>
                <h3>Video Headline 1</h3>
                <button type="button" 
                    video-show 
                    data-video-id="jR4lLJu_-wE">PLAY NOW&nbsp;&rang;</button>
            </li>
            <li>
                <h3>Video Headline 2</h3>
                <button type="button" 
                    video-show 
                    data-video-id="sd0f9as8df7">PLAY NOW&nbsp;&rang;</button>
            </li>
        </ul>
    </div>
</div>

javascript:

var thisViewModel = angular.module("savings-video", [])
.controller('SavingsVideoController', function($scope) {

    $scope.video = {
        show : false,
        videoId : ""
    };
};

thisViewModel.directive("videoShow", function(){
    return{
        restrict: 'A',
        link: function(scope , element){

            element.bind("click", function(e){

                var $this = angular.element(element);
                $this.closest('li').siblings().addClass('hide');   // hide the other one
                $this.closest('li').removeClass('hide');           // keep me open


                scope.video.show = true;  // doesn't work.  
                     // what is the best way to do this?

            });
        }
    }
});
Stewie
  • 60,366
  • 20
  • 146
  • 113
Scott
  • 1,862
  • 1
  • 33
  • 53
  • I would add a working demo to get a quick answer. – allenhwkim Dec 24 '13 at 00:36
  • If you want to change view.show in a event handler, you have to use `$scope.$apply(function() {$scope.view.show = true;});` in order to jump into Angular digest cycle. But you won't need to create a directive any way. Just use `ng-click` and `ng-show`. See my answer below. – Daiwei Dec 24 '13 at 02:00

3 Answers3

1

I see a few things you can improve.

  1. Checkout ngShow/ngHide and ngIf; they'll give you toggle-ability more easily than trying to do it from scratch.
  2. Think in angular. Rather than trying to use logic to modify the DOM on your own, simply setup your rules using angular directives, and let the framework do the rest for you.

For example, it seems like this is more what you want.

<div ng-controller="AppCtrl">
    <div ng-cloak ng-show='video.show">
              // youtube iframe content, for example
    </div>

    <div>
        <ul ng-switch="video.videoId">
            <my-video my-video-id="jR4ABCD" my-headline="Video Headline 1" ng-switch-when="myVideoId" my-video-manager="video" />
            <my-video my-video-id="al1jd89" my-headline="Video Headline 2" ng-switch-when="myVideoId" my-video-manager="video"/>
        </ul>
    </div>
</div>

What I changed is making your iframe show conditionally with ngShow, and using ngSwitch to control which video appears (the appearing video is based on the $scope's video.videoId). Then, I turned your <li>s into a directive called my-video, which ends up looking like this

thisViewModel.directive("my-video", function(){
    return{
        restrict: 'E',
        replace: true,
        scope: {
           myVideoId = "=",
           myHeadline = "=",
           myVideoManager = "="
        },
        template = '<li><h3>{{myHeadline}}</h3><button type="button" ng-click="play()">PLAY NOW&nbsp;&rang;</button></li>',
        link: function(scope , element){
            scope.play = function(){
                 myVideoManager.show = true;
                /*whatever you want here, using scope.myVideoId*/
            }
        }
    }
});

This directive does exactly what your old HTML did, but brings it into the angular framework so you can access the properties you're looking for. By using the raw angular directives, I eliminate the need for any manual UI logic; I don't need to access element at all anymore, and both my HTML and JavaScript are cleaner. There's certainly room for improvement here, even, but I would say that this is closer to the right track.

It takes practice to get more familiar with, but following the guidelines in the SO link above will help.

EDIT

Sorry, think I missed a requirement the first time around. If you want both videos to show when none are selected, don't use ng-switch; just set up some manual ng-shows.

    <div>
        <ul>
            <my-video my-video-id="jR4ABCD" my-headline="Video Headline 1" ng-show="myVideoId == video.videoId" my-video-manager="video" />
            <my-video my-video-id="al1jd89" my-headline="Video Headline 2" ng-show="myVideoId == video.videoId" my-video-manager="video"/>
        </ul>
    </div>

Since ng-switch is really just a shortcut for ng-show anyways, it amounts to the same thing; the logic just got moved into the ng-show attribute instead.

Also, if you have an array of videos, checkout out ng-repeat; it will let you repeat your video tag multiple times automatically, instead of by hand.

<ul>
   <my-video ng-repeat='aVideo in myVideoArray' my-video-id='aVideo.videoId' my-headline...(and so on)>
</ul>
Community
  • 1
  • 1
Hylianpuffball
  • 1,553
  • 10
  • 13
  • I have this working, but I need to show both video items when videoId="". Would I do a ngShow (to account for "") in addition to the ngSwitch (to account for filled videoid)? – Scott Jan 02 '14 at 20:18
0

Well your controller names don't match up. Try changing AppCtrl to SavingsVideoController.

Sam
  • 15,336
  • 25
  • 85
  • 148
0

You only need a very simple solution.

HTML

<div ng-controller="AppCtrl">
    <div ng-cloak ng-show="view.show">
        <!-- Use ng-show is more convenient -->
    </div>

    <div>
        <ul>
            <li>
                <h3>Video Headline 1</h3>
                <button type="button" 
                    ng-click="view.show = true"
                    data-video-id="jR4lLJu_-wE">PLAY NOW&nbsp;&rang;</button>
                    <!-- You don't need an extra directive to change view.show -->
            </li>
            <li>
                <h3>Video Headline 2</h3>
                <button type="button" 
                    ng-click="view.show = true"
                    data-video-id="sd0f9as8df7">PLAY NOW&nbsp;&rang;</button>
            </li>
        </ul>
    </div>
</div>

JS

var thisViewModel = angular.module("savings-video", [])
.controller('SavingsVideoController', function($scope) {

    $scope.video = {
        show : false,
        videoId : ""
    };
};

// No need to create another directive
Daiwei
  • 40,666
  • 3
  • 38
  • 48