Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mikermcneil/f8faae5903f049640d15 to your computer and use it in GitHub Desktop.
Save mikermcneil/f8faae5903f049640d15 to your computer and use it in GitHub Desktop.
An example of bootstrapping dynamic data onto the page, and then exposing it for programmatic access with `rttc.compile()`. This makes it possible to expose our view locals in our client-side js for access in the browser. This example uses Sails.js, but is equally applicable for Hapi or Express.

Bootstrapping dynamic data onto the page with rttc.compile()

An example of bootstrapping dynamic data onto the page, and then exposing it for programmatic access with rttc.compile(). This makes it possible to expose our view locals in our client-side js for access in the browser. This example uses Sails.js, but is equally applicable for Hapi or Express.

UPDATE: Check out balderdashy/sails#3522 and https://github.com/mikermcneil/expose-locals-to-browser-xss-test-app


Let's assume you are building a website about vampires.

In your view template (pages/all-vampires.ejs), you might write:

<div class="topbar">
  <span>Logged in as <%= me.name %></span>
</div>
<div class="vampire-list-component" is="vampire-list-component">
  <h2>All known vampires in the Los Angeles area:</h2>
  <ul class="vampire-list">
  <% _.each(vampires, function (vampire){ %>
    <li data-vampire-username="<%=vampire.username%>">
      <span><%= vampire.name %></span>
      <button is="summon-btn">Summon</button>
    </li>
  <% }); %>
  </ul>
</div>


<%
// If present, expose the `dataToExposeToBrowserJs` local to client-side js as `window.SAILS_LOCALS`:
if(typeof dataToExposeToBrowserJs !== undefined) { %>
<script type="text/javascript">
window.SAILS_LOCALS = <%- dataToExposeToBrowserJs %>;
</script>
<% } %>

<script type="text/javascript">
// * * (normally the code in this second script block would be in a separate client-side js file) * *

// When the summon button is clicked, trigger the `summon` function for this vampire.
$('[is="vampire-list-component"]').on('click', '[is="summon-btn"]', function whenSummonButtonIsClicked(e) {
  
  // Look up the clicked vampire in SAILS_LOCALS
  var clickedVampire = _.find(window.SAILS_LOCALS.vampires, {
    username: $(e.currentTarget).closest('[data-vampire-username]').attr('data-vampire-username')
  });
  
  // Call its summon function
  // (obviously the summon function has to contain logic which runs in the browser)
  var summoningStatusReport = clickedVampire.summon(window.SAILS_LOCALS.me.name);
  
  // Will log:
  // >  At last, the browser!  I am born again!  Thanks, Rupert.
  //
  // Or:
  // >  ...

  // And return:
  // > { mood: 'hungry' }
  // Or:
  // > { mood: 'brooding' }
});
</script>

using EJS on the server, and provided some data as a view local (as well as providing access to rttc):

var SOME_WATCHER = { name: 'Rupert Giles', hobbies: ['dirty dancing'], favoriteVampire: 'angelus' };
var SOME_VAMPIRES = [
  {
    username: 'angelus',
    name: 'Angelus',
    hobbies: ['taiji'],
    summon: function noteThatRttcCompilePreservesFunctionNamesIfProvided(){
      console.log('...');}
      return {mood: 'brooding'};
    },
  {
    username: 'darla',
    name: 'Darla',
    hobbies: ['torture'],
    summon: function (yourName){
      console.log('At last, the browser!  I am born again!  Thanks, '+ yourName+ '.');
      return {mood: 'hungry'};
    }
  }
];
return res.view('pages/my-sweet-page', {
  watcher: SOME_WATCHER,
  vampires: SOME_VAMPIRES,
  dataToExposeToBrowserJs: require('rttc').compile({
    watcher: SOME_WATCHER,
    vampires: SOME_VAMPIRES,
    otherStuff: {like: 'this'},
    orMaybe: [ { 'even another function': function (){ console.log('like this'); } } ]
  }
})
@mikermcneil
Copy link
Author

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