How can I nest loops in a directive?

5 views
Skip to first unread message

michael...@gmail.com

unread,
Sep 12, 2016, 12:32:46 AM9/12/16
to JavaScript Templates Engine PURE

First, thanks for making PURE available. The concept makes perfect sense.

I'm trying use PURE in a JQMobile app with multiple pages.  Most pages are variations on a layout that can be described succinctly in JSON or YAML.  (See the code comments below.) After getting familiar with simple examples, I took a stab at writing a template and data+directives for a portion of the real app. I'm using CoffeeScript to generate the JS and that's what I've included below for readability.  There's a complete Gist with the HTML and JS concatenated at 
https://gist.github.com/Michael-F-Ellis/aecdf25694b9b2c3577240fb09f19274.

The problem is that PURE is throwing: "
pure.js:84 Uncaught pure error: cannot have more than one loop on a target".  Is that actually a limitation and, if so, how can I work around it?

Here's the CoffeeScript:


pagedata =
 
###
 
Pages have one or more blocks of readouts and setpoints
  arranged side
-by-side beneath the page title. From left to
  right we suffix the block classes
with "a b c ..." to match
  the jQuery
Mobile grid layout syntax.
 
###
  pages
: [
    id
: 'ah-cal', title: 'Air-Humidity Calibration',
    blocks
: [
     
'id' : 'air-cal', letter: 'a',
   
, 'id' : 'water-cal', letter: 'b'
   
]


 
, id: 'wx-cal', title: 'Water-Aux Calibration',
    blocks
: [
     
'id' : 'water-cal', letter: 'a',
   
, 'id' : 'aux-cal', letter: 'b'
   
]
 
]


pagedirective
=
   
###
   
Expands page template.  Applies ids and titles.
   
Expands blocks in each page applying id's and class.
   ###
   "div[data-role='
page']" :
     '
page<-pages' :
       '
@id' : 'page.id'
       "div[data-role='
content'] > h2" : 'page.title'
       '
blk<-page.blocks':
         '
div.block@id' : 'blk.id'
         '
div.block@class': 'ui-block-' + 'blk.letter'


$p('
body').render pagedata, pagedirective  




And here's the HTML

<!DOCTYPE html>
<html>
<head>
<title>PURE Test</title>
<script src="http://pure.github.io/pure/libs/pure.js"></script>
</head>
<body>


<!-- HTML template -->
<div data-role="page">
   
<div data-role="header"></div>
   
<div data-role="content">
       
<h2></h2>
       
<div class="ui-grid-a">
           
<div class="block">
               
<div class="ui-bar ui-bar-a" style="height: 100%;">
                   
<form class="pgc-parms">
                       
<div class='ui-field-contain'>
                           
<label></label>
                           
<input class="number pgc-parm"/>
                       
</div>
                   
</form>
               
</div>
           
</div>
       
</div>
   
</div>
   
<div data-role="footer"></div>
</div>


<script src="junk.js">


</script>


</body>
</html>  

 



Mic (BeeBole)

unread,
Sep 12, 2016, 12:53:57 AM9/12/16
to JavaScript Templates Engine PURE, michael...@gmail.com
Hi Michael,

The syntax of the loop is: 
  • first the DOM target to be iterated
  • then the loop property names ( <- )
  • and finally the inside of the block
The directive of the second loop should be like this:

      'div.block':{
         
'blk<-page.blocks': {
           
'@id': 'blk.id',
           
'@class': 'ui-block-#{blk.letter}'
         
}
     
}

Also, to concatenate strings with dynamic values, you can use #{...} 
'ui-block-#{blk.letter}'
Will be concatenated at run time.

instead of 
'ui-block-' + 'blk.letter'

will be concatenated when the js is loaded and give the property name 'ui-block-blk.letter'

Michael Ellis

unread,
Sep 12, 2016, 8:45:53 AM9/12/16
to Mic (BeeBole), JavaScript Templates Engine PURE
Hi Mic,
Thanks for the prompt and thorough explanation.  It's great to know that loops can be nested with the proper syntax and the distinction between compile time and run time for string catenation. Before your response came in I split the directive and render calls apart, like this:

pagedirective =
   ###
   Expands page template.  Applies ids and titles.
   TODO Add entry to apply grid letter to grid block.
   ###
   "div[data-role='page']" :
     'page<-pages' :
       '@id' : 'page.id'
       "div[data-role='content'] > h2" : 'page.title'


blockdirective =
  ### Replicate blocks, applying id and class letter. ###
  "div.ui-block-" :
    'blk<-blocks':
      '@id' : 'blk.id'
      '@class+': "blk.letter"

p('body').render pagedata, pagedirective   # pages

for page in pagedata.pages                 # widget blocks
  $p('#' + page.id).render page, blockdirective              

The above is working correctly.  Are there any tradeoffs between doing it this way vs nesting the loops in a single directive?


Mic (BeeBole)

unread,
Sep 12, 2016, 3:42:41 PM9/12/16
to JavaScript Templates Engine PURE, m...@beebole.com, michael...@gmail.com
If you have many pages, the overhead of calling multiple renders may start to be slower than the direct nested render.
This is especially the reason I never delivered the version 3 of pure( DOM only, without innerHTML ), because building loops are way faster with innerHTML( what you don't have with the multiple renders ).
I always nest my loops :)

Michael Ellis

unread,
Sep 12, 2016, 4:53:09 PM9/12/16
to Mic (BeeBole), JavaScript Templates Engine PURE
Thanks, Mic, I'll keep that in mind.  It wouldn't be hard to re-write it nested if I'm pressed for performance.  This app will have maybe 20 pages in total, each displaying  2 to 10 readouts and inputs. I've got the content of 10 of those pages fully implemented.  I enabled timestamps in Chrome Dev Tools and logged the top of my page generator script, the start of rendering, and the start time for each page. On my development system it takes 62 milliseconds start to finish.

2016-09-12 16:27:02.305 pgc-page-generator.js:36 Enter Page Generator
2016-09-12 16:27:02.305 pgc-page-generator.js:916 Render start
2016-09-12 16:27:02.367 pgc-page-generator.js:938 Render end

Just for reference, my directives and render loops are below.  The CoffeeScript for the data blocks (omitted) is about 400 lines. 

AFAICT, the render time is going to be the least of my worries compared to the load and run times for JQ and JQ Mobile. Or to put it another way: You've done a helluva job with this tool!

Cheers,
Mike


### Directives ###
pagedirective =
   ###
   Expands page template.  Applies ids and titles.
   Applies grid letter to grid block.
   ###
   "div[data-role='page']":
     'page<-pages':
       '@id': 'page.id'
       "div[data-role='content'] > h2": 'page.title'
       "div.ui-grid-@class+": "page.grid"
blockdirective =
  ### Replicate blocks, applying id and class letter. ###
  "div.ui-block-":
    'blk<-blocks':
      '@id': 'blk.id'
      '@class+': "blk.letter"
formdirective =
  ### applies data to label and input within a form. ###
  'div.readout-top@style': (a) ->
    if a.context.top_readouts.length is 0
      'display: none;'
    else
      ''
  'div.readout-top':
    'readout<-top_readouts':
      'div > span.readout-label': 'readout.text'
      'div > span.readout-label@class': 'readout.lclasses'
      'div > span.readout-value@data-pgc-parm-name': 'readout.pvar'
      'div > span.readout-value@class': 'readout.vclasses'

  'form':
    'input<-inputs':
      'div > label@class': 'input.lclass'
      'div > label': 'input.ltext'
      'div > input@data-pgc-parm-name': 'input.pvar'

  'div.readout-top@style': (a) ->
    if a.context.top_readouts.length is 0
      'display: none;'
    else
      ''
  'div.readout-btm':
    'readout<-btm_readouts':
      'div > span.readout-label': 'readout.text'
      'div > span.readout-label@class': 'readout.lclasses'
      'div > span.readout-value@data-pgc-parm-name': 'readout.pvar'
      'div > span.readout-value@class': 'readout.vclasses'

### ************************************************************************ ###
### Rendering ###
console.log "Render start"
$p('body').render pagedata, pagedirective  # pages

for page in pagedata.pages                 # widget blocks
  console.log page.id
  $p('#' + page.id).render page, blockdirective

for page in pagedata.pages                 # widgets
  for block in page.blocks
    bar_sel = '#' + block.id + ' div.ui-bar'
    $p(bar_sel).render block.widgets, formdirective
console.log "Render end"
Reply all
Reply to author
Forward
0 new messages