ng-style/Dynamic Photo Grid 上的 Angular Digest Loop [英] Angular Digest Loop on ng-style / Dynamic Photo Grid

查看:20
本文介绍了ng-style/Dynamic Photo Grid 上的 Angular Digest Loop的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以更改过滤对象的过滤器.但是当我使用 ng-style="item.gridSize"我的过滤器:(网格大小的算法取自 这里

angular.module("custom.modules.photoGrid", []).filter('photoSearch', [function () {功能getGrid(照片){var 输出 = [];var 高度 = [];var COLUMN_WIDTH = 227;无边距 = 6;无功增量 = 20;var size = window.innerWidth - 50;var n_columns = Math.floor(size/(2 * (COLUMN_WIDTH + MARGIN)));create_columns(n_columns);var small_images = [];函数 create_columns(n) {高度 = [];for (var i = 0; i < n; ++i) {高度推(0);}}函数 get_min_column() {var min_height = 无穷大;var min_i = -1;for (var i = 0; i < HEIGHTS.length; ++i) {如果 (HEIGHTS[i] < min_height) {min_height = HEIGHTS[i];min_i = i;}}返回 min_i;}函数 gridSize(i, is_big) {变量大小 = {'margin-left': (MARGIN + (COLUMN_WIDTH + MARGIN) * i)+'px','margin-top': (HEIGHTS[Math.floor(i/2)] * (COLUMN_WIDTH + MARGIN))+'px',宽度":is_big ?(COLUMN_WIDTH * 2 + MARGIN)+'px' : COLUMN_WIDTH+'px',高度":is_big ?(COLUMN_WIDTH * 2 + MARGIN)+'px' : COLUMN_WIDTH+'px'};退货尺寸;}函数创建网格(数据){如果(数据长度> = 2){for(var i = 0; i < data.length; i++){var column = get_min_column();如果(Math.random()> 0.8){data[i]['gridSize'] = gridSize(column * 2, true);高度[列] += 2;} 别的 {small_images.push(i);如果(small_images.length === 2){数据[small_images[0]]['gridSize'] = gridSize(column * 2, false);数据[small_images[1]]['gridSize'] = gridSize(column * 2 + 1, false);高度[列] += 1;small_images = [];}}}如果(small_images.length){column = get_min_column();data[(data.length-1)]['gridSize'] = gridSize(column * 2, false);}}返回数据;}var grid = createGrid(照片);返回网格;}返回函数(照片,搜索){var 过滤 = [];if(!!search){/**@case1 如果只存在搜索查询**/搜索 = search.toLowerCase();for(var i = 0; i 

HTML:

<span>{{ results.length }}</span>照片找到<div ng-repeat='照片中的照片 |photoSearch:input.value 作为结果跟踪 photo.id' class="photo-item" ng-style="photo.gridSize"><img ng-src="/photos/{{photo.url}}">

一个小解释:每次 ng-model input.value 更改的过滤器都会运行,并为过滤后的照片数组创建不同的网格.所有维度都写在 gridSize 内,这会导致摘要循环.

到目前为止我所尝试的:我已经将我的 ng-repeat 移动到指令中,但是这样我就无法访问 result.lengthinput.value.

我也尝试过 bindonce 指令,但像 bo-style="photo.gridSize" 一样使用它不会在用户搜索后改变网格(并且是逻辑上正确,因为只出价一次,但值已更改.

所以我的问题是如何让 ng-repeat 分配新的 grdiSize 属性而不在摘要循环中运行.

更新: JSFiddle

工作小提琴: JSFiddle

解决方案

有几个问题.这不完全是 ng-style 问题,而是在每个摘要周期中,您的照片都在计算不同样式的对象,从而导致另一个摘要周期运行.

我发现的一些问题:

  • 逻辑错误是给出 0 列,因此在计算 margin-top 时导致 size 给出 NaN 并失败.为了解决这个问题,我从 1 列中添加了一个默认值.
  • 你的 Math.random() >0.8 每次执行过滤器功能时都会给出不同的结果.在每个摘要循环中,由于 Math.random() 给出了不同的结果,它迫使另一个摘要循环(您正在更新 gridSize 对象 - 因为每个元素都有一个 $watchng-repeat 中,它检测更改并强制执行一个摘要循环),依此类推.那是控制台中的错误日志.

我创建了这个有效的 fiddle.主要变化是

在声明数组后为每张照片定义一个固定的随机值

$scope.photos.forEach(function(onePhoto){onePhoto.randomValue = Math.random();});

然后在过滤器中检查这个值

if (data[i].randomValue > 0.8) {}

并在创建列时设置最少 1 列

var n_columns = Math.max(1, Math.floor(size/(2 * (COLUMN_WIDTH + MARGIN))));

此外(但我相信这仅发生在您的小提琴中),没有要过滤的 photo_name,所以我改用了 id.

您可能想用其他默认值修复 NaN 问题,但至少现在您知道控制台错误的问题.

如果你想在每次执行搜索时更新你的 randomValue,你可以在你的 input.value 上放置一个 $watch,移动迭代照片和创建的代码随机值到一个函数中,并在该 watch 函数的回调中使用它.因此,每次更新搜索结果时,您的网格都会使用不同的随机值,而不会干扰摘要循环.类似于this

var updateRandomValues = function() {$scope.photos.forEach(function(onePhoto){onePhoto.randomValue = Math.random();});};更新随机值();$scope.$watch('input.value', function(newVal, oldVal) {如果(新值!== 旧值){更新随机值();}});

然而,如果你只在得到不同的结果时才想得到不同的 css 样式(请记住,如果你输入并得到与之前相同的结果,无论如何它都会更新你的网格布局),你应该$watch 是结果变量.喜欢这个

$scope.$watch('results', function(newVal, oldVal) {如果(新值!== 旧值){更新随机值();}});

I have a filter that changes filtered object. But when I'm using ng-style="item.gridSize" My Filter: (The Algorithm for size Grid was taken (changed for my needs) from Here

angular.module("custom.modules.photoGrid", []).filter('photoSearch', [function () {
    function getGrid(photos){
        var output = [];
        var HEIGHTS = [];
        var COLUMN_WIDTH = 227;
        var MARGIN = 6;
        var DELTA = 20;

        var size = window.innerWidth - 50;
        var n_columns = Math.floor(size / (2 * (COLUMN_WIDTH + MARGIN)));
        create_columns(n_columns);
        var small_images = [];

        function create_columns(n) {
            HEIGHTS = [];
            for (var i = 0; i < n; ++i) {
                HEIGHTS.push(0);
            }
        }

        function get_min_column() {
            var min_height = Infinity;
            var min_i = -1;
            for (var i = 0; i < HEIGHTS.length; ++i) {
                if (HEIGHTS[i] < min_height) {
                min_height = HEIGHTS[i];
                min_i = i;
                }
            }
            return min_i;
        }

        function gridSize(i, is_big) {
            var size = {
                'margin-left': (MARGIN + (COLUMN_WIDTH + MARGIN) * i)+'px',
                'margin-top': (HEIGHTS[Math.floor(i / 2)] * (COLUMN_WIDTH + MARGIN))+'px',
                'width': is_big ? (COLUMN_WIDTH * 2 + MARGIN)+'px' : COLUMN_WIDTH+'px',
                'height': is_big ? (COLUMN_WIDTH * 2 + MARGIN)+'px' : COLUMN_WIDTH+'px'
            };
            return size;
        }
        function createGrid(data){
            if (data.length >= 2) {
                for(var i = 0; i < data.length; i++){
                    var column = get_min_column();
                    if (Math.random() > 0.8) {
                        data[i]['gridSize'] = gridSize(column * 2, true);
                        HEIGHTS[column] += 2;
                    } else {
                        small_images.push(i);
                        if (small_images.length === 2) {
                            data[small_images[0]]['gridSize'] = gridSize(column * 2, false);
                            data[small_images[1]]['gridSize'] = gridSize(column * 2 + 1, false);
                            HEIGHTS[column] += 1;
                            small_images = [];
                        }
                    }
                }
                if (small_images.length) {
                    column = get_min_column();
                    data[(data.length-1)]['gridSize'] = gridSize(column * 2, false);
                }
            }

            return data;
        }
        var grid = createGrid(photos);
        return grid;
    }


    return function(photos, search) {
        var filtered = [];
        if(!!search){ /**@case1 if only search query is present**/
            search = search.toLowerCase();
            for(var i = 0; i < photos.length; i++){
                if(photos[i].photo_name.toLowerCase().indexOf(search) !== -1){
                    filtered.push(photos[i]);
                }
            }

        }else {
            /**@case2 no query is present**/
            filtered = photos;
        }
        filtered = getGrid(filtered);
        return filtered;
    }
}]);

Html:

<input type="text" ng-model="input.value"> <span>{{ results.length }}</span> Photo Found
<div ng-repeat='photo in photos | photoSearch:input.value as results track by photo.id' class="photo-item" ng-style="photo.gridSize">
                    <img ng-src="/photos/{{photo.url}}">
                </div>

A small explanation: Every time ng-model input.value changed filter is runed and creates different grid for filtered array of photos. all dimensions are written inside gridSize and this cause digest loop.

What I've tried until now: I've moved my ng-repeat in directive, but this way I can't access result.length and input.value.

I've also tried a bindonce directive but using it like bo-style="photo.gridSize" doesn't change the grid after user search(and is logically right because is bidden only once, but values changed.

So my question is how to make ng-repeat assign new grdiSize property without running in digest loop.

UPDATE: JSFiddle

Working Fiddle: JSFiddle

解决方案

There were a couple of issues. It was not exactly a ng-style problem, but rather than in each digest cycle your photos were calculating different style objects, causing another digest cycle to run.

Some issues I've found:

  • Error in logic was giving 0 colums, thus causing size to give NaN when calculating margin-top and failing. To fix this, I added a default value from 1 column.
  • your Math.random() > 0.8 was giving different results in each time your filter function was executing. In each digest cycle, since Math.random() gives different results, it was forcing another digest loop (you were updating gridSize object - since there's a $watch for each element in the ng-repeat it detects the changes and forces one digest cycle), and so on. That was the error log in console.

I created this fiddle that works. The main changes are

defined a fixed random value for each photo, after declaring your array

$scope.photos.forEach(function(onePhoto){
    onePhoto.randomValue = Math.random();
  });

then in the filter you would check against this value

if (data[i].randomValue > 0.8) {
}

and set a minimum of 1 column when creating columns

var n_columns = Math.max(1, Math.floor(size / (2 * (COLUMN_WIDTH + MARGIN))));

Also (but I believe this occured only in your fiddle), there was no photo_name to filter for, so I used id instead.

you might want to fix the NaN problem with other default value, but at least now you know the problems with the console error.

If you wanted to update your randomValue everytime you executed the search, you could place a $watch over your input.value, move the code that iterates photos and creates random values into a function, and use that in the callback for that watch function. So everytime you update results in search, your grid uses different random values without causing interfering with digest cycle. Something like this

var updateRandomValues = function() {
    $scope.photos.forEach(function(onePhoto){
        onePhoto.randomValue = Math.random();
      });
  };
  updateRandomValues();
  $scope.$watch('input.value', function(newVal, oldVal) {
    if (newVal !== oldVal) {
        updateRandomValues();
    }
  });

However, if you want to get different css style only when you get different results (keep in mind that if you type and get same results as before, it will update your grid layout anyway), what you should $watch is the results variable instead. Like this

$scope.$watch('results', function(newVal, oldVal) {
    if (newVal !== oldVal) {
        updateRandomValues();
    }
  });

这篇关于ng-style/Dynamic Photo Grid 上的 Angular Digest Loop的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆