Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

support data-bind-attr without verbose mapping #58

Open
wants to merge 2 commits into from

4 participants

@autoric

Updated the default behavior of plates so that:

*Data is bound to a tag by using data-bind="key"; id or any other attribute will not cause data to be bound
*Any attribute can be set using data-bind-attr="key"
*Attribute creation is on

No custom maps need to be written to achieve this behavior. Writing any mapping will override this behavior with your manual bindings.

html:

<a data-bind-href="url" data-bind="title"></a>

js:

var data = {
        url:'http://www.google.com',
        title:'This is a link to google'
    }

Plates.bind(html, data)
}

output:

<a href="http://www.google.com" data-bind-href="url" data-bind="title">This is a link to google</a>
@autoric

Didn't realize the pull request would open a new issue.... I'm moving my comments from the previous feature request & deleting it to prevent redundancy -

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.

@autoric

To expand on this -

I've been trying to think of a way to execute this cleanly. One path I was considering is extending the Map prototype to include something along the lines of .match(regex) and .execute(callback). However that probably just moves down the road of offloading the work onto each user, and sort of defeats the purpose.

Instead, I want to try extending the default functionality of plates (ie where there are no mappings), to achieve the desired functionality. This still leaves users with the option of overriding with explicit maps but makes the standard behavior more robust.

Does this make sense; does anyone else felt like this would make plates more usable & accessible?

@tauren

I agree with adding data-bind features to plates. I'm already using data-bind instead of id or class, and it is tedious to have to manually create all of the mappings.

Also, I absolutely think that plates should accept functions as well as strings for parameters in the mappings. I discuss this some in this issue:
#29

@autoric

Thanks for the input, tauren. I actually have been thinking recently that plates might be improved by dropping maps & mappings altogether. In my experience the most useful templating engines are very simple with a small set of rules (thinking of mustache js for instance).

I'll outline my thoughts, would love to hear feedback -

Binding:

Plates parses html and binds based exclusively by the following two attributes in html tags. These accept certain types of data values and behave differently depending on what that type is (explained below):

  • data-bind="[key]" - accepts data types of string, array, object, boolean, function. Maps the inner html of the current tag to the value in data[key]
  • data-bind-[attr]="[key]" - accepts data types of string, boolean, function. Replaces the value of attr or adds attr to the tag, with the value of data[key]

Data value types

  • String - simple value replacement, exactly as plates works right now
  • Array - iterate over the array, same as plates works now
  • Object - iterate over the object and all subelements operate in the context of the object, same as plates works now
  • Boolean - a boolean true does nothing, a boolean false removes the attribute (data-bind-[attr]), or the html tag and all child elements (data-bind). Essentially gives you conditionals in your templating.
  • Function - A callback function. I have thought about this the least, but possibly gets the arguments of regex matching, and should return any of these other valid types. This should allow you to append values, in the case of wanting to add a class, or simply append text to the end of div.

If this is unclear or there is interest, I could write some code mockups clarifying the idea. In this case, I think maps can safely be dropped, and plates would provide an easy to understand & very powerful set of rules, all entirely html5 compliant and avoiding the DSL it set out to dodge.

@hij1nx

looking at this now.

@msull92

Already using these commits in my code! Thank you!!

@msull92

With this, is it possible to access nested JSON data?

@hij1nx

There were so many whitespace/indent changes in this commit it was kind of hard to read, also, its been a while so its out of sync with HEAD, can you update this so i can merge it in?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 85 additions and 28 deletions.
  1. +85 −28 lib/plates.js
View
113 lib/plates.js
@@ -25,6 +25,12 @@ var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefi
attr: /([\-\w]*)=(?:["\']([\-\.\w\s\/:;&#]*)["\'])/gi,
+ defaults: {
+ where: 'data-bind',
+ reGlobal: /data-bind-([\-\w]*?)=(["\'])(.{0,}?)\2/gi,
+ re: /data-bind-([\-\w]*?)=(["\'])(.{0,}?)\2/i
+ },
+
hasClass: function(str, className) {
return str.split(' ').indexOf(className) > -1;
},
@@ -197,10 +203,10 @@ var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefi
else if (!mappings[ii].replace && mappings[ii].attribute === key) {
if (
- mappings[ii].value === value ||
- that.hasClass(value, mappings[ii].value ||
+ mappings[ii].value === value ||
+ that.hasClass(value, mappings[ii].value ||
mappings.conf.where === key) ||
- ( ({}).toString.call(mappings[ii].value) === '[object RegExp]' &&
+ ( ({}).toString.call(mappings[ii].value) === '[object RegExp]' &&
mappings[ii].value.exec(value) !== null) ) {
var v = data[mappings[ii].dataKey];
@@ -213,7 +219,7 @@ var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefi
// If the item is an array, then we need to tell
// Plates that we're dealing with nests
that.nest.push(tagname);
- }
+ }
else if (typeof v === 'object') {
newdata = tagbody + that.iterate(html, v, components, tagname, value);
@@ -251,28 +257,79 @@ var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefi
}
else {
- //
- // if there is no map, we are just looking to match
- // the specified id to a data key in the data object.
- //
- tagbody.replace(
- this.attr,
- function (attr, key, value, idx) {
- if (key === map && map.conf.where || 'id' && data[value]) {
-
- var v = data[value],
- nest = isArray(v),
- output = (nest || typeof v === 'object') ? that.iterate(html, v, components, tagname, value) : v;
-
- // If the item is an array, then we need to tell
- // Plates that we're dealing with nests
- if (nest) { that.nest.push(tagname); }
-
- buffer += nest ? output : tagbody + output;
- matchmode = true;
- }
+ var defaults = this.defaults;
+
+ var dataBinds = [];
+ var matches = tagbody.match(defaults.reGlobal);
+ for(var ii in matches) {
+ var match = matches[ii].match(defaults.re);
+ var replace = match[1];
+ var dataKey = match[3];
+ var regex = new RegExp('^'+replace+'=(?:\\w+|["|\'](?:.*)["|\'])', 'i');
+ dataBinds.push({
+ replace: replace,
+ dataKey: dataKey,
+ re: regex,
+ setAttribute: false
+ });
+ }
+
+ for(var ii in dataBinds) {
+ var bind = dataBinds[ii];
+
+ tagbody = tagbody.replace(this.attr, function(str, key, value, a) {
+
+ if(str.match(bind.re)) {
+ bind.setAttribute=true;
+ return key+'="'+data[bind.dataKey]+'"';
+ }
+ return str;
+ });
+
+ if(!bind.setAttribute) {
+ var spliced = isSelfClosing? 2 : 1;
+ var close = isSelfClosing? '/>': '>';
+ var left = tagbody.substr(0, tagbody.length - spliced);
+ if (left[left.length - 1] == ' ') {
+ left = left.substr(0, left.length - 1);
+ if (isSelfClosing) {
+ close = ' ' + close;
+ }
+ }
+ tagbody = [
+ left,
+ ' ',
+ bind.replace,
+ '="',
+ data[bind.dataKey],
+ '"',
+ close
+ ].join('');
+ }
}
- );
+
+ //
+ // if there is no map, we are just looking to match
+ // the specified id to a data key in the data object.
+ //
+ tagbody.replace(
+ this.attr,
+ function (attr, key, value, idx) {
+ if (key === map && map.conf.where || key === defaults.where) {
+
+ var v = data[value],
+ nest = isArray(v),
+ output = (nest || typeof v === 'object') ? that.iterate(html, v, components, tagname, value) : v;
+
+ // If the item is an array, then we need to tell
+ // Plates that we're dealing with nests
+ if (nest) { that.nest.push(tagname); }
+
+ buffer += nest ? output : tagbody + output;
+ matchmode = true;
+ }
+ }
+ );
}
}
@@ -310,7 +367,7 @@ var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefi
};
function last(newitem) {
-
+
if (newitem) {
this.mappings.push({});
@@ -319,9 +376,9 @@ var Plates = (typeof process !== 'undefined' && typeof process.title !== 'undefi
if (m && m.attribute && m.value && m.dataKey && m.replace) {
m.re = new RegExp(m.attribute + '=([\'"]?)' + m.value + '\\1');
-
+
} else {
-
+
delete m && m.re;
}
return m;
Something went wrong with that request. Please try again.