revolunet / angular-carousel

Mobile friendly AngularJS carousel
http://revolunet.github.io/angular-carousel
MIT License
1.55k stars 705 forks source link

Callback on slideChange #331

Open gmocciaro opened 9 years ago

gmocciaro commented 9 years ago

It will be useful to put a callback on the slide changes (i found the methods used: nextslide() and/or goToSlide()), i need to understand which element is active when the slide changes (in order to make graphic/behavior changes)

gmocciaro commented 9 years ago

i found a workaround (little bit dirty) to detect the active element, you can retrieve actual index with rn-carousel-index and watch for it (with $scope.$watch). the rn-carousel-index will be the index of your array aswell so you can retrieve the required information.

tonejac commented 9 years ago

Hi @Lemolas, can you supply a code sample of the $scope.$watch function for the rn-carousel-index?

Best regards, Tony

gmocciaro commented 9 years ago

Hi @tonejac, this is what i did html:

<div id="home_featured_slider">
    <ul rn-carousel="" rn-carousel-controls="" rn-carousel-index="carouselIndex" rn-carousel-auto-slide="">
        <li ng-repeat="slideImages in data">
            <img src="{{slideImages.event_thumbnails[500]}}">
        </li>
    </ul>
</div>

js:

$scope.$watch('carouselIndex', function(newValue) {
    console.log(jQuery('#home_featured_slider ul > li:nth-child(' + (+newValue + 1) + ')').html());
});

Little explanation:

i'm assuming that 'carouselIndex' is the index list of my object/array. So i built an object with n elements and the 'carouselIndex' variable contains the actual element active. Assuming that the number of element inside the ng-repeat is the same of the index count, i'm able to take the current carousel position. Doing this, you can even access directly to the object to take other information.

$scope.featuredSliderActual = []; //  this will contain the ACTIVE carousel item
 $scope.$watch('carouselIndex', function(newValue) {
    $scope.featuredSliderActual = $scope.data[newValue]; // data is my complete object
    console.log(jQuery('#home_featured_slider ul > li:nth-child(' + (+newValue + 1) + ')').html());
});

so you can manipulate all the element inside (in my case, i'm able to take the right price and calculate the currencies)

$scope.featuredSliderActual = []; //  this will contain the ACTIVE carousel item
 $scope.$watch('carouselIndex', function(newValue) {
    $scope.featuredSliderActual = $scope.data[newValue]; // data is my complete object
    console.log(jQuery('#home_featured_slider ul > li:nth-child(' + (+newValue + 1) + ')').html());

    $scope.priceList = {
                price_aud : $scope.featuredSliderActual.price_aud,
                price_eur : $scope.featuredSliderActual.price_eur,
                price_gbp : $scope.featuredSliderActual.price_gbp,
                price_usd : $scope.featuredSliderActual.price_usd
            };
});

so the $scope variable 'priceList' will contain always the right price to show.

let me know if you need something more. Best regards, Guglielmo

tonejac commented 9 years ago

That's fantastic! I implemented it for my wine site here: http://app.winetracker.co/#!/wines/all/gallery

I am now able to run the formatting functions on the child elements (eg truncate text to make it fit on each individual carousel slide. 

Thanks for the code sample. Totally cleared it up for me! 

Sincerely,

Tony

— Sent from Mailbox

On Mon, Jun 29, 2015 at 3:52 AM, Lemolas notifications@github.com wrote:

Hi @tonejac, this is what i did html:

    js: $scope.$watch('carouselIndex', function(newValue) { console.log(jQuery('#home_featured_slider ul > li:nth-child(' + (+newValue + 1) + ')').html()); }); Little explanation: i'm assuming that 'carouselIndex' is the index list of my object/array. So i built an object with n elements and the 'carouselIndex' variable contains the actual element active. Assuming that the number of element inside the ng-repeat is the same of the index count, i'm able to take the current carousel position. Doing this, you can even access directly to the object to take other information. $scope.featuredSliderActual = []; // this will contain the ACTIVE carousel item $scope.$watch('carouselIndex', function(newValue) { $scope.featuredSliderActual = $scope.data[newValue]; // data is my complete object console.log(jQuery('#home_featured_slider ul > li:nth-child(' + (+newValue + 1) + ')').html()); }); so you can manipulate all the element inside (in my case, i'm able to take the right price and calculate the currencies) $scope.featuredSliderActual = []; // this will contain the ACTIVE carousel item $scope.$watch('carouselIndex', function(newValue) { $scope.featuredSliderActual = $scope.data[newValue]; // data is my complete object console.log(jQuery('#home_featured_slider ul > li:nth-child(' + (+newValue + 1) + ')').html()); $scope.priceList = { price_aud : $scope.featuredSliderActual.price_aud, price_eur : $scope.featuredSliderActual.price_eur, price_gbp : $scope.featuredSliderActual.price_gbp, price_usd : $scope.featuredSliderActual.price_usd }; }); so the $scope variable 'priceList' will contain always the right price to show. let me know if you need something more. Best regards, ## Guglielmo Reply to this email directly or view it on GitHub: https://github.com/revolunet/angular-carousel/issues/331#issuecomment-116604502
revolunet commented 9 years ago

nice except there's a little flickering due to the watch i guess

gmocciaro commented 9 years ago

@revolunet i know that isn't the best or the cleanest solution, but it's the more effective solution i found so far

gmocciaro commented 9 years ago

@tonejac you can try to wait for the variable to change in the $watch to avoid the "trembling" just doing this

$scope.$watch('variable', function(current, updated){
            if (current != updated){
                // CODE HERE...
            }
        });

but i think that this bad effect doesn't depend on the $watch

tonejac commented 9 years ago

Yes you're right the text trembling was happening before. I'm not sure why the carousel does that. Have you experienced that problem before?

— Sent from Mailbox

On Tue, Jun 30, 2015 at 3:47 AM, Guglielmo Mocciaro notifications@github.com wrote:

@tonejac you can try to wait for the variable to change in the $watch to avoid the "trembling" just doing this

$scope.$watch('variable', function(current, updated){
            if (current != updated){
                // CODE HERE...
            }
        });

but i think that this bad effect doesn't depend on the $watch

Reply to this email directly or view it on GitHub: https://github.com/revolunet/angular-carousel/issues/331#issuecomment-117122138

gmocciaro commented 9 years ago

never... you can try to put here your code (shortly) and i can give a look. I guess that you're showing some variable that doesn't refresh itself in the right way. It seems that is the variable that handles the wine description

tonejac commented 9 years ago

Here's the controller where my carousel logic is located:

    $scope.generateSlides = function() {

        function getSlide(target) {
            var i = target.length;
            return {
                index: i,
                indexClass: 'slide'+i,
                img: 'http://images.winetracker.co/rs/' + $scope.fullList[i].photoURL,
                imgThumb: 'http://images.winetracker.co/rs2/' + $scope.fullList[i].photoURL,
                scoreTotal: $scope.fullList[i].scoreTotal,
                comment: $scope.fullList[i].comment,
                created: $scope.fullList[i].created,
                obj: $scope.fullList[i]
            };
        }

        function addSlide(target) {
            target.push(getSlide(target));
        }

        $scope.carouselIndex = 0;

        function addSlides(target, qty) {
            for (var i=0; i < qty; i++) {
                addSlide(target);
            }
        }

        $scope.slides = [];
        addSlides($scope.slides, $scope.fullList.length);

        /* Additional carousel custom functionality */
        $scope.$watch(function(scope) {
                return scope.carouselIndex
            }, function() {
                ViewController.TruncateWineComments();
            }
        );

        $(document).keyup(function(e) {
            if (e.keyCode === 37) {
                $('.rn-carousel-control-prev').trigger('click'); // LEFT ARROW keypress
            } else if (e.keyCode === 39) {
                $('.rn-carousel-control-next').trigger('click');  // RIGHT ARROW keypress
            }

            ViewController.TruncateWineComments();

        });

        ViewController.HideSwiperGraphic();

        setTimeout(function() {
            ViewController.TruncateWineComments();
        }, 0);

        if (window.currentWineIndex) {

            //$scope.goToSlide( window.currentWineIndex );
            $scope.carouselIndex = window.currentWineIndex;

            window.currentWineIndex = undefined;
            ViewController._firstRunSliders = true; // reset the slider gate after done editing
        }

    };

And here is the dom markup:

<div ng-controller="WinesController" id="wine-gallery-viewer" data-ng-init="findOthers()">

    <ul rn-carousel rn-carousel-controls rn-carousel-index="carouselIndex" rn-carousel-buffered class="owl-carousel" rn-carousel-easing="easeOutQuad" rn-carousel-duration="400">
        <li ng-repeat="slide in slides track by slide.index" ng-class="{{slide.indexClass}}" class="item">

            <div class="inner-container">
                <img ng-src="{{ slide.img }}" ondragstart="return false;" />
            </div>

            <div class="gradient-overlay"></div>

            <div class="wine-details-container" ng-click="showDetails($event, {{ slide.obj }}, slide.index)">
                <div class="score-number">
                    <span>{{ slide.scoreTotal | number:0  }}<sup>.{{ (slide.scoreTotal | number:1).toString().split('.')[1] }}</sup></span>
                </div>
                <div class="wine-title" data-state="closed">
                    <div class="content">
                        <strong class="ng-binding">by {{ slide.obj.user.username }}</strong><br/>
                        {{ slide.comment }} <span class="show-more-link">view details</span>
                    </div>
                </div>
            </div>

            <div class="full-details-container">
                <div class="content">
                    <button class="button-close">
                        <svg version="1.1" class="close-button-svg-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="12.896px" height="12.896px" viewBox="0 0 12.896 12.896" enable-background="new 0 0 12.896 12.896" xml:space="preserve"><path fill="#231F20" d="M8.569,6.448l3.89-3.89c0.583-0.583,0.583-1.538,0-2.121s-1.538-0.583-2.121,0l-3.89,3.89l-3.89-3.89 c-0.583-0.583-1.538-0.583-2.121,0v0c-0.583,0.583-0.583,1.538,0,2.121l3.889,3.889l-3.889,3.889c-0.583,0.584-0.583,1.538,0,2.121 l0,0c0.583,0.584,1.538,0.584,2.121,0l3.89-3.889l3.889,3.889c0.583,0.584,1.538,0.584,2.121,0c0.583-0.583,0.583-1.537,0-2.121 L8.569,6.448z"/></svg>
                    </button>
                    <p class="score-number">
                        <span>{{ slide.scoreTotal | number:0  }}<sup>.{{ (slide.scoreTotal | number:1).toString().split('.')[1] }}</sup></span>
                    </p>
                    <p class="date-time-stamp">
                        {{ slide.created | date:'EEEE' }}<br/>
                        {{ slide.created | date:'d/MMM/y' }}<br/>
                    </p>
                    <p class="comment">
                        <strong class="ng-binding">by {{ slide.obj.user.username }}</strong><br/>
                        {{ slide.comment }}
                    </p>
                    <div class="details-action-container">
                        <button class="button-add-to-wishlist" ng-click="navigateToEditWineForAddToWishlist(slide.obj)">
                            <svg id="icon-add-to-my-wishlist" width="21px" height="19px" viewBox="0 0 21 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="Star-5-+-Star-4-+-Star-3-Copy" sketch:type="MSLayerGroup" fill="#302613"><polygon id="Star-5" sketch:type="MSShapeGroup" points="9.1875 14.535572 3.78722299 18.2860499 5.7700764 12.1102693 0.449668257 8.25241159 7.07541606 8.18604723 9.1875 2.05128205 11.2995839 8.18604723 17.9253317 8.25241159 12.6049236 12.1102693 14.587777 18.2860499 "></polygon><polygon id="Star-4" sketch:type="MSShapeGroup" points="18.2374828 2.88572297 16.6294901 3.88312838 17.6529091 2.31600821 16.6294901 0.748888048 18.2374828 1.74629346 19.8454755 0.748888048 18.8220564 2.31600821 19.8454755 3.88312838 "></polygon><polygon id="Star-3" sketch:type="MSShapeGroup" points="13.335573 5.44982554 11.7275803 6.44723094 12.7509993 4.88011078 11.7275803 3.31299061 13.335573 4.31039602 14.9435656 3.31299061 13.9201466 4.88011078 14.9435656 6.44723094 "></polygon></g></g></svg>
                            Add to My Wishlist
                        </button>
                    </div>
                </div>
            </div>

        </li>
    </ul>

</div>

I already tried commenting out the 'ViewController.TruncateWineComments();' function but that made no difference. The other note is that the '.full-details-container' div is offscreen below the viewport and slides up when the user clicks anywhere on the comment or score.

Would appreciate any thoughts why the comment text and score are flickering as they user swipes.

I noticed that it doesn't do any flickering until the carousel slides are dynamically re-writing (after it gets to carouselindex of greater than 2).

gmocciaro commented 9 years ago

Which version of angular are you using? i'm not sure about this

/* Additional carousel custom functionality */
        $scope.$watch(function(scope) {
                return scope.carouselIndex
            }, function() {
                ViewController.TruncateWineComments();
            }
        );

according to the documentation (https://docs.angularjs.org/guide/scope#scope-watch-performance-considerations)

scope.$watch('name', function(newValue, oldValue) {
  scope.counter = scope.counter + 1;
});

You should put the $scope variable you're scoping (basically, angular triggers the funtion when the variable 'name' changes. In our case, when the carousel changes the variable carouselIndex

tonejac commented 9 years ago

I'm using this version of angular:

* @license AngularJS v1.2.28

The blinking of the comments text was happening before I introduced the $watch() function. It looks like it might be calling some sort of 'render' function twice on the set of 5 slides that are being buffered.

Any ideas as to what I might explore?