15

Problem: I want to create a modal lightbox using the Angular UI Bootstrap modal

Details: I have built a photo grid using ng-repeat. Each repeated photo opens the modal using the open() method. I'm struggling with how to pass the scope of the clicked item to the modal so I can grab the image url to display. I've implemented the scope parameter on the modal, which gives me access to the parent; however the parent is the parent scope of the clicked item and contains the whole array of all images in the grid. I need to figure out how to tell (programmatically) which index has been clicked, or send just the child scope to the modal. I'm a newbie... if I'm missing something or there's a better way to approach this, any help is welcome.

My HTML:

<section ng-controller="ModalDemoCtrl">
  <div ng-repeat="photo in photos.data">

    <img src="{{photo.source}}" class="thumbnail img-responsive" ng-click="open()">

  </div>
</section>

Instance and controller:

app.controller('ModalDemoCtrl', function ($scope, $modal, $log) {

  $scope.items = ['item1', 'item2', 'item3'];

  $scope.open = function (scope) {

    var modalInstance = $modal.open({
      templateUrl: 'myModalContent.html',
      scope: $scope,
      controller: ModalInstanceCtrl,
      resolve: {
        items: function () {
          return $scope.items;
        },
// this returns as undefined
        photo: function(){
            return $scope.photo;
        }
      }
    });


    modalInstance.result.then(function (selectedItem) {
      $scope.selected = selectedItem;
    }, function () {
      $log.info('Modal dismissed at: ' + new Date());
    });
  };
});


var ModalInstanceCtrl = function ($scope, $modalInstance, items, photo) {



  $scope.items = items;
  $scope.photo = photo;
  $scope.selected = {
    item: $scope.items[0]
  };


  $scope.ok = function () {
    $modalInstance.close($scope.selected.item);
  };


  $scope.cancel = function () {
    $modalInstance.dismiss('cancel');
  };
  console.log($scope);

};

This is basically how scope looks. The item index I need is buried deep and I need to know (programmatically) which one was clicked. I need the source off Index[0]

$scope
--$parent
---$parent
----$photos
-----$$v
------data
-------0
--------Source
-------1
-------2
-------3
-------4
-------5
-------6
-------7
-------8
Kram62
  • 275
  • 1
  • 2
  • 7

3 Answers3

18

You could just do something like this.

HTML

<img src="{{photo.source}}" class="thumbnail img-responsive" ng-click="open(photo)">

Javascript

$scope.open = function (photo) {

  var modalInstance = $modal.open({
    templateUrl: 'myModalContent.html',
    scope: $scope,
    controller: ModalInstanceCtrl,
    resolve: {
      items: function () {
        return $scope.items;
      },

      photo: function(){
          return photo;
      }
    }
});
rob
  • 17,995
  • 12
  • 69
  • 94
  • That did it... removing the $scope from the function!! Thank you for pointing that out. Do you know why $scope is not required? I would have thought that any reference to scopes attribute would have required $scope – Kram62 Feb 27 '14 at 04:13
  • @user2679704 You are refrencing the `photo` from the parameter of the open function. And you are passing photo into that function from the html in the ng-click. There is only one scope for the whole controller is putting the particular photo on $scope wouldn't really make sense. – rob Feb 27 '14 at 04:16
  • This is basically the same solution that MightySchmoePong pointed out – rob Feb 27 '14 at 04:18
  • Thanks Rob and MightySchmoePong for your answer and explanation. I can sleep easy now =). – Kram62 Feb 27 '14 at 04:25
  • Great answer, useful to know that you need to explicitly inject the scope with: scope: $scope – Snick Apr 21 '15 at 12:50
6

I tried to post this as a comment but apparently it's too long. So, I will post it as an answer even though the correct answers have been given.

If your definition was

$scope.open = function (xyz) {...

then your resolve would be

...photo: function(){ return xyz;}

You just got confused because you had used string name 'photo' as your function parameter. It has nothing to do with the scope. Also in your resolve definition, you could have called it anything instead of photo

...abc: function() {return xyz} 

and then use abc in your

ModelInstanceCtrl(... , abc)

Again, there is no link to scope here. You are just passing along a value from

open(photo) to function (xyz) to ModalInstanceCtrl (... , abc)

Within the controller you can set it to anything you would like

$scope.xxx = abc;

photo actually does not exist in the main scope since ng-repeat creates a local scope within the loop. photo is only visible within the loop and that's why you have to pass along to the controller through the function open() parameter. I am new to Angular and keeping track of scope lives has been challenging. Experts out there, please correct me if I am wrong.

Chi Row
  • 1,106
  • 7
  • 18
3

Can't you just pass the photo into open? I.e. ng-click="open(photo)"

  • Thanks for your answer. However, I tried that but it returns as undefined. – Kram62 Feb 27 '14 at 04:01
  • Did you do it as the first argument or did you still have scope there? I.e. $scope.open = function (phone) {} vs. $scope.open = function (scope, phone) {} – MightySchmoePong Feb 27 '14 at 04:08
  • I applied as a first function $scope.open = function (phone) {}. But apparently changing the return statement from return $scope.photo to return photo; did the trick. Though I'm not quite sure why. I would have thought any reference to a scope would have required $scope. – Kram62 Feb 27 '14 at 04:19