8

i have used jQuery slider in my project in which i am loading images using angular. My current view looks like this;

        <div id="slides">
            <div class="slides_container">
                <a href="" data-ng-repeat="image in gallery">
                    <img data-ng-src="{{image.imageUrl}}" width="919" height="326" alt="" />
                </a>
            </div>
        </div>

the controller;

blogApp.controller("HomeController", ['$scope', '$resource', function ($scope, $resource) {
    var basePath = '/api/',
        FrontGallery = $resource(basePath + 'gallery?culture=en-US'),

    $scope.gallery = FrontGallery.query();
}]);

And the jQuery slider code (to make it a slider. I'm using this plugin http://archive.slidesjs.com/)

$(document).ready(function () {
    $('#slides').slides({
        preload: true,
        preloadImage: '/content/images/theme/loading.gif',
        play: 5000,
        pause: 2500,
        hoverPause: true
    });
});

When, i try this code, my all images are loaded from database (debugged it through firebug) but jQuery slider isn't applying the animation or sliding effect to it
And when i remove the data-ng-repeat="image in gallery and use Some static content i.e images, i get the sliding effect back.
What's the problem here. I think Angular is manipulating some how my DOM. That's why i'm getting this problem whenever i put i.e use some angular attributes on my slider.
Note: i have the same issue with jQuery news i.e jQuery InnerFade plugin http://medienfreunde.com/lab/innerfade/
Here is how the innerFade is used;

<h1>
    <img src="/content/images/theme/hotnews.png" alt="" />Hot News</h1>
<ul id="news">
    <li class="ng-repeat:headline in news">
        <span class="ng-bind:headline.description"></span>
        <a href="#" title="Read More">» more</a>
    </li>
</ul>

and the controller;

var HotNews = $resource(basePath + 'article/hotnews?culture=en-US');
$scope.news = HotNews.query();

How do i fix these problems?
Update 2: Here is my routes;

// declare a module
var blogApp = angular
    .module('blogApp', ['ngResource', 'ngSanitize'])
    .config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
        //$locationProvider.html5Mode(true);
        //configure the routes
        $routeProvider
            .when('/', {
                // list the home page
                templateUrl: "tmpl/home.html",
                controller: "HomeController"
            })
            .when('/article/:id', {
                // access custom param
                foo: "hello world",
                // list the home page
                templateUrl: "tmpl/article.html",
                controller: "ArticleController"
            })
            .when('/404', {
                template: '<h1>404 Not Found!</h1>'
            })
            .otherwise({ redirectTo: '/404' });
    }]);

Solution #1: Temporary, (As @Jason Goemaat answered) works for me. So, here is what i have done. I didn't create any directive but straightly i injected the $timeout to my controller and then i did the following;

$timeout(function () {
    jQuery('#news').innerfade({
        animationtype: 'slide',
        speed: 750,
        timeout: 2000,
        type: 'random',
        containerheight: '1em'
    });
    jQuery('#slides').slides({
        preload: true,
        preloadImage: 'image/theme/loading.gif',
        play: 5000,
        pause: 2500,
        hoverPause: true
    });

}, 100);

And it did work for both i.e for Slider and for InnerFade

1
  • Please provide a jsfiddle or similar, I would try to write a directive to fix that ;) Commented Jun 12, 2013 at 10:15

5 Answers 5

9
+50
 $(document).ready(function () {

is firing before angular has loaded the content onto the page. The jQuery slider script should only be activated after angular has loaded the content. Part of the problem here is that angular does not provide a callback for $digest so you don't have an event to listen for.

The content load -> event callback -> ui build, while common in jQuery and standard javascript thinking, its not part of the angular mentality. Luckily angular has an insanely powerful method for handling this problem.

What you need is to turn the jQuery slideshow script into an angular directive. Then it will become part of the angular process space and get instantiated as a part of the angular digest cycle.

Something like

   <div id="slides" my-slideshow >
        <div class="slides_container">
            <a href="" data-ng-repeat="image in gallery">
                <img data-ng-src="{{image.imageUrl}}" width="919" height="326" alt="" />
            </a>
        </div>
    </div>

Good luck!


Happy example time using innerfade (I have not tested this yet, will do ASAP). First your application must declare an angular module if it doesn't already. ng-app="myModule". http://docs.angularjs.org/api/angular.module

(make sure your controller is attached as well!)

Now you can declare a directive on that module. Which is super cool.

angular.module('myModule').directive("innerFade", function () {
   var res = {
     restrict : 'C',
     link     : function (scope, element, attrs) {
           scope.$watch(attrs.innerFade, function(){             
               element.innerfade({
                  speed: 'slow',
                  timeout: 1000,
                  type: 'sequence',
                  containerheight: '1.5em'
               });
           });
        }
     };
  return res;
});

The way this works is pretty simple. The $watch function will monitor the part of scope containing your data. When that changes (including initialization), it will fire the innerfade jQuery function on the element with the directive. We are also passing 'C' to the restrict argument so this custom directive will be a class only directive (I noticed you seem to like that).

 <ul class="inner-fade:innerData" >
      <li class="ng-repeat:fadeItem in innerData">{{fadeItem}}</li>
 </ul>

Now all you have to do is bind the data to $scope.innerData in your controller (bind it any way you like including ajax), and it will just work. You can even change the data on $scope.innerData and it will update with full jQuery glory.

Sign up to request clarification or add additional context in comments.

16 Comments

I can transform it into a directive, but I don't have the time right now to do the work and test it for delivery as a completed code. Plus it better to just learn how the angular ecosystem works anyway. It should not be too hard, and more than worth knowing.
@DotNetDreamer if you end up going with the custom directive approach, don't forget to accept this answer.
Hey, so I noticed you opened up a bounty on this one. Whats up? Did the custom directive direction not work out for you?
no dear, the problem is i don't understand the directives completly. I tried different directives aproach but it didn't work
i have 4 other developers here, if you can proivde the answer, they will also give +1s, becuase they have this problem too
|
6

Angular IS manipulating your dom, the ng-repeat directive is creating the elements when it has a list to work from. Because of that you need to call .slides after angular is done creating the elements because that plugin is altering the DOM itself.

Here's a directive you can use to call $.slides on an element based on when an array has length > 0. It shows using extra attributes like 'start' to modify the call to slides too. Here it checks an array on your $scope called 'images' and calls $timeout() during the link phase to call .slidesjs() when angular is done creating the DOM. I didn't know what plugin you used so I found one called 'slidesjs that looked similar. 'Here's a plunker: http://plnkr.co/edit/4qdUctYMIpd8WqEg0OiO?p=preview

Html:

<div my-slides="images" start="4">
  <img ng-repeat="image in images" ng-src="{{image.url}}" />
</div>

Directive:

app.directive('mySlides', function() {
  var directive = {
    restrict: 'A',
    link: function(scope, element, attrs, ctrl) {
      scope.$watch(attrs.mySlides, function(value) {
        setTimeout(function() {
          // only if we have images since .slidesjs() can't
          // be called more than once
          if (value.length > 0) {
            $(element[0]).slidesjs({
              preload: true,
              preloadImage: '/content/images/theme/loading.gif',
              play: attrs.play || 5000,
              pause: attrs.pause || 2500,
              start: attrs.start || 1,
              hoverPause: attrs.hoverPause || true,
              navigation: { active: true, effect: "slide" }
            });
          }
        }, 1);
      });
    }
  };
  return directive;
});

One thing to note is that it still can only be called once, so if your image list gets updated it won't work. To do that you could create the content in the directive...

9 Comments

the data is comming from database like this=> FrontGallery = $resource(basePath + 'gallery?culture=en-US'); $scope.gallery = FrontGallery.query();
You don't need to, the call to $scope.$watch will call the function you pass whenever the value, gallery in this case, changes.
did you wrap the jQuery call in a timeout? At the point $watch executes the function the elements might not be created yet...
no, i didn't. should i use $timoute as well. If yes, can you provide a little more descriptive answer
I will perhaps sound like a jerk but putting jquery or DOM stuff in a controller should not be marked as a solution. You can use $timeout in directives, and if your timeout callback don't change the model (don't need angular to digest changes) you can just use setTimeout.
|
1

A few thoughts
1) My guess is that there is something sequentially wrong with your main app page. Do you have jQuery include after angularjs? I assume you are using ng-repeat directive?

2) change $ to jQuery

3) place document.ready script in main page ... also consider replacing it with

 window.onload=function(){} 

I am sure I can debug if you can show code for main page as well as the view.

6 Comments

this is working but in Firefox, Chrome, when i press the Ctrl+f5 key. However, when i simply Refresh the browser (without ctrl+f5), then it's not working. And in IE it doesn't work at all
And also, if i come back from another route, the behavior isn't applied
i put alert("It's loaded!"); inside the window.onload, i don't see any alert when i come back fron another route.
Regarding IE - try adding this line in the header: id="ng-app". Anyway,lets cut to the chase and get this thing done for you! Give me access to all the code and I will rewrite for you and provide detailed comments and explanations for future reference.
that's really appreciated. but how can i send my files to you
|
1

Add a directive to #slides

app.directive("slideDirective", function () {
     return function (scope, element, attrs) {
         // Dom
         element.slides({
             preload: true,
             preloadImage: '/content/images/theme/loading.gif',
             play: 5000,
             pause: 2500,
             hoverPause: true
         });
     }
}

4 Comments

Can you be more specific? This is how I implemented another jQuery script that needed a doc ready function.
i first created a directive like above and then i call it like this; <div id="slider" slide-directive>. However, i don't see any changes. I know that the best solution to this problem is directives but i'm new to angular and directives are a little far from me
You need to use a timeout so that .slides is called after angular is done manipulating the dom
@JasonGoemaat this is exactly what helped in my case (bxSlider-plugin)
1

as it was said: "The jQuery slider script should only be activated after angular has loaded the content" So you can wait before it will be loaded, and then your jQuery code will start running.

$(document).ready(function () {
setTimeout(function(){
      $('#slides').slides({
        preload: true,
        preloadImage: '/content/images/theme/loading.gif',
        play: 5000,
        pause: 2500,
        hoverPause: true
     });
}, 10);
});

1 Comment

What garanties do you have that the page will take 10ms to load? It depends the conection.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.