michaelbromley / angularUtils

A place where I will collect useful re-usable Angular components that I make
MIT License
2k stars 858 forks source link

Loads of warnings on this code #300

Closed jjravleuven closed 8 years ago

jjravleuven commented 8 years ago

So i have this thing about running code I want to use through linter's and this code is rife with tons of problems... and I'm curious why you didn't check it yourself... however, I have listed quite a few for you

  line 65   col 34  'dirPaginateDirective' was used before it was defined.
  line 73   col 9   Inner functions should be listed at the top of the outer function.
  line 73   col 40  'dirPaginationCompileFn' was used before it was defined.
  line 225  col 32  'noCompileDirective' was used before it was defined.
  line 232  col 52  'dirPaginationControlsTemplateInstaller' was used before it was defined.
  line 236  col 44  'dirPaginationControlsDirective' was used before it was defined.
  line 253  col 9   Inner functions should be listed at the top of the outer function.
  line 253  col 45  'dirPaginationControlsLinkFn' was used before it was defined.
  line 293  col 31  Expected '!==' and instead saw '!='.
  line 301  col 35  Expected '!==' and instead saw '!='.
  line 433  col 32  'itemsPerPageFilter' was used before it was defined.
  line 464  col 31  'paginationService' was used before it was defined.
  line 469  col 9   Possible strict violation.
  line 478  col 9   Possible strict violation.
  line 482  col 9   Possible strict violation.
  line 486  col 9   Possible strict violation.
  line 490  col 9   Possible strict violation.
  line 493  col 9   Possible strict violation.
  line 498  col 9   Possible strict violation.
  line 501  col 9   Possible strict violation.
  line 505  col 9   Possible strict violation.
  line 508  col 9   Possible strict violation.
  line 512  col 9   Possible strict violation.
  line 516  col 9   Possible strict violation.
  line 524  col 40  'paginationTemplateProvider' was used before it was defined.
  line 528  col 9   Possible strict violation.
  line 532  col 9   Possible strict violation.

And through an online linter which indicates much of the code is never called at all:

(function() {
  2      
  3      'use strict'; 
  4  
  5      /**
  6       * Config
  7       */
  8      var moduleName = 'clientApp.paginationServices';
  9      var DEFAULT_ID = '__default';
 10  
 11      /**
 12       * Module
 13       */
 14      var module;
 15      try {
 16          module = angular.module(moduleName);
 17      } catch(err) {
 18          // named module does not exist, so create one
 19          module = angular.module(moduleName, []);
 20      }
 21  
 22      module
 23          .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective])
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 24          .directive('dirPaginateNoCompile', noCompileDirective)
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 25          .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective])
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 26          .filter('itemsPerPage', ['paginationService', itemsPerPageFilter])
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 27          .service('paginationService', paginationService)
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 28          .provider('paginationTemplate', paginationTemplateProvider)
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 29          .run(['$templateCache',dirPaginationControlsTemplateInstaller]);
     ========^
     lint warning: unexpected end of line; it is ambiguous whether these lines are part of the same statement
 30  
 31      function dirPaginateDirective($compile, $parse, paginationService) {
 32  
 33          return  {
 34              terminal: true,
 35              multiElement: true,
 36              compile: dirPaginationCompileFn
 37          };
 38  
 39          function dirPaginationCompileFn(tElement, tAttrs){
     ========^
     lint warning: unreachable code
 40  
 41              var expression = tAttrs.dirPaginate;
 42              // regex taken directly from https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L211
 43              var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
 44  
 45              var filterPattern = /\|\s*itemsPerPage\s*:[^|]*/;
 46              if (match[2].match(filterPattern) === null) {
 47                  throw 'pagination directive: the \'itemsPerPage\' filter must be set.';
 48              }
 49              var itemsPerPageFilterRemoved = match[2].replace(filterPattern, '');
 50              var collectionGetter = $parse(itemsPerPageFilterRemoved);
 51  
 52              addNoCompileAttributes(tElement);
 53  
 54              // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
 55              // dir-pagination-controls directives that may be looking for this ID.
 56              var rawId = tAttrs.paginationId || DEFAULT_ID;
 57              paginationService.registerInstance(rawId);
 58  
 59              return function dirPaginationLinkFn(scope, element, attrs){
 60  
 61                  // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
 62                  // potentially register a new ID if it evaluates to a different value than the rawId.
 63                  var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
 64                  paginationService.registerInstance(paginationId);
 65  
 66                  var repeatExpression = getRepeatExpression(expression, paginationId);
 67                  addNgRepeatToElement(element, attrs, repeatExpression);
 68  
 69                  removeTemporaryAttributes(element);
 70                  var compiled =  $compile(element);
 71  
 72                  var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId);
 73                  paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);
 74  
 75                  if (typeof attrs.totalItems !== 'undefined') {
 76                      paginationService.setAsyncModeTrue(paginationId);
 77                      scope.$watch(function() {
 78                          return $parse(attrs.totalItems)(scope);
 79                      }, function (result) {
 80                          if (0 <= result) {
 81                              paginationService.setCollectionLength(paginationId, result);
 82                          }
 83                      });
 84                  } else {
 85                      scope.$watchCollection(function() {
 86                          return collectionGetter(scope);
 87                      }, function(collection) {
 88                          if (collection) {
 89                              paginationService.setCollectionLength(paginationId, collection.length);
 90                          }
 91                      });
 92                  }
 93  
 94                  // Delegate to the link function returned by the new compilation of the ng-repeat
 95                  compiled(scope);
 96              };
 97          }
 98  
 99          /**
100           * If a pagination id has been specified, we need to check that it is present as the second argument passed to
101           * the itemsPerPage filter. If it is not there, we add it and return the modified expression.
102           *
103           * @param expression
104           * @param paginationId
105           * @returns {*}
106           */
107          function getRepeatExpression(expression, paginationId) {
108              var repeatExpression,
109                  idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);
110  
111              if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
112                  repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:[^|]*)/, "$1 : '" + paginationId + "'");
113              } else {
114                  repeatExpression = expression;
115              }
116  
117              return repeatExpression;
118          }
119  
120          /**
121           * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the
122           * appropriate multi-element ng-repeat to the first and last element in the range.
123           * @param element
124           * @param attrs
125           * @param repeatExpression
126           */
127          function addNgRepeatToElement(element, attrs, repeatExpression) {
128              if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) {
129                  // using multiElement mode (dir-paginate-start, dir-paginate-end)
130                  attrs.$set('ngRepeatStart', repeatExpression);
131                  element.eq(element.length - 1).attr('ng-repeat-end', true);
132              } else {
133                  attrs.$set('ngRepeat', repeatExpression);
134              }
135          }
136  
137          /**
138           * Adds the dir-paginate-no-compile directive to each element in the tElement range.
139           * @param tElement
140           */
141          function addNoCompileAttributes(tElement) {
142              angular.forEach(tElement, function(el) {
143                  if (el.nodeType === Node.ELEMENT_NODE) {
144                      angular.element(el).attr('dir-paginate-no-compile', true);
145                  }
146              });
147          }
148  
149          /**
150           * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives.
151           * @param element
152           */
153          function removeTemporaryAttributes(element) {
154              angular.forEach(element, function(el) {
155                  if (el.nodeType === Node.ELEMENT_NODE) {
156                      angular.element(el).removeAttr('dir-paginate-no-compile');
157                  }
158              });
159              element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate');
160              element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end');
161          }
162  
163          /**
164           * Creates a getter function for the current-page attribute, using the expression provided or a default value if
165           * no current-page expression was specified.
166           *
167           * @param scope
168           * @param attrs
169           * @param paginationId
170           * @returns {*}
171           */
172          function makeCurrentPageGetterFn(scope, attrs, paginationId) {
173              var currentPageGetter;
174              if (attrs.currentPage) {
175                  currentPageGetter = $parse(attrs.currentPage);
176              } else {
177                  // if the current-page attribute was not set, we'll make our own
178                  var defaultCurrentPage = paginationId + '__currentPage';
179                  scope[defaultCurrentPage] = 1;
180                  currentPageGetter = $parse(defaultCurrentPage);
181              }
182              return currentPageGetter;
183          }
184      }
     ====^
     warning: function dirPaginateDirective does not always return a value
185  
186      /**
187       * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end).
188       * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of
189       * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled.
190       */
191      function noCompileDirective() {
192          return {
193              priority: 5000,
194              terminal: true
195          };
196      }
197  
198      function dirPaginationControlsTemplateInstaller($templateCache) {
199          $templateCache.put('clientApp.paginationServices.template', '<ul class="pagination pagination-sm" data-ng-if="1 < pages.length"><li data-ng-if="boundaryLinks" data-ng-class="{ disabled : pagination.current == 1 }"><a href="" data-ng-click="setCurrent(1)">&laquo;</a></li><li data-ng-if="directionLinks" data-ng-class="{ disabled : pagination.current == 1 }"><a href="" data-ng-click="setCurrent(pagination.current - 1)">&lsaquo;</a></li><li data-ng-repeat="pageNumber in pages track by $index" data-ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' }"><a href="" data-ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a></li><li data-ng-if="directionLinks" data-ng-class="{ disabled : pagination.current == pagination.last }"><a href="" data-ng-click="setCurrent(pagination.current + 1)">&rsaquo;</a></li><li data-ng-if="boundaryLinks"  data-ng-class="{ disabled : pagination.current == pagination.last }"><a href="" data-ng-click="setCurrent(pagination.last)">&raquo;</a></li></ul>');
200      }
201  
202      function dirPaginationControlsDirective(paginationService, paginationTemplate) {
203  
204          var numberRegex = /^\d+$/;
205  
206          return {
207              restrict: 'AE',
208              templateUrl: function(elem, attrs) {
209                  return attrs.templateUrl || paginationTemplate.getPath();
210              },
211              scope: {
212                  maxSize: '=?',
213                  onPageChange: '&?',
214                  paginationId: '=?'
215              },
216              link: dirPaginationControlsLinkFn
217          };
218  
219          function dirPaginationControlsLinkFn(scope, element, attrs) {
     ========^
     lint warning: unreachable code
220  
221              // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
222              // not yet been linked (e.g. if it is inside an ng-if block), and in that case it prevents this controls directive from assuming that there is
223              // no corresponding dir-paginate directive and wrongly throwing an exception.
224              var rawId = attrs.paginationId ||  DEFAULT_ID;
225              var paginationId = scope.paginationId || attrs.paginationId ||  DEFAULT_ID;
226  
227              if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
228                  var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' ';
229                  throw 'pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive.';
230              }
231  
232              if (!scope.maxSize) { scope.maxSize = 9; }
233              scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
234              scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;
235  
236              var paginationRange = Math.max(scope.maxSize, 5);
237              scope.pages = [];
238              scope.pagination = {
239                  last: 1,
240                  current: 1
241              };
242              scope.range = {
243                  lower: 1,
244                  upper: 1,
245                  total: 1
246              };
247  
248              scope.$watch(function() {
249                  return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
250              }, function(length) {
251                  if (0 < length) {
252                      generatePagination();
253                  }
254              });
255  
256              scope.$watch(function() {
257                  return (paginationService.getItemsPerPage(paginationId));
258              }, function(current, previous) {
259                  if (current != previous && typeof previous !== 'undefined') {
260                      goToPage(scope.pagination.current);
261                  }
262              });
263  
264              scope.$watch(function() {
265                  return paginationService.getCurrentPage(paginationId);
266              }, function(currentPage, previousPage) {
267                  if (currentPage != previousPage) {
268                      goToPage(currentPage);
269                  }
270              });
271  
272              scope.setCurrent = function(num) {
273                  if (isValidPageNumber(num)) {
274                      num = parseInt(num, 10);
275                      paginationService.setCurrentPage(paginationId, num);
276                  }
277              };
278  
279              function goToPage(num) {
280                  if (isValidPageNumber(num)) {
281                      scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
282                      scope.pagination.current = num;
283                      updateRangeValues();
284  
285                      // if a callback has been set, then call it with the page number as an argument
286                      if (scope.onPageChange) {
287                          scope.onPageChange({ newPageNumber : num });
288                      }
289                  }
290              }
291  
292              function generatePagination() {
293                  var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;
     ==================================================================================^
     lint warning: parseInt missing radix parameter
294  
295                  scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
296                  scope.pagination.current = page;
297                  scope.pagination.last = scope.pages[scope.pages.length - 1];
298                  if (scope.pagination.last < scope.pagination.current) {
299                      scope.setCurrent(scope.pagination.last);
300                  } else {
301                      updateRangeValues();
302                  }
303              }
304  
305              /**
306               * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
307               * template to display the current page range, e.g. "showing 21 - 40 of 144 results";
308               */
309              function updateRangeValues() {
310                  var currentPage = paginationService.getCurrentPage(paginationId),
311                      itemsPerPage = paginationService.getItemsPerPage(paginationId),
312                      totalItems = paginationService.getCollectionLength(paginationId);
313  
314                  scope.range.lower = (currentPage - 1) * itemsPerPage + 1;
315                  scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
316                  scope.range.total = totalItems;
317              }
318  
319              function isValidPageNumber(num) {
320                  return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
321              }
322          }
323  
324          /**
325           * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
326           * links used in pagination
327           *
328           * @param currentPage
329           * @param rowsPerPage
330           * @param paginationRange
331           * @param collectionLength
332           * @returns {Array}
333           */
334          function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
335              var pages = [];
336              var totalPages = Math.ceil(collectionLength / rowsPerPage);
337              var halfWay = Math.ceil(paginationRange / 2);
338              var position;
339  
340              if (currentPage <= halfWay) {
341                  position = 'start';
342              } else if (totalPages - halfWay < currentPage) {
343                  position = 'end';
344              } else {
345                  position = 'middle';
346              }
347  
348              var ellipsesNeeded = paginationRange < totalPages;
349              var i = 1;
350              while (i <= totalPages && i <= paginationRange) {
351                  var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);
352  
353                  var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end'));
354                  var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start'));
355                  if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
356                      pages.push('...');
357                  } else {
358                      pages.push(pageNumber);
359                  }
360                  i ++;
361              }
362              return pages;
363          }
364  
365          /**
366           * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
367           *
368           * @param i
369           * @param currentPage
370           * @param paginationRange
371           * @param totalPages
372           * @returns {*}
373           */
374          function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
375              var halfWay = Math.ceil(paginationRange/2);
376              if (i === paginationRange) {
377                  return totalPages;
378              } else if (i === 1) {
379                  return i;
380              } else if (paginationRange < totalPages) {
381                  if (totalPages - halfWay < currentPage) {
382                      return totalPages - paginationRange + i;
383                  } else if (halfWay < currentPage) {
384                      return currentPage - halfWay + i;
385                  } else {
386                      return i;
387                  }
388              } else {
389                  return i;
390              }
391          }
392      }
     ====^
     warning: function dirPaginationControlsDirective does not always return a value
393  
394      /**
395       * This filter slices the collection into pages based on the current page number and number of items per page.
396       * @param paginationService
397       * @returns {Function}
398       */
399      function itemsPerPageFilter(paginationService) {
400  
401          return function(collection, itemsPerPage, paginationId) {
402              if (typeof (paginationId) === 'undefined') {
403                  paginationId = DEFAULT_ID;
404              }
405              if (!paginationService.isRegistered(paginationId)) {
406                  throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.';
407              }
408              var end;
409              var start;
410              if (collection instanceof Array) {
411                  itemsPerPage = parseInt(itemsPerPage) || 9999999999;
     ====================================================^
     lint warning: parseInt missing radix parameter
412                  if (paginationService.isAsyncMode(paginationId)) {
413                      start = 0;
414                  } else {
415                      start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage;
416                  }
417                  end = start + itemsPerPage;
418                  paginationService.setItemsPerPage(paginationId, itemsPerPage);
419  
420                  return collection.slice(start, end);
421              } else {
422                  return collection;
423              }
424          };
425      }
426  
427      /**
428       * This service allows the various parts of the module to communicate and stay in sync.
429       */
430      function paginationService() {
431  
432          var instances = {};
433          var lastRegisteredInstance;
434  
435          this.registerInstance = function(instanceId) {
436              if (typeof instances[instanceId] === 'undefined') {
437                  instances[instanceId] = {
438                      asyncMode: false
439                  };
440                  lastRegisteredInstance = instanceId;
441              }
442          };
443  
444          this.isRegistered = function(instanceId) {
445              return (typeof instances[instanceId] !== 'undefined');
446          };
447  
448          this.getLastInstanceId = function() {
449              return lastRegisteredInstance;
450          };
451  
452          this.setCurrentPageParser = function(instanceId, val, scope) {
453              instances[instanceId].currentPageParser = val;
454              instances[instanceId].context = scope;
455          };
456          this.setCurrentPage = function(instanceId, val) {
457              instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
458          };
459          this.getCurrentPage = function(instanceId) {
460              var parser = instances[instanceId].currentPageParser;
461              return parser ? parser(instances[instanceId].context) : 1;
462          };
463  
464          this.setItemsPerPage = function(instanceId, val) {
465              instances[instanceId].itemsPerPage = val;
466          };
467          this.getItemsPerPage = function(instanceId) {
468              return instances[instanceId].itemsPerPage;
469          };
470  
471          this.setCollectionLength = function(instanceId, val) {
472              instances[instanceId].collectionLength = val;
473          };
474          this.getCollectionLength = function(instanceId) {
475              return instances[instanceId].collectionLength;
476          };
477  
478          this.setAsyncModeTrue = function(instanceId) {
479              instances[instanceId].asyncMode = true;
480          };
481  
482          this.isAsyncMode = function(instanceId) {
483              return instances[instanceId].asyncMode;
484          };
485      }
486  
487      /**
488       * This provider allows global configuration of the template path used by the dir-pagination-controls directive.
489       */
490      function paginationTemplateProvider() {
491  
492          var templatePath = 'clientApp.paginationServices.template';
493  
494          this.setPath = function(path) {
495              templatePath = path;
496          };
497  
498          this.$get = function() {
499              return {
500                  getPath: function() {
501                      return templatePath;
502                  }
503              };
504          };
505      }
506  })();
jjravleuven commented 8 years ago

Basically this needs to be rewritten from scratch. I'm going to rewrite it. it is disturbing to me that people write code - don't test it, clean it and make sure it is 100% error free - before releasing it to the public.

michaelbromley commented 8 years ago

Hi, Thanks for bringing this to my attention. However, I have a number of problems with this issue:

Linters are mostly a stylistic & heuristic tool. That is, you can have perfectly valid code which may still produce linter errors (as is the case here). For example: 'dirPaginateDirective' was used before it was defined - due to the way that functions are hoisted in JavaScript, it is perfectly valid to do this, and is indeed the recommended pattern in the most widely-used AngularJS styleguide. Likewise, there are indeed times when it makes sense to use != rather than !== - making that call depends on a good understanding of the code involved - something the linter cannot always judge, which is why they usually provide switches to turn off certain inapplicable checks.

"it is disturbing to me that people write code - don't test it, clean it and make sure it is 100% error free - before releasing it to the public.".

Linting is completely different to testing, and serves a different purpose. This module actually has a pretty huge test suite - over 1000 lines of code vs. ~500 for the module itself. I would say it is unusually well-tested.

If you managed to find some code - public or otherwise - that is definitely 100% error free, I'd be very interested to know.

"Basically this needs to be rewritten from scratch. I'm going to rewrite it."

A complete rewrite is a drastic action to take when there are just some linter errors which don't actually impact the working of the code itself. But feel free - I'll happily review a pull request.

At the very least, I fully agree that parseInt() should be used with a radix parameter. My bad for missing that. If you want to add a linter to the build pipeline, that would also be constructive.

jjravleuven commented 8 years ago

If you managed to find some code - public or otherwise - that is definitely 100% error free, I'd be very interested to know.

In all the code which I write, and I have migrated to AngularJS exclusively - I have - within all of my code - zero warnings

It is possible to write code which is pristine. It just takes a bit more due diligence.

I realize that Javascript is a very forgiving language, but I do also believe that due to the number of new developers whom have entered the marketplace - whose focus is entirely on release and not quality - the quality of the work within our industry has dropped exponentially.

I've also - because I was curious - run a few tests and linted the code within AngularJS and the entire framework has less than 1% error in code based upon what you've written.

I mean no disrespect but linting is always a good place to start prior to serious code testing because it eliminates the obvious. I run all of my code through lint testing before I run code testing on it, and there is great satisfaction in "0 errors 0 warnings" in all the code I write.

There is also great satisfaction within the reality of knowing that my code - can stand up against all tests given it. No one ever suffered by aspiring to be better - and no one ever improved by dismissing their own errors, with excuses, justifications, validations, or flippant arguments.

michaelbromley commented 8 years ago

I agree with you that linting is a good thing. I have no argument with the concept. I agree that it would be better if this project used a linter in the build.

I do argue, however, that achieving a 100% pass from a linter gives no guarantee of the logical correctness of your code. Your 100% error-free code is only error-free as far as the linter is concerned. Whether it functions correctly is another matter.

Unit tests are a much better guarantee of this. Still not perfect, but nothing is.

There is also great satisfaction within the reality of knowing that my code - can stand up against all tests given it.

Agreed. Linting is just one type of test you can use against your code. Personally I get more satisfaction from a comprehensive unit test suite passing than from a linter's output. Ideally both are present.

I mean no disrespect

Then telling me that "Basically this needs to be rewritten from scratch." was probably not the way to put it originally.

jjravleuven commented 8 years ago

heh - of course my code functions properly. I use jasmine, built within an full grunt environment and e2e testing in a live coding environment etc - I have 25+ years experience in a wide array of languages if I was inadequate it would have been told by this point.

Then after ensuring my code functions properly, then and only then do I run it through a linter - to clean it up and then again, a fully verbose compile test - again to read the entire flow - and then again - testing it online on various testing environments to see if there are any thing I may have missed.

Now in regards to your pagination "code works properly" i've found numerous bugs within it, which I've had to modify. So it doesn't work correctly. It doesn't use an existing scope, rather it creates an entirely new scope with the elements from the existing scope - and that is duplication within angular - and as you know good angular design is to keep an eye on the size of the scope properly creating and destroying what isn't used.

The searching doesn't work alphabetically either, because I tested it with a generic alphabetical search and even after forcing the pagination code to use an existing scope rather than create entirely new scope assets, it searches from nested array items, rather than simply the represented scope items.

The pagination is counting incorrectly and entire functions are disconnected and not called at all, isolating them from the code.

You know this - i would be surprised that you didn't know it.

i'm not going to fork this repo. I am however going to borrow the same elements you borrowed from Google and write a completely different one, but as it's not my nature to make it public and to customize it for the individual project - I suspect this conversation is over.

Ego has no place in our industry, because it is fully and wholly rendered in the quality of the code.

It is also interesting to me, that you haven't even made an effort to read through the entire area's of error which do not all come from JSLint.

I want to make that clear.

michaelbromley commented 8 years ago

I suspect this conversation is over.

:+1: :100: :white_check_mark:

jjravleuven commented 8 years ago

Ok here's the thing and the reason i have to rewrite your pagination script from scratch.

1: very rarely does one use an object of:

$scope.items[
    item: {
        name: "Jane Doe",
        age: 21
    },
    item: {
        name: "John Smith"
        age: 25
    }
];

In most scenarios the $scope.object is:

$scope.items[
    0: {
        id: 1,
        user: {
            name: "Jane Doe",
            age: 21
        },
        department: {
            location: "123 Street",
            position: {
                input: "select",
                selected: {
                    id: 3,
                    value: "HR"
                }
            }
        },
        created: <date>
    },
    1: {
        id: 2,
        user: {
            name: "John Smith",
            age: 25
        },
        department: {
            location: "456 Avenue",
            position: {
                input: "select",
                selected: {
                    id: 2,
                    value: "Shipping/Receiving"
                }
            }
        },
        created: <date>
    }
];

Your pagination fails in this scenario. Yes it will list the items, yes it will (if properly configured) render the objects. However, the more in-depth the code is written (not using partials for example and creating html elements on the fly dynamically) then the paging, searching fails.

Instantly the $index is re-ordered - because each of these scope elements are re-created within the scope and isolated from the initial scope, rather than simply accessing the existing $scope.items object - which is poor angular $scope management. Why would you duplicate an existing scope into new individual scope items when they already exist? It's not an unreasonable question.

Then if a user wants to drill down further and add inline editing, and that inline editing has a variant collection of inputs, selects, radio/checkbox assignments based upon the scope.object.array.item - well the alphabetical search fails miserably, as does the global search for numeric values, especially if certain fields are hidden by display management. -> I have any field with an id hidden by default, because I have no reason to render the id of inline editing elements which will convert into a select list drop down which renders from a completely different scope (for the sale of the select list) while still using the items scope as the value.

Obviously because the $index on search fails as pagination has created entirely new scope elements per row, per element - it always returns the incorrect record - forcing a

for(in this row loop to capture the id)

scenario.

The point is to use the existing scope. Not duplicate the scope wasting resources.

I think that the pagination script should be checking the scope object, not deciding to create new scope elements and littering the scope with duplicate items.

There are a lot of issues with $scope.$watch and $scope.$watchCollection using your method.

I realize that you toss things here based upon your existing project, but to declare this to be a 'paginate almost anything' really means 'page only single array objects, but it's useless on any nested array object'

I used lint to go through your code to find out why - and that was when I noticed hey - most of these functions are entirely disconnected, and rather pointless as a 'universal solution' because the filters and directives - rather than being built as filters and directives - are all tossed into the service with the filters and directives referenced but not actually encapsulated within those filters and directives.

There is nothing wrong with someone else examining code - i never get my feelings hurt because someone else pointed out a flaw in my code so that I could (and do) correct it to make it better code.

And looking at the list of issues you ignore within your 'issues' section it is quite clear that you know this isn't the best you could have done.

michaelbromley commented 8 years ago

I'm sorry, but I genuinely don't understand most of what you wrote above. Perhaps that is a result of your 25+ years experience vs. my few. If you are so inclined and are able to reproduce a specific behaviour in a plunker demo, that would make it clear to me and also should be an actionable item for my growling list of open issues :wink:

"And looking at the list of issues you ignore within your 'issues' section it is quite clear that you know this isn't the best you could have done."

The reason the open issues list is longer than I'd like is a combination of a) lack of free time to spend triaging them b) waiting for information from the originator and b) some are genuinely very difficult and would require an inordinate amount of time to resolve. But also note that there are 219 closed vs 34 open at this writing.

If you choose not to open another specific issue regarding the faulty behaviour you outlined above, then I bid you farewell and good luck with your rewrite. :+1:

ghost commented 8 years ago

Lmfao at this thread. Is this real life?

jjravleuven commented 8 years ago

@Dillybob92 yup it is real. you know a lot of "coders" call themselves programmers, but let's face it they're not. they surf google, grab code written by other people, then attempt to integrate it into whatever project they're working on and then turn around and tell everyone they're a "programmer"

That's no different than putting fuel in your car at a gas station and telling everyone you're a mechanic, or putting WD40 in a squeaky door and telling people you are in renovations.

And the funny thing about that is that there is really a lot of code out there, which is propagated as "one size fits all" (like this particular script) yet is littered with errors, littered with warnings and so on.

Anyway, I rewrote it. It works for server side paging (the manner by which I'm using it) as well as my need for view type changes such as tables, panels, tiles, tables with nested object tables, and also works now with more complicated objects, such as nested javascript objects, and id/value key/value assignment pairs for inline editing (without using another google search based inline editor that is angular oriented - please see last paragraph).

Before buddy decided to be all dickwad condescending, I was going to share it with everyone, as a pull request, but then i thought fuck it. Why would I want to propagate and encourage people to call themselves programmers when all they do is surf the web and then jam other people's code calling it programming"

so yeah after looking at the list of errors which simply don't (and shouldn't) exist in my code apps... having zero errors, warnings in code is what we are SUPPOSE to be doing. But that's the difference between getting a pay cheque and building apps that companies buy from us... I'd rather build and sell it and move onto the next new idea then be 80 and I'm still working for someone else being a 1/2 assed faker who calls myself a "programmer" lol

But then again... I don't have a single html partial or segment and I only have 2 routes in all my angular applications - because the reality is - you don't need the retarded (not really programming) .when('build a new html page and then a new controller, and then a new service, and factory, and resource every time a client wants to add something) to the angular application, and my apps can be up and running and fully client facing in 72 hours - so yeah this is real life.

But enjoy taking other people's code slamming it into your app while making excuses to your clients as to why they can't do this, or that because you can't actually refactor the code you're ripping from other people lol

and btw - when I deal w/ coders? because I CAN code? I fire them if they choose to give me bullshit excuses for errors and warnings, instead of just writing GOOD code. I got into IT because of the 1/2 assed way those who like to call themselves programmers have the "this is good enough" approach to coding. Either do something well or get a job at McDonalds. period

michaelbromley commented 8 years ago

I'm gonna lock this thread now to protect the sanity of all involved. Let's get on with out lives.