Skip to content

Instantly share code, notes, and snippets.

@andrey-kazakov
Created January 23, 2013 23:26
Show Gist options
  • Save andrey-kazakov/4615694 to your computer and use it in GitHub Desktop.
Save andrey-kazakov/4615694 to your computer and use it in GitHub Desktop.
This AngularJS directive, when added to element, repeats it's content as many times as length of given array (almost like ng-repeat). The main difference is that DOM, once built, don't being changed on each $digest(). If you want to force the changing of repeated content, you should emit an event 'invalidateView' with (optionally) name of the sc…
angular
.module('ContentRepeat', [])
.directive 'contentrepeat', ($compile) ->
priority: 1000
terminal: true
compile: (element, attr, linker) ->
template = $compile(element.html())
element.empty()
(scope, self, attr) ->
viewValid = false
viewPending = false
debug = true
match = attr.contentrepeat.match /^\s*(.+)\s+in\s+(.*)\s*$/
instanceIdent = match[1]
collectionIdent = match[2]
invalidateIdent = attr.invalidate || instanceIdent
unregisterListener = scope.$parent.$on 'invalidateView', (event, which) ->
if not which or which == invalidateIdent
viewValid = false
console.log "view #{which} invalidated" if debug
unregisterWatcher = scope.$watch (scope) ->
if not viewValid and not viewPending
startTime = new Date()
self.empty()
collection = scope.$eval collectionIdent
if collection? and collection.length and collection.length > 0
for instance, index in collection
childScope = scope.$new()
childScope[instanceIdent] = instance
childScope.$index = index
childScope.$first = index == 0
childScope.$last = index == collection.length - 1
childScope.$middle = not (childScope.$first or childScope.$last)
template(childScope, (child, childScope) ->
if childScope.$first
viewPending = true
self.empty()
child.appendTo self
if childScope.$last
viewPending = false
)
viewValid = true
console.log "#{collectionIdent} compiled in #{(new Date()) - startTime} ms" if debug
else if debug
console.log "#{collectionIdent} wasn't redrawn due to validness" if viewValid
console.log "#{collectionIdent} wasn't redrawn due to pending render" if viewPending
if viewPending
viewValid = false # to perform render on next digest
scope.$on '$destroy', ->
unregisterListener()
unregisterWatcher()
console.log "contentrepeat for #{instanceIdent} destroyed" if debug
@andrey-kazakov
Copy link
Author

@donamkhanh

you should put an HTML element inside of a contentrepeat-marked tag. It's a limitation of angular's $compile.
This will work fine: <div contentrepeat="item in items"><span>{{ item.name }}</span></div>, but faster one is <div contentrepeat="item in items"><span ng-bind="item.name"></span></div>.

By the way, if you don't like my approach, you can use new angular's ng-repeat directive. It works extremely fast in 1.1.5 :)

@donamkhanh
Copy link

Thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment