Coder Social home page Coder Social logo

plates's Introduction

Framework components for node.js and the browser

Example HTTP Server:

var flatiron = require('flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.http);

app.router.get('/', function () {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' });
  this.res.end('Hello world!\n');
});

app.start(8080);

Example HTTPS Server:

var flatiron = require('flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.http, {
  https: {
    cert: 'path/to/cert.pem',
    key: 'path/to/key.pem',
    ca: 'path/to/ca.pem'
  }
});

app.router.get('/', function () {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' });
  this.res.end('Hello world!\n');
});

app.start(8080);

Example CLI Application:

// example.js

var flatiron = require('flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli, {
  dir: __dirname,
  usage: [
    'This is a basic flatiron cli application example!',
    '',
    'hello - say hello to somebody.'
  ]
});

app.cmd('hello', function () {
  app.prompt.get('name', function (err, result) {
    app.log.info('hello '+result.name+'!');
  })
})

app.start();

Run It:

% node example.js hello
prompt: name: world
info:   hello world!

Installation

Installing NPM (Node Package Manager)

  curl http://npmjs.org/install.sh | sh

Installing Flatiron

  [sudo] npm install flatiron

Installing Union (Required for flatiron.plugins.http)

  npm install union

Usage:

Start With flatiron.app:

flatiron.app is a broadway injection container. To be brief, what it does is allow plugins to modify the app object directly:

var flatiron = require('flatiron'),
    app = flatiron.app;

var hello = {
  attach: function (options) {
    this.hello = options.message || 'Why hello!';
  }
};

app.use(hello, {
  message: "Hi! How are you?"
});

// Will print, "Hi! How are you?"
console.log(app.hello);

Virtually all additional functionality in flatiron comes from broadway plugins, such as flatiron.plugins.http and flatiron.plugins.cli.

app.config

flatiron.app comes with a config plugin pre-loaded, which adds configuration management courtesy nconf. app.config has the same api as the nconf object.

The literal store is configured by default. If you want to use different stores you can easily attach them to the app.config instance.

// add the `env` store to the config
app.config.use('env');

// add the `file` store the the config
app.config.use('file', { file: 'path/to/config.json' });

// or using an alternate syntax
app.config.env().file({ file: 'path/to/config.json' });

// and removing stores
app.config.remove('literal');

app.log

flatiron.app will also load a log plugin during the init phase, which attaches a winston container to app.log. This logger is configured by combining the app.options.log property with the configuration retrieved from app.config.get('log').

Create An HTTP Server with flatiron.plugins.http(options):

This plugin adds http serving functionality to your flatiron app by attaching the following properties and methods:

Define Routes with app.router:

This is a director router configured to route http requests after the middlewares in app.http.before are applied. Example routes include:

// GET /
app.router.get('/', function () {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' });
  this.res.end('Hello world!\n');
});

// POST to /
app.router.post('/', function () {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' });
  this.res.write('Hey, you posted some cool data!\n');
  this.res.end(util.inspect(this.req.body, true, 2, true) + '\n');
});

// Parameterized routes
app.router.get('/sandwich/:type', function (type) {
  if (~['bacon', 'burger'].indexOf(type)) {
    this.res.writeHead(200, { 'Content-Type': 'text/plain' });
    this.res.end('Serving ' + type + ' sandwich!\n');
  }
  else {
    this.res.writeHead(404, { 'Content-Type': 'text/plain' });
    this.res.end('No such sandwich, sorry!\n');
  }
});

app.router can also route against regular expressions and more! To learn more about director's advanced functionality, visit director's project page.

Access The Server with app.server:

This is a union middleware kernel.

Modify the Server Options with app.http:

This object contains options that are passed to the union server, including app.http.before, app.http.after and app.http.headers.

These properties may be set by passing them through as options:

app.use(flatiron.plugins.http, {
  before: [],
  after: []
});

You can read more about these options on the union project page.

Start The Server with app.start(port, <host>, <callback(err)>)

This method will both call app.init (which will call any asynchronous initialization steps on loaded plugins) and start the http server with the given arguments. For example, the following will start your flatiron http server on port 8080:

app.start(8080);

Create a CLI Application with flatiron.plugins.cli(options)

This plugin turns your app into a cli application framework. For example, [jitsu] (https://github.com/nodejitsu/jitsu) uses flatiron and the cli plugin.

Valid options include:

{
  "argvOptions": {}, // A configuration hash passed to the cli argv parser.
  "usage": [ "foo", "bar" ], // A message to show for cli usage. Joins arrays with `\n`.
  "dir": require('path').join(__dirname, 'lib', 'commands'), // A directory with commands to lazy-load
  "notFoundUsage": false // Disable help messages when command not found
}

Add lazy-loaded CLI commands with options.dir and app.commands:

Flatiron CLI will automatically lazy-load modules defining commands in the directory specified by options.dir. For example:

// example2.js
var path = require('path'),
    flatiron = require('./lib/flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli, {
  dir: path.join(__dirname, 'cmds')
});

app.start();
// cmd/highfive.js
var highfive = module.exports = function highfive (person, cb) {
  this.log.info('High five to ' + person + '!');
  cb(null);
};

In the command, you expose a function of arguments and a callback. this is set to app, and the routing is taken care of automatically.

Here it is in action:

% node example2.js highfive Flatiron 
info:   High five to Flatiron!

You can also define these commands by adding them directly to app.commands yourself:

// example2b.js
var flatiron = require('./lib/flatiron'),
    app = flatiron.app;

var path = require('path'),
    flatiron = require('./lib/flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli);

app.commands.highfive = function (person, cb) {
  this.log.info('High five to ' + person + '!');
  cb(null);
};

app.start();
% node example2b.js highfive Flatiron 
info:   High five to Flatiron!

Callback will always be the last argument provided to a function assigned to command

app.commands.highfive = function (person, cb) {
  this.log.info('High five to ' + person + '!');
  console.log(arguments);
}
% node example2b.js highfive Flatiron lol haha
info:    High five to Flatiron!
{
  '0': 'Flatiron',
  '1': 'lol',
  '2': 'haha',
  '3': [Function]
}

Define Ad-Hoc Commands With app.cmd(path, handler):

This adds the cli routing path path to the app's CLI router, using the director route handler handler, aliasing app.router.on. cmd routes are defined the same way as http routes, except that it uses (a space) for a delimiter instead of /.

For example:

// example.js
var flatiron = require('./lib/flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli, {
  usage: [
    'usage: node test.js hello <person>',
    '',
    '  This will print "hello <person>"'
  ]
});

app.cmd('hello :person', function (person) {
  app.log.info('hello ' + person + '!');
});

app.start()

When you run this program correctly, it will say hello:

% node example.js hello person
info:   hello person!

If not, you get a friendly usage message:

% node test.js hello
help:   usage: node test.js hello <person>
help:
help:     This will print "hello <person>"

Check CLI Arguments with app.argv:

Once your app is started, app.argv will contain the optimist-parsed argv options hash, ready to go!

Here's an example:

// example3.js
var flatiron = require('./lib/flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli);

app.start();

app.log.info(JSON.stringify(app.argv));

This prints:

% node example3.js
info:    {"_":[], "$0": "node ./example3.js"}

Awesome!

Add a Default Help Command with options.usage:

When attaching the CLI plugin, just specify options.usage to get a friendly default message for when there aren't any matching routes:

// example4.js
var flatiron = require('./lib/flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli, {
  usage: [
    'Welcome to my app!',
    'Your command didn\'t do anything.',
    'This is expected.'
  ]
});

app.start();
% node example4.js 
help:   Welcome to my app!
help:   Your command didn't do anything.
help:   This is expected.

Start The Application with app.start(callback):

As seen in these examples, starting your app is as easy as app.start! this method takes a callback, which is called when an app.command completes. Here's a complete example demonstrating this behavior and how it integrates with options.usage:

// example5.js
var path = require('path'),
    flatiron = require('./lib/flatiron'),
    app = flatiron.app;

app.use(flatiron.plugins.cli, {
  usage: [
    '`node example5.js error`: Throws an error.',
    '`node example5.js friendly`: Does not throw an error.'
  ]
});

app.commands.error = function (cb) {
  cb(new Error('I\'m an error!'));
};

app.commands.friendly = function (cb) {
  cb(null);
}

app.start(function (err) {
  if (err) {
    app.log.error(err.message || 'You didn\'t call any commands!');
    app.log.warn('NOT OK.');
    return process.exit(1);
  }
  app.log.info('OK.');
});

Here's how our app behaves:

% node example5.js friendly
info:   OK.

% node example5.js error
error:  I'm an error!
warn:   NOT OK.

% node example5.js
help:   `node example2b.js error`: Throws an error.
help:   `node example2b.js friendly`: Does not throw an error.
error:  You didn't call any commands!
warn:   NOT OK.

Read More About Flatiron!

Articles

Sub-Projects

Tests

Tests are written in vows:

  $ npm test

License: MIT

plates's People

Contributors

3rd-eden avatar andreasmadsen avatar coderarity avatar cryptix avatar danhumphrey avatar dscape avatar dtrejo avatar flowonyx avatar forbeslindesay avatar heapwolf avatar indexzero avatar indutny avatar jfhbrook avatar marak avatar mmalecki avatar msabramo avatar mwawrusch avatar nhunzaker avatar pgte avatar pksunkara avatar raynos avatar robashton avatar smith avatar southern avatar tauren avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plates's Issues

Readme Needs updating

Would love to do it myself, but I am way to short on time the next couple of days.

Basically, it is Plates now, not Plate and you need to invoke it either

tmpl = new Plates("<......")

or

tmpl = new Plates ("<......", { "name" : "Frank" } )

or

tmpl = new Plates ("<......", { "name2" : "Frank" }, {"name2" : "name"} ) // mapping might be the other way around

and you can set data and html explicitly as well with data(...), html(..)

to get the result you simply call tmpl.bind()

To use it with jquery and embedded in html (for example to preprocess the html with haml, jade do something like this):

%script{:id=>'customers_template', :type=>'text/x-plates-tmpl'}
%ul{:'data-bind'=>'customers'}

and load the template with $('#customers_template').text()

Trim all whitespace from tests

Just a small request. Vows in the terminal is an absolute pain to troubleshoot when there's a ton of whitespace/newline characters in the output. Would it be too much trouble in the future to strip all all whitespace from these elements?

Having troubles with the API,docs

I am using plate.js in the browser.

If I use it the way described in the readme (changing Plate to Plates) it does not work, so I ended up with the following:

var plate = new Plates('

  • ', {
    'name': 'frank'
    });
    console.log(plate.bind());

    (gist here: https://gist.github.com/7b32c71c681e7c7a048a)

    which blows on line 189 (within the reduce function, as it tried to access the treeMap[...] [d] where treeMap[...] is undefined

    Not sure if I am using this wrong or there is a bug, but I need help with this.

    Multiple list binding problem

    I am trying to bind an array of names to the entries in list A, but not to list B.

    <div>
      <ul id="A">
        <li data-bind="names"></li>
      </ul>
      <ul id="B">
        <li></li>
      </ul>
    </div>
    var data = {names: ['woody allen', 'wu tang clan']};
    map.where('data-bind').is('names').to('names');
    plates.bind(html, data, map);

    Expected output:

    <div>
      <ul id="A">
        <li data-bind="names">woody allen</li>
        <li data-bind="names">wu tang clan</li>
      </ul>
      <ul id="B">
        <li></li>
      </ul>
    </div>

    Actual output:

    <div>
      <ul id="A">
        <li data-bind="names">woody allen</li>
      </ul> 
      <ul id="B">
        <li></li>
        <li data-bind="names">wu tang clan</li>
      </ul> 
      <ul id="B">
        <li></li>
      </ul>
      <ul id="B">
        <li></li>
      </ul>
    </div>

    If I remove list B, the data gets bound to list A properly. Any thoughts on what is happening?

    Thanks

    Exclude attribute or element if data value is falsey

    Description

    I think there needs to be a way exclude tags and attributes from the generated HTML if the data value it is mapped to is false.

    For instance, if you want to include checked="checked" from a checkbox input based on if a data value done is true or false. This is currently not possible in Plates (if it is, please show me how!).

    In addition, it would be really nice to exclude an entire HTML tag and its children if a data value is false.

    Possible Solution

    I've changed this line:

    https://github.com/tauren/plates/blob/master/lib/plates.js#L122

    To this:

     return newdata ? key + '="' + (newdata || '') + '"' : '';
    

    And it excludes the checked attribute for my simple situation, but I'm concerned it would break other scenarios. Would appreciate any feedback.

    Note that I'm unable to push changes to my Plates fork right now (I'm behind a restrictive firewall), but I can push this change tonight and submit a pull request.

    Further Details

    Consider a typical Todo app. Next to each item in the Todo list is a checkmark to indicate that the item is done. The todo list data might look like this:

    var todos = [
      {content:'item one',done:false},
      {content:'item two',done:true)
    ];
    

    The template to render might look something like this:

     <script type="text/x-plates-tmpl" id="todo">
       <div class="todo">
         <div class="display">
           <input class="check" type="checkbox" />
           <div class="todo-content"></div>
           <span class="todo-destroy"></span>
         </div>
         <div class="edit">
           <input class="todo-input" type="text" value="" />
         </div>
       </div>
    </script>
    

    The code to render the template would be similar to this:

    var html = $('script#todo').text();
    
    var data = {
      content: 'Todo item',
      done: false
    };
    
    var map = Plates.Map();
    map.class('todo-content').to('content');
    map.class('check').use('done').as('checked');
    
    console.log(Plates.bind(html, data, map));
    

    First of all, the checked attribute doesn't get added if it isn't already in the template. This means the template currently needs to include <input class="check" type="checkbox" checked="checked" />. It looks like this pull request might fix this:
    #21

    Secondly, there is currently no way to set an attribute value to anything besides a value in the data. So if done is true, then checked="true" would be generated instead of the checked="checked" that I want. I would prefer to not add extra redundant data to my model simply for the templating solution to work.

    Lastly, and most importantly, I don't see a way to enable/disable an attribute based on a data value. If done is false, I don't want any checked attribute to appear.

    I posted some of this into a gist:
    https://gist.github.com/1784972

    Does not overwrite "placeholder" values

    While writing a tool, I had the following template:

    <h2><span class="logo">DNS<small>lookup</small></span> HAS LOCATED THE SUSPECT'S IP ADDRESS.</h2>
    
    <h2><strong>IP Address For <span id="domain">domain</span>:</strong> <span id="ipaddress">127.0.0.1</span></h2>

    with the "plates" bind step looking like:

    res.end( (new Plate(templates['caught'], {
      "ipaddress": ip,
      "domain": domain
    })).bind() );

    The output looks like this:

    <h2><span class="logo">DNS<small>lookup</small></span> HAS LOCATED THE SUSPECT'S IP ADDRESS.</h2>
    <h2><strong>IP Address For <span id="domain">this.isadomain.comdomain</span>:</strong> <span id="ipaddress">192.168.0.1127.0.0.1</span></h2>

    In other words, it simply concats the new value to the beginning of the existing values instead of overwriting it.

    Usage explanations, examples

    The usage of Plates.Map is explained well and easy to understand. But I'm actually still not sure what the standard behavior of plates is (simple usage).

    # Usage
    
    ## Simple case
    By default, `plates` will try to match the `data-key` in the data to an `ID` in the tag, since both should are uniqe.
    

    // typo at uniqe

    ```js
    var Plates = require('plates');
    
    var html = '<div id="test">Old Value</div>';
    var data = { "test": "New Value" };
    
    var output = Plates.bind(html, data); 
    ```
    

    Reading the tests, plates can do a lot more without the map. What exactly?
    I'm especially out to omit map.class("barfoo").to("foo"), as it occurs a lot and shows errors in my IDE.

    support css selectors

    instead of this:

    var html = '<span class="name">User</span>...<span class="name">User</span>'
    var data = { "username": "John Smith" }
    var map = Plates.Map()
    map.class('name').to('username')
    console.log(Plates.bind(html, data, map))
    

    i want to do this:

    var html = '<span class="name">User</span>...<span id="name">User</span>'
    var data = { ".username": "John Smith", "#username": "Bob Hope" }
    console.log(Plates.bind(html, data, map))
    

    class is reserved

    Plates uses the reserved word class as a method of Plates.Map. This works but it causes linters to throw warnings. For instance, JSHint:

    Expected an identifier and instead saw 'class' (a reserved word). "this.mapping.class('first').to('firstName');"
    

    Perhaps it would be better to rename the method? For instance:

    Plates.Map.clazz
    Plates.Map.className
    

    There is an issue in JSHint already:

    jshint/jshint#232

    README's "Simple Example" is not working. Is Plates broke, or is this a bug?

    With a completely fresh project, running "npm install plates", and then running the example code of..

    var Plates = require('plates');
    
    var html = '<div id="test">Old Value</div>';
    var data = { "test": "New Value" };
    
    var output = Plates.bind(html, data);
    

    The following error is given:

    node.js:134
            throw e; // process.nextTick error, or 'error' event on first tick
            ^
    TypeError: Object #<Object> has no method 'bind'
        at Object.<anonymous> (/mnt/ws/users/leeolayvar/136705/test.js:6:21)
        at Module._compile (module.js:411:26)
        at Object..js (module.js:417:10)
        at Module.load (module.js:343:31)
        at Function._load (module.js:302:12)
        at Array.<anonymous> (module.js:430:10)
        at EventEmitter._tickCallback (node.js:126:26)
    

    Thoughts? Note that Plates also does not have the Map attribute either.

    whitespace after =

    This doesn't substitute names correctly because of the whitespace after class=.

    var plates = require('plates');
    
    var html = '<div class= "name"></div>';
    var collection = [
      {'name': 'Louis'}
      , {'name': 'Andy Kindler'}
      , {'name': 'Greg Giraldo'}
    ];
    console.log(plates.bind(html, collection));

    Bug parsing attributes with > character

    For example, binding a page with the following causes mangled html because the > character is interpreted as ending the tag:

    <span data-bind="visible: foobar > 1"></span>
    

    This is rather inconvenient in conjunction with knockout. Note that this

    Another nested object problem

    When dealing with a nested object, plates seems to lose track of something if the template uses higher-level data after dealing with the sub-object. I've created a test case, and a gist to illustrate: https://gist.github.com/2370943

    As you'll see, plates seems to be outputting the unprocessed template snippet, then the copy with substitutions made.

    Adding Plates 0.4.5 to npm

    Hi,

    Just wondering if it would be possible to get the latest version of Plates (with collection support) to be added to npm.

    Just as this will make things a lil easier for may when i am deploying my stuff to no.de via git push.

    Cheers!

    No collections?

    With weld, you can do something like:

    <ul class="employees">
      <li><strong class="name">John Doe</strong>: <span class="title">Salaryman</span></li>
    </ul>
    {
      "employees": [
        { "name": "Josh", "title": "engineer" },
        { "name": "Paolo", "title": "CTO" }
      ]
    }

    and have it populate the whole list:

    <ul class="employees">
      <li><strong class="name">Josh</strong>: <span class="title">engineer</span></li>
      <li><strong class="name">Paolo</strong>: <span class="title">CTO</span></li>
    </ul>

    but it seems that Plates can't do this? For example, check out this gist:

    https://gist.github.com/1707961

    When you run this, the list gets stringified and instead of what you want to see, you get [object Object],[object Object] and such.

    It's certainly possible to get around this with multiple applications of plates, as in this gist:

    https://gist.github.com/1708001

    but it feels wrong.

    So, the way I see it, this was either an intentional decision that makes Plates' complexity manageable, or it's a bug, and I'm not sure which.

    jsdocs?

    There is jsdoc-like markup embedded inside the markdown file, but not there in the plates.js file.

    Why not?

    Issues Mapping with class attributes...

    I'm trying to insert a new class into the class attribute of a tag, but the Mapping behavior doesn't quite run as expected:

    I'm using:
    map.class('home').insert('selected');

    (with data: {selected:"selected"})

    and this happens: <div class="home"></div> becomes <div class="selected"></div>

    As soon as any other class is mixed in, it fails:
    <div class="something home"></div> is skipped

    Ideally, I'd expect an "insert" action to actually insert data, instead of replacing it:
    <div class="someclass home"></div> would become <div class="someclass home selected"></div>

    Any suggestions?

    On a side note, this works the same as above:
    map.where('class').is('home').insert('selected');

    Perhaps a 'has' conditional would be appropriate?
    map.where('class').has('home').insert('selected');

    resolve . notation

    I stumbled upon the following scenario:

    { "name": "frank",
    "creator" : {
    userName : "user x",
    url: "http://...."
    }}

    And I would like to do the following:

  • ......

    resulting in

  • ...user x ....
  • Class mapping fails for unquoted class attribute

    It seems to me class mapping fails for unquoted class attributes.

    html5 does not require quoted attributes. We're in html5 now.

    Here's a printout demonstrating the issue:

    function testPlates() {
        console.log(arguments.callee.toString())
        map.class('appName').to('title')
        var o = { title: 'Boss' }
        console.log('1', plates.bind('<div class=appName></div>', o, map))
        console.log('2', plates.bind('<div class="appName"></div>', o, map))
    }
    1 <div class=appName></div>
    2 <div class="appName">Boss</div>

    case 2 works as expected
    BUG: case 1 was expected to have Boss inserted into the tags body

    1 <div class=appName>Boss</div>

    Mixing conventions + config

    In most libraries where conventions are used, they're used as a default, on which further config can be placed.

    Either I'm missing something or this is not the case in Plates, which is mildly annoying because it means for cases like

    var model = {} // Some really cool model
    var map = Plates.Map()
                           .where('rel').is('self').use('id').as('href')
    var html = Plates.bind(template, model, map)
    

    Will result in only the rel being mapped

    Where as with the code previously

    var model = {} // Some really cool model
    var html = Plates.bind(template, model)
    

    This worked fine and all the data got mapped by class beautifully.

    I'm happy to make this change if it is agreed with.

    Feature-request: support data-bind-attr without verbose mapping

    The wiki docs sugest using data-bind="name" or data-bind-href="url" for clean templating, which I totally agree with ( https://github.com/flatiron/plates/wiki/Using-data-bind-for-Clean-Plating).

    However, right now it looks like to support that, you have to manually write your map for every data item -

    map.where('data-bind-href').is('url').use('url').as('href')

    It seems like either the default behavior should support the best practice, or there should be a simple way to hand the map a regex and a function such that data-bind-attr="key" will always bind attr="data[key]". If we could agree on the best approach I would be glad to take a shot at implementing.

    Feature request: .to(function (obj) { ... })

    It would be nice to provide some ViewModel-like mapping logic when doing an explicit map to .to. For example:

      var data = { list: [1, 2, 3, 4, 5] }
    
      var html = '<div class="length-of-list"></div>';
    
      var map = Plates.Map()
        .class('length-of-list').to(function (data) {
          return data.list.length;
        });
    
      console.log(Plates.bind(html, data, map));

    Outputs:

      '<div class="length-of-list">5</div>'

    This would save the extra step of having to create a separate object containing the length property.

    Use dom on client side

    Writing a mini-parser for server-side usage was a huge win, but I suspect that a native dom would be faster on the client.

    Attribute Binding

    I agree 100% with this approach, here is a suggestion for binding attributes in a clean intuitive manner...

    1. Injecting attributes, you could use something like this:

    { 'url':'https://github.com/', title:'github' }

    clientside global namespace pollution

    ;var Plates = (typeof process !== 'undefined' && ~process.title.indexOf('node')) ? exports : {};
    

    This checks if running in node and if yes, uses exports else it just creates a new empty object on the global namespace called Plates.
    Well I don't want this global variable, and I'm using a custom require() function to use node code on the client side. Libraries are bundled into one file, including Plates, which does not export, as process is not defined.

    Maybe you could just check for exports and or require to be defined and if yes use it, I don't want to create a process shim.

    problem with nested objects

    Hi there

    I like the concept of client side template rendering and started playing with your promising lib.
    Unfortunately I get the following behavior when using this code snippet:

    https://gist.github.com/2025054

    Any comment on this? Am I doing something wrong?

    Thx in advance. cheers
    Cédric

    Failing test, appears legit

    when providing both data and markup for simple template
      ✗ should merge data to markup
        » expected "<div id='a.b'>ok</div>\n",
    got   "<div id='a.b'></div>\n" (==) // common.js:32
    

    This is after getting other tests of the same form running fine.

    Not a npm module

    When I run this code:

    npm install plates

    It give the error: plates' is not in the npm registry.

    Tags with two template remplacements fail.

    <input type="text" data-bind="name" data-bind-placeholder="placeholder"></input>

    Expected output ==>

    <input type="text" data-bind="name" data-bind-placeholder="placeholder" 
       value="Frank" placeholder="Some dude"></input>
    data = { "name" : "Frank", "placeholder" : "Some dude" };
    
    map = {"name" : ["data-bind", "value"] ,"placeholder" : ["data-bind-placeholder", "placeholder"] };

    It seems that this might have something to do with the attr regext somewhere around line 100.

    Feature Request: Bind/Map to Tags

    I'd like to request the ability to bind and/or map onto tags themselves.

    A simple example would be to bind additional <link> or <script> tags into the body of the <head> element.

    Note that i am aware, in this example, that you could give <head> an id, or a class, but that just seems silly. I think it would be a perfectly reasonable use case to be able to insert data into the bodies of elements, especially for single element types such as head or body.

    Some additional thoughts..

    • Prefix, Suffix, or Replace insertion would be nice. So you could insert the data into the beginning or end of the element, or replace it's content all together.
    • Multiple Matches. When dealing with tags such as <head> or <body> you'll often run into only one, but if you try and bind data to <li>, you'll likely match many more. With that said, you should be able to resolve which match(s) you would like to bind the data to.. or all of them?

    Partials / Nested Templates

    On the server side you still need JSDOM to implement partials, since partials are needed in most real-world cases, they should probably be part of the server side light weight DOM implementation.

    I have been working on something very similar to plates for some time and have some suggestions on how this can be done without any special template syntax. I'm on #nodejitsu IRC if you want to discuss.

    Thanks,

    Mike

    Example of arrays?

    Seems like a pretty common use-case where you might want to bind an array to a table or list (like a .forEach scenario), but I don't see how that would work in Plates; is there such a concept?

    Attribute substitution for iterative object

    I can't find documentation or a test for this anywhere.

    I am trying to substitute a value of an object in an array as the value of an attribute(href). Not sure if it's not supported or if I just can't find how to map it

    I've tried variations along the lines of:
    map.where('href').has('id').insert('items.id')

    for the html/json in this gist:
    https://gist.github.com/2467056

    Defensive semicolon

    Apologies in advance for nit-picking, but is there a specific scenario where the defensive semicolon at the beginning of plates.js is necessary?

    ;var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefined') ? exports : {};
    

    My understanding is a defensive defensive semicolon is needed before a line that starts with an open parenthesis, such as an immediately executed anonymous function:

    ;(function() {})(); 
    

    But I don't see why it would be needed before var. I'm asking because currently there is no way for plates.js to pass jshint without removing the semicolon. I requested a jshint option to be more lenient on defensive semicolons, but @antonkovalyov didn't see the reasoning either and it was understandably rejected:
    jshint/jshint#487

    If there is a good reason for it, let me know, and I'll reopen the jshint issue.

    ...use('key').as('tag') not working when multiple mappings

    var plates = require('plates')

    var data = {
    first : 'john',
    last : 'smith'
    }

    var map = plates.Map();
    map.where('name').is('first').use('first').as('value');
    map.where('name').is('last').use('last').as('value');

    var html = '';

    console.log(plates.bind(html, data, map))

    OUTPUT:

    EXPECTED:

    add possibility to transform/map binded values

    How about adding support for calling functions on binded values ?

    I can think of having the "with" method as in:

    map.class('snippet').with(toUpper).to('val');

    or

    map.class('snippet').with(function(val) { val == null ? 'i'm not here' : val}).to('val');

    see bae1fae for an example implementation

    Support for IE7/IE8

    I just attempted to run my app that uses Plates in IE8 and it blew up. I noticed pull request #53 makes plates more compatible with ES3, so I thought Plates might work in IE8.

    Is there already support for IE7/IE8? If not, is it something that is planned? If there is support, what shims are required to make it work? My application needs to support IE7+, and I'm trying to determine how much effort will be involved to use Plates.

    I'm already using this style of accessing reserved words:

    this.mapping['class']('check').use('done').as('checked');
    

    Instead of this:

    this.mapping.class.('check').use('done').as('checked');
    

    Feature: Iteration

    A common feature in template engines is the ability to iterate through an array to generate multiple lines eg. multiple script tags based on an array with filenames.

    This is missing in plates.

    Settle on a mime type for the templates when embedded in html as a script

    It is common practice to embed templates in html as script like so :

    <script id="foo" type="text/x-plates-tmpl"> Here goes all your template text..... The plates team should decide on a mime type (text/x-plates-tmpl or text/x-plates comes to mind) and put that, including the example from issue 7) on the readme. Make it simple for casual users to start without having to think.

    Please help me simplify this use case.

    Here is an example from my code which works with the current version of plates. I am not happy with the complexity. How can this be simplified?

    A couple of notes: I tried to map the creator object using . notation, but that did not work, so I had to resort to creating a custom view object to avoid polluting the data object.
    I tried to use class names like data-bind-name and map them to name, that would be a less intrusive version than what we have now. The reason I cannot use a 1 to 1 match (class name to name data property) is because it gets unmaintainable and undebuggable given the amount of css present. With a common name prefix I can do a text search over data-bind and find all the instances.

    Another problem was the a tag that contains two elements to be mapped, hence the data-bind-href and data-bind.

    I am reallz open for any suggestion how this could be simplified. In a perfect world I want to have a clean html and the least amount of code.

     {
     "scottyId" : "23423445546456",
     "name" : "Frank",
     "creator" : {
     "avatarUrl30" : "http:// ...."
      }
    }
    <li>
    <p class='left'>
    <a data-bind-href='linkToScotty' data-bind='name'></a>
    </p>
    <img class='tiny_person right' data-bind='creator_avatarUrl30' >
    <div class='clearfix'></div>
    </li>
      _render_scotty_ref: function(scottyRef) {
        var html, mappedData, mapping;
        html = '... see above ...';
        mappedData = {
          name: scottyRef.name,
          creator_avatarUrl30: scottyRef.creator.avatarUrl30,
          linkToScotty: window.routingController.getDesignScottyPath(scottyRef.scottyId)
        };
        mapping = {
          'name': ['data-bind'],
          'creator_avatarUrl30': ['data-bind', 'src'],
          'linkToScotty': ['data-bind-href', 'href']
        };
        return Plates.bind(html, mappedData, mapping); 
      }

    Short update: This actually does work except for the linkToScotty binding, which is not replacing my href. Which is why I am doing this temporarily:

    var $res;
    $res = $(Plates.bind(html, mappedData, mapping));
    $res.find("[data-bind-href]").each(function(idx, item) {
      $(item).attr("href", $(item).attr("data-bind-href"));
    });
    return $res;

    Add to existing attribute based on data value

    Plates needs a way to enhance an attribute based on a data value, not just replace it with the data value.

    For instance, in my Todo app, if a todo item is checked as done, I want to add a CSS class to the div, but I don't want to remove the existing class.

    HTML of an uncompleted item {content: 'My Todo Item', done: false}:

    <div class="todo-content">My Todo Item</div>
    

    HTML of a completed item {content: 'My Todo Item', done: true}:

    <div class="todo-content done">My Todo Item</div>
    

    I'd be happy to implement this and submit a pull request, but welcome any input or suggestions before doing so. Is there already a way to accomplish this?

    It seems like there needs to be a way to supply a Map with custom functions so we can better control the output. For instance:

    // Function to pass into Plates.Map.use()
    // It should be passed the existing attribute value and the data model
    function addClass(currentAttr, data) {
      return currentAttr.indexOf('done') === -1 ? currentAttr + ' done' : currentAttr;
    }
    
    var map = Plates.Map();
    map.class('todo-content').to('content');
    map.class('todo-content').use(addClass).as('class');
    

    The above will need more work since the use() method does not yet know which attribute it pertains to, since as('class') is chained after it. But I hope it gets the concept across.

    Input type="password" matching model.password with no mapping

    Given the following template:

    <script type="text/x-plates-tmpl" id="testTmpl">
      <input type="text" id="x_username" placeholder="Enter username..."/>
      <br/>
      <input type="password" id="x_password" placeholder="Enter password..."/>
    </script>
    <div id="output"></div>

    And the following code:

    var user = {
      name: 'Sally Smith',
      username: 'sally',
      password: 'mypass'
    };
    
    var html = $('script#testTmpl').text();
    var map = Plates.Map();
    $('#output').html(Plates.bind(html, user, map));


    This doesn't render properly. The value mypass is rendered after the input. The template should be rendered exactly as is without any variable replacements.

    See in action here:
    http://jsfiddle.net/tauren/HRDaD/

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.