Question

In short:

I have a controller that triggers some jQuery in my AngularJS web page to fade out a play button and a corresponding image. But, when the URL changes, the controller fails to work on subsequent pages. I'm using a dynamic URL, and I think that's why my controller is breaking.

Detail

I've organized my controllers by having one for each page of my website app. Here you can see how my routes are setup:

angular.module('100_Ages', ['mydirectives', 'ngResponsiveImages']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
    when('/100_Ages', {templateUrl: 'partials/splash.html', controller: SplashCtrl}).
    when('/100_Ages/nav', {templateUrl: 'partials/nav.html', controller: NavCtrl}).
    when('/100_Ages/about', {templateUrl: 'partials/person-list.html', controller: AboutCtrl}).
    when('/100_Ages/0', {templateUrl: 'partials/splash.html', controller: SplashCtrl}).
    when('/100_Ages/:personId', {templateUrl: 'partials/person.html', controller: DetailCtrl}).
    otherwise({redirectTo: '/100_Ages'});
}]);

And here is the controller in question:

function DetailCtrl($scope, $routeParams, $http) {
// Pull down a JSON file with my data to populate Angular template
  $http.get('person.json').success(function(data) {
// matches person's id to route.
    angular.forEach(data, function(person) {
          if (person.id == $routeParams.personId) 
            $scope.person = person;
        });
    });
// start of attempt to integrate button click functionality.
  $scope.$on('$routeChangeSuccess', function(){
    $.noConflict();
      jQuery(function(){
        jQuery("#playButton").click(function(){
          jQuery("#person_photo").fadeOut('9000');
          jQuery("#playButton").fadeOut('9000');
          jQuery("#player").fadeIn('9000');
        });
      });
  });
}

I'm using jQuery here because I simply couldn't figure out how to do this in Angular. Anyways, when I refresh the page and click on the button image, it works. But, I have a 'next' link that increments the route by '1' to go to the next page in my app (a list of 100 people, with one person on each page).

When I go to a different page using this link, my jQuery no longer works. I'm guessing this is because the route isn't changing, even though the URL is. Anyway to make this work with my dynamic route? Thanks.

Was it helpful?

Solution

A couple things...

1) You don't need $.noConflict(). Angular and jQuery play nice.

2) You should should put DOM scripting within an Angular Directive rather than a controller. Here's an example...

Directive*:

(function () {

    function personPlayerDirective() {
        return function (scope, $element, attr) {
            $element.find('.play-btn').click(function () {
                $element.find(".img-person").fadeOut('9000');
                $element.find(".play-btn").fadeOut('9000');
                $element.find(".player").fadeIn('9000');
            });
        };
    }

    angular.module('app', [])
        .directive('personPlayer', [personPlayerDirective]);
}());

Assuming your template is*:

<div person-player>
    <img class="img-polaroid img-person" ng-src="{{person.imgUrl}}">
    <div class="player">
        I'm a player.
    </div>
    <div class="btn-group">
        <button class="btn play-btn">
            <i class="icon-play"></i> Play
        </button>
        <button class="btn pause-btn">
            <i class="icon-pause"></i> Pause
        </button>
        <button class="btn stop-btn">
            <i class="icon-stop"></i> Stop
        </button>
    </div>
</div>

3) You could use CSS3 transitions with ng-click to achieve the same behavior without any jQuery DOM scripting...

New Template*:

<style>
    /* Twitter Bootstrap fade */
    .fade {
        opacity: 0;
        -webkit-transition: opacity 0.15s linear;
        -moz-transition: opacity 0.15s linear;
        -o-transition: opacity 0.15s linear;
        transition: opacity 0.15s linear;
    }
    .fade.in {
        opacity: 1;
    }
</style>

<div class="person-player">
    <img class="img-polaroid img-person fade {{imgPersonFade}}" ng-src="{{person.imgUrl}}">

    <div class="player fade {{playerFade}}">
        I'm a player.
    </div>
    <div class="btn-group">
        <button class="btn play-btn fade {{playBtnFade}}" ng-click="playing=true">
            <i class="icon-play"></i> Play
        </button>
        <button class="btn pause-btn">
            <i class="icon-pause"></i> Pause
        </button>
        <button class="btn stop-btn">
            <i class="icon-stop"></i> Stop
        </button>
    </div>
</div>

Controller*:

function DetailCtrl($scope, $routeParams, $http) {

    $scope.playing = false;

    // Update the *Fade scope attributes whenever the `playing` scope attribute changes
    $scope.$watch('playing', function (playing) {
        $scope.playerFade = playing ? 'in' : '';
        $scope.imgPersonFade = playing ? '' : 'in';
        $scope.playBtnFade = playing ? '' : 'in';
    });

    // Pull down a JSON file with my data to populate Angular template
    // ...

}

Anyway, like many of the new JS MV* application frameworks, Angular encourages you to "watch" for data/model changes rather than "listen" for DOM events. In fact, I recommend trying to use as little jQuery as possible until you've got the hang of this new paradigm shift.

* Code not tested :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top