0

UPDATE: After some very insightful code from @Marc Kline, I went back and cleaned up my page. It turned out that I had my controllers listed in reversed (My angular controller was inside the Isotope controller, instead of the other way round). Once I changed it back and cleaned off some additional scripting, it started working again. I have updated the code snippet to reflect the change. Thanks to Marc and S.O!

I am having trouble figuring out how can I add new items using Angular and still let Isotope manage their UI display.

I am using Isotope and Angular to render server results in a masonry style layout. When I add new items to the layout on a button click, angular adds it just fine. However, they do not appear in the context of the isotope UI and appear separately (and cannot be sorted, laid out or filtered using Isotope).

Here is my JS Code

    <!-- Define controller -->
    var contrl = app.controller("MainController", function($scope){
        $scope.items ={!lstXYZ}; //JSON data from server

//Function called by button click
        $scope.addItem = function(item)
        {
          $scope.items.push(item);
          $scope.item = {};
        }
    });

    contrl.$inject = ['$scope'];

Here is the HTML to display the server results...(Updated to show working code..refer comments)

<div ng-controller="MainController">
 <div class="isotope" id="isotopeContainer">
      <div ng-repeat="item in items">        
        <div class='element-item {{item.status}}' data-category='{{item.status}}'>
          <p class="number">{{item.type}}</p>
        </div>
      </div>
    </div>
  </div>

And here is my HTML button to add the new items

  <table>
    <tr>
      <td><input type="text" ng-model="item.status" /></td>
    </tr>
    <tr>
      <td><input type="text" ng-model="item.type" /></td>
    </tr>
    <tr>
      <td colspan="2"><input type="Button" value="Add" ng-click="addItem(item)" /> </td>
    </tr>
  </table>

I am not sure how do I ensure that Isotope can recognize the newly added element and re-animate the layout.

Any help or pointers will be very appreciated. Thanks!

DevIntern
  • 225
  • 2
  • 4
  • 17
  • Are you looking to do [this](http://isotope.metafizzy.co/methods.html#appended), triggered by `addItem()`? – Marc Kline May 18 '14 at 16:28
  • @MarcKline: Sort of...Not sure if this is clear in my head but I read somewhere that when using frameworks such as Angular, you want them to handle all the DOM manipulation. – DevIntern May 18 '14 at 17:49
  • Yes, generally you want to avoid doing your own imperative-styled DOM manipulation in Angular apps... but you're working with a third-party module which does DOM manipulation. So, you can't rely on Angular to trigger something in that module - you have to invoke it. – Marc Kline May 18 '14 at 18:13
  • @MarcKline: I think that makes sense and I may have to just go down that path. I have also looked at the libraries such as https://github.com/mankindsoftware/angular-isotope but was not sure if this is adding more moving parts to the mix of JS. Curious if you have any thoughts on how this library is resolving the additems / removeitems and if this is a cleaner approach. Thanks! – DevIntern May 18 '14 at 18:41
  • I looked very briefly at the code but quickly decided I would probably write my own code for any limited implementation of Isotope. See if you like the approach provided in my answer. If not, study their code and decide whether you're better of just using the library or taking the time to learn how they're handling similar problems. – Marc Kline May 19 '14 at 00:01

2 Answers2

0

ng-repeat takes care of adding the new element to the DOM for you. However, Isotope isn't doing any "watching" for you - you have to manually invoke a redraw of the container.

You could just add something like $("#isotopeContainer").isotope(...) directly to your controller, but in the spirit of keeping your controllers lean and free of DOM-related code, you should instead create a service. Something like:

myApp.service('Isotope', function(){

  this.init = function(container) {
    $(container).isotope({itemSelector: '.element-item'});
  };

  this.append = function(container, elem) {
    $(container).isotope('appended', elem);
  }

});

... where the first method initializes a new Isotope container and the next redraws the container after an item is appended.

You could then inject this service into any controller or directive, but directives probably are best for this scenario. In this Plunker, I created two new directives: one for Isotope containers and another for Isotype elements, and used the service to do the initialization and redrawing.

Marc Kline
  • 9,399
  • 1
  • 33
  • 36
  • I answered a similar question today, where the OP didn't ask for a solution that doesn't use `$timeout`. It requires less lines but does use $timeout. You may want to check it out anyway: http://stackoverflow.com/questions/23744591/ng-repeat-inside-of-a-directive-not-having-directive-functions-applied-to-it/23745790#23745790 – Marc Kline May 19 '14 at 20:41
  • 1
    I was looking to incorporate your suggestion and create directive / services, but discovered that I had the controllers listed in reverse order in my HTML. Once I changed those, Isotope was able to detect layout changes and work accordingly. I have updated the code above. If you see anything that I am missing, please let me know. I appreciate your guidance and it made me go back and review everything again. Thanks a lot. – DevIntern May 19 '14 at 23:36
0

In this particular case, my code was not written correctly. I have updated the question's code but wanted to mention it more clearly here...

Apparently, the beauty of Angular is that you do not need to bother with the underlying UI framework (Isotope in this case). As long as you update the Angular data array, the UI binding is updated automatically.

The only gotcha is to ensure that the UI framework div is within the context of your Angular div.

Here is the non-working code...Note that the isotope div is outside the Angular controller.

   <div class="isotope" id="isotopeContainer">
    <div ng-controller="MainController">

          <div ng-repeat="item in items">        
            <div class='element-item {{item.status}}' data-category='{{item.status}}'>
              <p class="number">{{item.type}}</p>
            </div>
          </div>
        </div>
      </div>

Here is the updated code with isotope running within the Angular controller context...

    <div ng-controller="MainController">
     <div class="isotope" id="isotopeContainer">
          <div ng-repeat="item in items">        
            <div class='element-item {{item.status}}' data-category='{{item.status}}'>
              <p class="number">{{item.type}}</p>
            </div>
          </div>
        </div>
      </div>

Hope this helps. I am thankful for all the responses and help I got from SO. Appreciate the learning opportunity.

DevIntern
  • 225
  • 2
  • 4
  • 17