Ionic-Material icon indicating copy to clipboard operation
Ionic-Material copied to clipboard

Ink effects not working on ng-repeat

Open ricomonster opened this issue 9 years ago • 13 comments

i have an ng-repeat and i've noticed if i clicked the list, no effects are showing. Here is the code that i've used

<div class="list">
            <a class="item item-text-wrap" ui-sref="route.detail" ng-repeat="route in searchResults">
                <h2>{{ route.name }}</h2>
                <p>
                    From <strong>{{ route.to }}</strong>
                    to <strong>{{ route.from }}</strong>
                </p>
            </a>
        </div>

ricomonster avatar May 14 '15 10:05 ricomonster

My workaround was to emit an event when the last item gets loaded then fire the display command when it's caught

Made a custom emitter directive (based on this http://stackoverflow.com/questions/15207788/calling-a-function-when-ng-repeat-has-finished)

angular.module('listUtils')
.directive('ngLastRepeat', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit('ngLastRepeat'+ (attr.ngLastRepeat ? '.'+attr.ngLastRepeat : ''));
                });
            }
        }
    }
});

then in the html, add the extra attribute:

<!-- "mylist" will be appended to the end of the ngLastRepeat event name -->
<div ng-repeat="item in items" ng-last-repeat="mylist">
...
</div>

then in the controller, I add the following where the displayEffect would normally sit:

$scope.$on('ngLastRepeat.mylist',function(e) {
  ionic.material.ink.displayEffect();
}

PatAtMacadamian avatar May 14 '15 15:05 PatAtMacadamian

@ricomonster, I have run into this in my own projects too and have done the approach @PatAtMacadamian shared (thanks dude!)

I have explored easier ways the framework can handle this, but there are performance tradeoffs with some of the easier options. Anybody have suggestions?

Until we can find a solution with a good balance of performance/ease, I think we should add @PatAtMacadamian's approach to the documentation. It's gotta be a rather common issue that's run into.

zachfitz avatar May 14 '15 17:05 zachfitz

Maybe we could implement a directive that extended (wraps) ng-repeat (material-repeat) that implemented the above workaround for now and we could work on it in the future

On Thu, 14 May 2015 at 18:25, Zach Fitzgerald [email protected] wrote:

@ricomonster https://github.com/ricomonster, I have run into this in my own projects too and have done the approach @PatAtMacadamian https://github.com/PatAtMacadamian shared (thanks dude!)

I have explored easier ways the framework can handle this, but there are performance tradeoffs with some of the easier options. Anybody have suggestions?

Until we can find a solution with a good balance of performance/ease, I think we should add @PatAtMacadamian https://github.com/PatAtMacadamian's approach to the documentation. It's gotta be a rather common issue that's run into.

— Reply to this email directly or view it on GitHub https://github.com/zachsoft/Ionic-Material/issues/46#issuecomment-102108300 .

rbutera avatar May 14 '15 17:05 rbutera

+1 @raibutera - I think that's a really sound approach

zachfitz avatar May 14 '15 17:05 zachfitz

thanks @PatAtMacadamian! btw, ink effect does not work also on modal. does the workaround of @PatAtMacadamian works for the modal ink effects?

ricomonster avatar May 15 '15 02:05 ricomonster

I would think it's the same underlying cause where displayEffect needs to be invoked again since new buttons are entering the DOM after the first time displayEffect() has run.

If you have more than a single need in the controller, you could probably piggyback on @PatAtMacadamian 's solution and name the emitter something generic like 'applyInk' so you can invoke it not just on the list adding, but after any new item is added to the DOM that needs ink.

ex.

$scope.$on('applyInk',function(e) {
  ionic.material.ink.displayEffect();
}

$scope.openModal = function() {
    $scope.modal.show();
    // Do stuff to DOM
    $scope.$emit('applyInk');
};
// Cleanup the modal when we're done with it
$scope.$on('$destroy', function() {
    $scope.modal.remove();
});

zachfitz avatar May 15 '15 03:05 zachfitz

will you guys put this by default on your next version release?

ricomonster avatar May 15 '15 03:05 ricomonster

We can add the directive as part of the library and add documentation for its usage.

I think we'll want to extend displayEffect of the library as well. Where we're needing to apply displayEffect to a smaller set of ink-able elements in the DOM, I propose we make ionic.material.ink.displayEffect() able to receive an argument that accepts an element, array of elements, or parent's selector string (like '#some-element').

This way, instead of displayEffect applying to all the ink elements in the DOM (most that were already initialized on the first .displayEffect()), we can just run it on the subset that's passed into the execution that needs the ink effect applied. It'll be more performant.

So, it would look something like:

$scope.$on('applyInk',function(e, applyTo) {
  ionic.material.ink.displayEffect(e, applyTo);
}

$scope.openModal = function() {
    $scope.modal.show();
    // Do stuff that effects the DOM
    $scope.$emit('applyInk', '#my-modal');
};

... and the displayEffect function of ionic.material.ink would look for and inspect what's passed as applyTo, and then apply the ink on just those items.

Thoughts?

zachfitz avatar May 15 '15 03:05 zachfitz

ya, kinda cool. i'm really excited about this. thanks for the reply and goodluck :)

ricomonster avatar May 15 '15 03:05 ricomonster

reopened as there are a number of actions we need to take as a result of this discussion (or refactor into separate issues, at least!)

rbutera avatar May 15 '15 03:05 rbutera

Thanks for the love. I was thinking about this last night. Some of the outlying issues may simply need the displayEffect call to be sent asynchronously to the bottom of the call stack, so that other async calls have a chance to fire. You might also want to add an optional argument to ink.displayEffect to allow either a numeric value to set the call as an async timeout call, or an eventName string value to set the call as an event listener.

some at the top of the code like this:

function displayEffect(delay) {
  if (!isNaN(Number(delay))) {
    //numeric 
    $timeout(diplayEffect,delay);
    return;
  }
  else if (delay && String(delay) === delay) {
    //non-empty string
    $scope.$on(delay,displayEffect);
    return;
  }
  ... //rest of the function
} 

PatAtMacadamian avatar May 15 '15 13:05 PatAtMacadamian

The workaround doesn't work if the list is populated by events. Any suggestion on how to adapt it?

$rootScope.$on('item.received', function (item) {
      $scope.items.push(item);
});

Test events:

$timeout(function () {
      $rootScope.$broadcast('item.received', {name: 'foo'});
}, 100);

$timeout(function () {
      $rootScope.$broadcast('item.received', {name: 'bar'});
}, 300);

$timeout(function () {
      $rootScope.$broadcast('item.received', {name: 'car'} 
}, 1000);

Events received in the first 300ms will display, others won't.

Thanks

fsamir avatar Oct 05 '16 23:10 fsamir

Not Fixed .i have created pen too .check issue http://stackoverflow.com/questions/40583555/ionic-material-issue-with-item-class

anujsphinx avatar Nov 14 '16 07:11 anujsphinx