Refactoring AngularJS Shop Variant filtering logic for improved speed

This commit is contained in:
Rob Harrington
2016-08-05 16:13:46 +10:00
parent f9b58b7b90
commit 47df8d6d8e
15 changed files with 142 additions and 131 deletions

View File

@@ -1,10 +1,2 @@
Darkswarm.controller "CartCtrl", ($scope, Cart, $timeout) ->
$scope.Cart = Cart
initializing = true
$scope.$watchCollection "Cart.line_items_present()", ->
if initializing
$timeout ->
initializing = false
else
$scope.Cart.orderChanged()

View File

@@ -1,5 +0,0 @@
Darkswarm.controller "LineItemCtrl", ($scope)->
$scope.$watch '[line_item.quantity, line_item.max_quantity]', (newValue, oldValue)->
if newValue != oldValue
$scope.Cart.orderChanged()
, true

View File

@@ -7,15 +7,35 @@ Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, Ord
$scope.filtersActive = true
$scope.limit = 10
$scope.order_cycle = OrderCycle.order_cycle
# $scope.infiniteDisabled = true
# All of this logic basically just replicates the functionality filtering an ng-repeat
# except that it allows us to filter a separate list before rendering, meaning that
# we can get much better performance when applying filters by resetting the limit on the
# number of products being rendered each time a filter is changed.
$scope.$watch "Products.loading", (newValue, oldValue) ->
$scope.updateFilteredProducts()
$scope.$broadcast("loadFilterSelectors") if !newValue
$scope.incrementLimit = ->
$scope.limit += 10 if $scope.limit < Products.products.length
if $scope.limit < Products.products.length
$scope.limit += 10
$scope.updateVisibleProducts()
$scope.$watchGroup ['query','taxonSelectors','propertySelectors'], ->
$scope.$watch 'query', -> $scope.updateFilteredProducts()
$scope.$watchCollection 'activeTaxons', -> $scope.updateFilteredProducts()
$scope.$watchCollection 'activeProperties', -> $scope.updateFilteredProducts()
$scope.updateFilteredProducts = ->
$scope.limit = 10
f1 = $filter('products')(Products.products, $scope.query)
f2 = $filter('taxons')(f1, $scope.activeTaxons)
$scope.filteredProducts = $filter('properties')(f2, $scope.activeProperties)
$scope.updateVisibleProducts()
$scope.updateVisibleProducts = ->
$scope.visibleProducts = $filter('limitTo')($scope.filteredProducts, $scope.limit)
$scope.searchKeypress = (e)->
code = e.keyCode || e.which

View File

@@ -1,6 +1,9 @@
Darkswarm.directive "shopVariant", ->
Darkswarm.directive "shopVariant", ->
restrict: 'E'
replace: true
templateUrl: 'shop_variant.html'
scope:
variant: '='
controller: ($scope, Cart) ->
$scope.$watchGroup ['variant.line_item.quantity', 'variant.line_item.max_quantity'], ->
Cart.adjust($scope.variant.line_item)

View File

@@ -11,7 +11,15 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo
for line_item in @line_items
line_item.variant.line_item = line_item
Variants.register line_item.variant
line_item.variant.extended_name = @extendedVariantName(line_item.variant)
adjust: (line_item) =>
line_item.total_price = line_item.variant.price_with_fees * line_item.quantity
if line_item.quantity > 0
@line_items.push line_item unless line_item in @line_items
else
index = @line_items.indexOf(line_item)
@line_items.splice(index, 1) if index >= 0
@orderChanged()
orderChanged: =>
@unsaved()
@@ -48,7 +56,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo
# TODO: These changes to quantity/max_quantity trigger another cart update, which
# is unnecessary.
for li in @line_items_present()
for li in @line_items when li.quantity > 0
if stockLevels[li.variant.id]?
li.variant.count_on_hand = stockLevels[li.variant.id].on_hand
if li.quantity > li.variant.count_on_hand
@@ -67,7 +75,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo
data: =>
variants = {}
for li in @line_items_present()
for li in @line_items when li.quantity > 0
variants[li.variant.id] =
quantity: li.quantity
max_quantity: li.max_quantity
@@ -89,45 +97,21 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, $modal, $roo
$(window).bind "beforeunload", ->
t 'order_not_saved_yet'
line_items_present: =>
@line_items.filter (li)->
li.quantity > 0
total_item_count: =>
@line_items_present().reduce (sum,li) ->
@line_items.reduce (sum,li) ->
sum = sum + li.quantity
, 0
empty: =>
@line_items_present().length == 0
@line_items.length == 0
total: =>
@line_items_present().map (li)->
li.variant.totalPrice()
@line_items.map (li)->
li.total_price
.reduce (t, price)->
t + price
, 0
register_variant: (variant)=>
exists = @line_items.some (li)-> li.variant == variant
@create_line_item(variant) unless exists
clear: ->
@line_items = []
storage.clearAll() # One day this will have to be moar GRANULAR
create_line_item: (variant)->
variant.extended_name = @extendedVariantName(variant)
variant.line_item =
variant: variant
quantity: null
max_quantity: null
@line_items.push variant.line_item
extendedVariantName: (variant) =>
if variant.product_name == variant.name_to_display
variant.product_name
else
name = "#{variant.product_name} - #{variant.name_to_display}"
name += " (#{variant.options_text})" if variant.options_text
name

View File

@@ -17,7 +17,6 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro
@extend()
@dereference()
@registerVariants()
@registerVariantsWithCart()
@loading = false
extend: ->
@@ -44,15 +43,7 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro
registerVariants: ->
for product in @products
if product.variants
product.variants = (Variants.register variant for variant in product.variants)
variant.product = product for variant in product.variants
if product.master
product.master.product = product
product.master = Variants.register product.master
registerVariantsWithCart: ->
for product in @products
if product.variants
for variant in product.variants
Cart.register_variant variant
Cart.register_variant product.master if product.master
product.variants = for variant in product.variants
variant = Variants.register variant
variant.product = product
variant

View File

@@ -9,8 +9,21 @@ Darkswarm.factory 'Variants', ->
@variants[variant.id] ||= @extend variant
extend: (variant)->
# Add totalPrice method to calculate line item total. This should be on a line item!
variant.totalPrice = ->
variant.price_with_fees * variant.line_item.quantity
variant.basePricePercentage = Math.round(variant.price / variant.price_with_fees * 100)
variant.extended_name = @extendedVariantName(variant)
variant.base_price_percentage = Math.round(variant.price / variant.price_with_fees * 100)
variant.line_item ||= @lineItemFor(variant) # line_item may have been initialised in Cart#constructor
variant.line_item.total_price = variant.price_with_fees * variant.line_item.quantity
variant
extendedVariantName: (variant) =>
if variant.product_name == variant.name_to_display
variant.product_name
else
name = "#{variant.product_name} - #{variant.name_to_display}"
name += " (#{variant.options_text})" if variant.options_text
name
lineItemFor: (variant) ->
variant: variant
quantity: null
max_quantity: null

View File

@@ -2,7 +2,7 @@
%span.joyride-nub.right
.joyride-content-wrapper
.collapsed{"ng-show" => "!expanded"}
%price-percentage{percentage: 'variant.basePricePercentage'}
%price-percentage{percentage: 'variant.base_price_percentage'}
%a{"ng-click" => "expanded = !expanded"}
%span{"ng-bind" => "::'price_breakdown' | t"}
%i.ofn-i_005-caret-down
@@ -10,26 +10,26 @@
.expanded{"ng-show" => "expanded"}
%ul
%li.cost
.right {{ variant.price | localizeCurrency }}
.right {{ ::variant.price | localizeCurrency }}
%span{"ng-bind" => "::'item_cost' | t"}
%li.admin-fee{"ng-if" => "::variant.fees.admin"}
.right {{ variant.fees.admin | localizeCurrency }}
.right {{ ::variant.fees.admin | localizeCurrency }}
%span{"ng-bind" => "::'admin_fee' | t"}
%li.sales-fee{"ng-if" => "::variant.fees.sales"}
.right {{ variant.fees.sales | localizeCurrency }}
.right {{ ::variant.fees.sales | localizeCurrency }}
%span{"ng-bind" => "::'sales_fee' | t"}
%li.packing-fee{"ng-if" => "::variant.fees.packing"}
.right {{ variant.fees.packing | localizeCurrency }}
.right {{ ::variant.fees.packing | localizeCurrency }}
%span{"ng-bind" => "::'packing_fee' | t"}
%li.transport-fee{"ng-if" => "::variant.fees.transport"}
.right {{ variant.fees.transport | localizeCurrency }}
.right {{ ::variant.fees.transport | localizeCurrency }}
%span{"ng-bind" => "::'transport_fee' | t"}
%li.fundraising-fee{"ng-if" => "::variant.fees.fundraising"}
.right {{ variant.fees.fundraising | localizeCurrency }}
.right {{ ::variant.fees.fundraising | localizeCurrency }}
%span{"ng-bind" => "::'fundraising_fee' | t"}
%li.total
%strong
.right = {{ variant.price_with_fees | localizeCurrency }}
.right = {{ ::variant.price_with_fees | localizeCurrency }}
&nbsp;
%a{"ng-click" => "expanded = !expanded"}

View File

@@ -1,5 +1,4 @@
.progress
.right {{'fees' | t}}
.right {{::'fees' | t}}
.meter
{{'item_cost' | t}}
{{::'item_cost' | t}}

View File

@@ -27,5 +27,5 @@
.small-12.medium-2.large-2.columns.total-price.text-right
.table-cell
%strong{"ng-class" => "{filled: variant.totalPrice()}"}
{{ variant.totalPrice() | localizeCurrency }}
%strong{"ng-class" => "{filled: variant.line_item.total_price}"}
{{ variant.line_item.total_price | localizeCurrency }}