简体   繁体   中英

How do I assign an angularjs event listener inside an ng-repeat loop?

I am new to angularjs. I am trying to create a photo gallery where the initial page is a sheet of thumbnail photos from twitter and instagram created with an angularjs ng-repeat loop. When the user hovers over a image the image fades and a button appears for the user to click. I have created the hover effect using ng-mouseenter and ng-mouseleave. But for some reason when I hover the mouse over an image all of the images are affected. Only the single image that I hover should be targeted. I am not sure what I have done wrong. Please Help! The code is below. Also here is a link to the code online: http://www.petermingione.com/my-demo/

<style>
        * {
            box-sizing: border-box;
            font-family: sans-serif;
        }
        h1, 
        h2{
            margin-left:10px;
        }
        .inner{
            display:inline-block;
            vertical-align:top;
            padding:5px;
            position:relative;
        }
        .inner img{
            width:100%;
        }
        .inner .outer-caption{
            color:black;
            width:375px;
            height:125px;
            background-color:#fc7cab;
            display:flex;
        }
        .inner .outer-caption .inner-caption{
            font-size:14px;
            font-family:sans-serif;
            overflow:hidden;
            width:320px;
            height:70px;
            margin:auto;
        }
        .inner .overlay-color{
            position: absolute;
            top:0;
            bottom:0;
            left:0;
            right:0;
            margin:auto;
            background-color: #fff;
            transition: all .5s;    
        }
        .inner .overlay-text{
            border: 2px solid #7e7e7e;
            color:#7e7e7e;
            font-size: 10px;
            position: absolute;
            top:0;
            bottom:0;
            left:0;
            right:0;
            width:70px;
            height:35px;
            line-height:35px;
            margin:auto;
            border-radius:1px;
            text-align: center;
            transition: all .5s;

    </style>

<body>  
    <h1>{{title}}</h1> 
    <h2>{{myMessage}}</h2>
    <div id="outer" class="outer">
        <div class="inner" ng-mouseenter="makeVisibile()" ng-mouseleave="makeInVisibile()" ng-repeat="x in insideData">
            <img ng-if="x.service=='Instagram'||(x.service=='Twitter' && x.mediaType=='image')" ng-src='{{x.thumbnails[0].url}}'>
             <div ng-if="x.service=='Twitter'&& x.mediaType!='image'" class="outer-caption"><div class="inner-caption">{{x.caption}}</div></div>
            <div class="overlay-color" ng-style="overlayColorStyles" ></div>
            <div class="overlay-text" ng-style="overlayTextStyles">VIEW</div>
        </div>
    </div>

<script>

    // Create the module
    var appModule = angular.module('appName', []);

    // Create rootScope variables
    appModule.run(
        function($rootScope){
            $rootScope.title = "Taneleer Demonstration";
        }
    );

    // Create the controller
    appModule.controller('appCtrl', function($scope, $http) {

        $scope.overlayColorStyles = {"opacity": 0};
        $scope.overlayTextStyles =  {"opacity": 0};

        $scope.makeVisibile = function(){
            //alert("test"); 
            //document.getElementById("overlay-color").style.opacity = .75;
            //document.getElementById("overlay-text").style.opacity = 1;
            $scope.overlayColorStyles = {"opacity" : .75};
            $scope.overlayTextStyles = {"opacity" : 1};

        }
        $scope.makeInVisibile = function(){
            //alert("test"); 
            //document.getElementById("overlay-color").style.opacity = 0;
            //document.getElementById("overlay-text").style.opacity = 0;
            $scope.overlayColorStyles = {"opacity" : 0};
            $scope.overlayTextStyles = {"opacity" : 0};

        }

        $http({
                method : "GET",
                url : "https://taneleer.composedcreative.com/api/v1/feed/a0329f16-9225-11e6-89bb-296a97b9d609/bb0429f6-f0ca-11e7-8f5d-d92739a9a53f"
            }).then(function mySucces(response) {

                $scope.myMessage = "Success!";

                $scope.response = response;
                $scope.meta = response.data.meta;
                $scope.outsideData = response.data;
                $scope.insideData = response.data.data;
                $scope.links = response.data.links;

                $scope.selfLink = response.data.links.self;
                $scope.firstLink = response.data.links.first;
                $scope.lastLink = response.data.links.last;
                $scope.nextLink = response.data.links.next;
                $scope.prevLink = response.data.links.prev;

                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   

            }, function myError(response) {
                $scope.myMessage = "Error!";
                $scope.response = response;
                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   
            });


        $scope.getNext = function() {
            $http({
                method : "GET",
                url : $scope.nextLink
            }).then(function mySucces(response) {

                $scope.myMessage = "Success!";
                $scope.response = response;
                $scope.outsideData = response.data;
                $scope.meta = response.data.meta;

                $scope.insideData = $scope.insideData.concat(response.data.data);

                $scope.links = response.data.links;
                $scope.selfLink = response.data.links.self;
                $scope.firstLink = response.data.links.first;
                $scope.lastLink = response.data.links.last;
                $scope.nextLink = response.data.links.next;
                $scope.prevLink = response.data.links.prev;

                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   

            }, function myError(response) {
                $scope.myMessage = "Error!";
                $scope.response = response;
                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   
            });
        }
        $(window).scroll(function() {
            if($(window).scrollTop() + $(window).height() == $(document).height()) {
                $scope.getNext();
            }
        });
    });

</script>

Hi, Thanks again for the answer Aaron. I have one more issue that I need to resolve. I am now building out the light-box portion of the app. That is the overlay that you see when you click on the view button. I have placed the latest version here online: http://www.petermingione.com/my-demo/ As you can see regardless of which image you click on the only image that appears is the first image in the collection. Again this is another problem with ng-repeat. The image is located in each object as mainImage.url and I am accessing it through x.mainImage.url in the ng-repeat loop. I am not sure why it isn't working. Any help that anyone can give me would be greatly appreciated. The code is below and online:

   <style>
    * {
        box-sizing: border-box;
        font-family: sans-serif;
    }
    h1, 
    h2{
        margin-left:10px;
    }

    .outer-wrapper{
        overflow: hidden;
    }
    .inner-wrapper{
        display:inline-block;
        vertical-align:top;
        padding:5px;
        position:relative;
    }
    .inner-wrapper img{
        width:100%;
    }
    .inner-wrapper .outer-caption{
        color:black;
        width:100%;
        padding-top:35%;
        background-color:#fc7cab;
        position:relative;
    }
    .inner-wrapper .outer-caption .inner-caption{
        font-size:14px;
        font-family:sans-serif;
        overflow:hidden;
        width:75%;
        height:70px;
        position:absolute;
        top:0;
        left:0;
        bottom:0;
        right:0;
        margin:auto;
    }
    .inner-wrapper .item-overlay-color{
        position: absolute;
        top:0;
        bottom:0;
        left:0;
        right:0;
        margin:auto;
        background-color: #fff;
        transition: all .5s;
        opacity: 0.0;   
    }
    .inner-wrapper:hover .item-overlay-color {
        opacity: 0.75;
    }
    .inner-wrapper .item-overlay-text{
        border: 2px solid #7e7e7e;
        color:#7e7e7e;
        font-size: 10px;
        position: absolute;
        top:0;
        bottom:0;
        left:0;
        right:0;
        width:70px;
        height:35px;
        line-height:35px;
        margin:auto;
        border-radius:1px;
        text-align: center;
        transition: all .5s;
        opacity: 0.0;
    }
    .inner-wrapper:hover .item-overlay-text {
        opacity: 1.0;
    }
    .inner-wrapper .page-overlay {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0; 
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(255,255,255,0.75);
        z-index: 2;
        cursor: pointer;
    }
    .inner-wrapper #page-overlay {
        opacity:0;
        transition: all .5s;
        pointer-events:none;
    }
    .inner-wrapper .page-overlay .text{
        position: absolute;
        top: 50%;
        left: 50%;
        font-size: 50px;
        color: white;
        transform: translate(-50%,-50%);
        -ms-transform: translate(-50%,-50%);
    }
    .inner-wrapper .page-overlay .text .close{
        position:absolute;
        top:0;
        right:0;
        color:#000;
        font-weight: lighter;
        font-size: 30px;
        line-height: 30px;
        padding-top:5px;
        padding-right:5px;
    }

    @media screen and (min-width: 1301px){
        .inner-wrapper{
            width:16.6666%;
        }
    }
    @media screen and (max-width: 1300px){
        .inner-wrapper{
            width:20%;
        }
    }
    @media screen and (max-width: 1024px){
        .inner-wrapper{
            width:25%;
        }
    }
    @media screen and (max-width: 768px){
        .inner-wrapper{
            width:50%;
        }
    }
    @media screen and (max-width: 480px){
        .inner-wrapper{
            width:100%;
        }
    }

  </style>

 <body>
<div id="outer-wrapper" class="outer-wrapper">
    <div id="inner-wrapper" class="inner-wrapper" ng-repeat="x in insideData">
        <img ng-if="x.service=='Instagram'||(x.service=='Twitter' && x.mediaType=='image')" ng-src='{{x.thumbnails[0].url}}'>
        <div class="outer-caption" ng-if="x.service=='Twitter'&& x.mediaType!='image'">
            <div class="inner-caption">{{x.caption}}</div>
        </div>
        <div class="item-overlay-color"></div>
        <div class="item-overlay-text" ng-click="showOverlay()">VIEW</div>
        <div id="page-overlay" class="page-overlay">
                <div class="text">
                    <img ng-src='{{x.mainImage.url}}'>
                    <span class="close" ng-click="hideOverlay()">X</span>
                </div>
        </div>
    </div>
</div>
 </body>
  <script>
    // Create the module
    var appModule = angular.module('appName', []);

    // Create rootScope variables
    appModule.run(
        function($rootScope){
            $rootScope.title = "Taneleer Demonstration";
        }
    );

    // Create the controller
    appModule.controller('appCtrl', function($scope, $http) {

        $scope.showOverlay = function(){
            document.getElementById("page-overlay").style.opacity = 1;
            document.getElementById("page-overlay").style["pointer-events"] = "auto";
        }
        $scope.hideOverlay = function(){
            document.getElementById("page-overlay").style.opacity = 0;
            document.getElementById("page-overlay").style["pointer-events"] = "none";
        }

        $http({
                method : "GET",
                url : "https://taneleer.composedcreative.com/api/v1/feed/a0329f16-9225-11e6-89bb-296a97b9d609/bb0429f6-f0ca-11e7-8f5d-d92739a9a53f"
            }).then(function mySucces(response) {

                $scope.myMessage = "Success!";

                $scope.response = response;
                $scope.meta = response.data.meta;
                $scope.outsideData = response.data;
                $scope.insideData = response.data.data;
                $scope.links = response.data.links;

                $scope.selfLink = response.data.links.self;
                $scope.firstLink = response.data.links.first;
                $scope.lastLink = response.data.links.last;
                $scope.nextLink = response.data.links.next;
                $scope.prevLink = response.data.links.prev;

                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   

            }, function myError(response) {
                $scope.myMessage = "Error!";
                $scope.response = response;
                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   
            });


        $scope.getNext = function() {
            $http({
                method : "GET",
                url : $scope.nextLink
            }).then(function mySucces(response) {

                $scope.myMessage = "Success!";
                $scope.response = response;
                $scope.outsideData = response.data;
                $scope.meta = response.data.meta;

                $scope.insideData = $scope.insideData.concat(response.data.data);

                $scope.links = response.data.links;
                $scope.selfLink = response.data.links.self;
                $scope.firstLink = response.data.links.first;
                $scope.lastLink = response.data.links.last;
                $scope.nextLink = response.data.links.next;
                $scope.prevLink = response.data.links.prev;

                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   

            }, function myError(response) {
                $scope.myMessage = "Error!";
                $scope.response = response;
                $scope.statuscode = response.status;
                $scope.statustext = response.statusText;
                $scope.statusheaders = response.headers(); 
                $scope.statusconfig = response.config;   
            });
        }
        $(window).scroll(function() {
            if($(window).scrollTop() + $(window).height() == $(document).height()) {
                $scope.getNext();
            }
        });
    });

I know you asked how to do this the angular way, but ng-mouseenter and ng-mouseleave are VERY performance intensive, if you're planning to use this in a real world app, this should really be done with css.

I'll go ahead and show you how to fix your version, and then I'll give you a css version that's MUCH more efficient :)

For this use case, there's a very useful variable $index, which can be referenced in an angular template anywhere within inside of an ng-repeat tag to get the index of the current data item being iterated over. You should use that to save the index of the item that is currently hovered to the controller scope. Then, when the mouse leaves the item, you can discard the saved index.

Then when you're assigning styles, you can use a ternary to assign active or inactive styles based on whether the items index matches the active index variable saved on scope.

Also, in case you aren't familiar with the, a ternary expression is a shorthand for a conditional statement of the form conditional_expression ? value_returned_if_true : value_returned_if_false conditional_expression ? value_returned_if_true : value_returned_if_false . It's good for writing very concise conditional statements in javascript (or, in this case, an angular template with interprets javascript :) )

So, for this example, I use a combination of a ternary expression and saving the index of the last item the mouse entered to assign hover or non-hovered styles to each item.

And abbreviated version of your example:

Angular controller:

app.controller('exampleController', function($scope) {
  $scope.overlayColorStyles = {"opacity": 0};
  $scope.overlayTextStyles  = {"opacity": 0};
  $scope.overlayTextStylesActive  = {"opacity": 1};
  $scope.overlayColorStylesActive = { "opacity": .75};
  $scope.activeItemIndex    = null;
});

Template:

<div id="outer" class="outer">
  <div class="inner"
      ng-mouseenter="$scope.activeItemIndex = $index"
      ng-mouseleave="$scope.activeItemIndex = null"
      ng-repeat="x in insideData">
      <img ng-if="x.service=='Instagram'||(x.service=='Twitter' && x.mediaType=='image')" ng-src='{{x.thumbnails[0].url}}'>
      <div ng-if="x.service=='Twitter'&& x.mediaType!='image'" class="outer-caption"><div class="inner-caption">{{x.caption}}</div></div>
      <div class="overlay-color"
        ng-style="$scope.activeItemIndex === $index ? overlayColorStylesActive : overlayColorStyles">
      </div>
      <div class="overlay-text"
        ng-style="$scope.activeItemIndex === $index ? overlayTextStylesActive : overlayTextStyles">
        VIEW
      </div>
  </div>
</div>

Again though, this code should only be used for the sake of a learning exercise. Angular's job is not to apply styles based on user interaction, and if you try to use it that way, your application with get super slow as you start to add more items. Doing this with css is pretty trivial and will result in significantly better performance :)

css version below:

html:

<div id="outer" class="outer">
  <div class="inner"
      ng-repeat="x in insideData">
      <img ng-if="x.service=='Instagram'||(x.service=='Twitter' && x.mediaType=='image')" ng-src='{{x.thumbnails[0].url}}'>
      <div ng-if="x.service=='Twitter'&& x.mediaType!='image'" class="outer-caption"><div class="inner-caption">{{x.caption}}</div></div>
      <div class="overlay-color"></div>
      <div class="overlay-text">VIEW</div>
  </div>
</div>

css

.overlay-color {
  opacity: 0.0;
}

.overlay-text {
  opacity: 0.0;
}

.inner:hover .overlay-text {
  opacity: 1.0;
}

.inner:hover .overlay-color {
  opacity: 0.75;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM