Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added support for data-linking to textarea elements (#2)

Improved sample for form elements, and included textarea example.
http://borismoore.github.com/jsviews/demos/step-by-step/07_form-elements.html
Integrated updated design for #index, from JsRender.
Signed-off-by: Boris Moore <borismoore@gmail.com>
  • Loading branch information...
commit 89354dd80870216500a55fb4b9ab00b5d6225182 1 parent 93c0ff2
@BorisMoore authored
View
2  README.md
@@ -1,5 +1,5 @@
## JsViews: Next-generation jQuery Templates
-_Interactive data-driven views, built on top of JsRender templates_<br/>
+_Interactive data-driven views, built on top of [JsRender templates](https://github.com/BorisMoore/jsrender)_<br/>
To view demo pages (on gh-branch) navigate to [http://borismoore.github.com/jsviews/demos/index.html](http://borismoore.github.com/jsviews/demos/index.html "JsViews Samples").<br/>
See also [JsRender step-by-step samples](http://borismoore.github.com/jsrender/demos/index.html).<br/>
View
1  demos/index.html
@@ -31,6 +31,7 @@
<div class="box">
<div><a href="http://www.slideshare.net/BorisMoore/jsviews-next-generation-jquery-templates" >Slide deck: jQuery Conference October 2011</a></div>
<div><a href="http://msdn.microsoft.com/en-us/magazine/hh882454.aspx" >MSDN 'Client Insight' article on JsRender</a></div>
+ <div>Training course: <a href="http://johnpapa.net/new-course-on-jsrender-templating-fundamentals-with-javascript" >JsRender Fundamentals</a> from John Papa on <a href="http://pluralsight.net/" >Pluralsight</a> (3 hours of video)</div>
</div>
</body>
View
3  demos/resources/demos.css
@@ -7,7 +7,7 @@ body > button { float: left; clear: right; margin: 3px }
a { color: #55b; }
pre { font-size:10pt; font-weight:bold; }
.inset { padding-left: 18px; }
-.box { border: 1px solid #777; padding: 5px; margin: 5px 0 30px; }
+.box { border: 1px solid #777; padding: 10px; margin: 5px 0 30px; }
.box div { margin: 3px 0 10px 0; }
.box .label { margin: 0; padding: 10px 0 0 0; font-style:italic; color: #55b; }
.box.label { font-style:italic; color: #55b; }
@@ -15,3 +15,4 @@ pre { font-size:10pt; font-weight:bold; }
pre { border-left: 3px solid #aaa; padding:10px; margin-bottom: 30px; }
.indexitems { list-style-type:none; padding-left:10px; margin: 0 0 20px;}
h3 { margin-bottom: 10px; font-size: 11pt;}
+select { margin-bottom:15px; width: 200px; }
View
61 demos/step-by-step/07_form-elements.html
@@ -16,21 +16,43 @@
<div id="moviePrices"></div>
<script id="moviePriceTemplate" type="text/x-jsrender">
- <div class="subhead">Choose a movie</div>
+ <h4>Purchase a movie ticket</h4>
- <select id="selTheater" data-link="selectedMovie">
- <option value="-1">Choose...</option>
- {{for movies}}
- <option value="{{:#index}}">{{:title}}</option>
- {{/for}}
- </select>
+ <div class="box">
+ <div class="subhead">Choose a movie</div>
- <div data-link="visible{:selectedMovie > -1}">
- <p>
- <b>Ticket Price:</b> <span data-link="convertedPrice(selectedMovie, currency)" />
- </p>
+ <select id="selTheater" data-link="selectedMovie">
+ <option value="none">Choose...</option>
+ {{for movies}}
+ <option value="{{:#index}}">{{:title}}</option>
+ {{/for}}
+ </select>
- <p>Currency Picker: <input type="checkbox" data-link="chooseCurrency" /></p>
+ <div data-link="visible{: selectedMovie!=='none' }">
+
+ <div class="subhead">Name:</div>
+ <div><input data-link="name" /></div>
+
+ <div class="subhead">Special requests:</div>
+ <div><textarea data-link="request" rows="4" cols="25" /></div>
+
+ </div>
+
+ </div>
+
+ <div data-link="visible{: ~and(selectedMovie!=='none', name) }">
+
+ <h3>Ticket order</h3>
+
+ <div class="box" data-link="visible{:name}">
+ <div>Ticket for <em data-link="name" /> to movie: <em data-link="~selectedTitle(selectedMovie)" /></div>
+ <div data-link="visible{:request}"><em>Your special request:</em> <pre data-link="request" ></pre></div>
+ <div>
+ <b>Ticket Price:</b> <span data-link="convertedPrice(selectedMovie, currency)" />
+ </div>
+ </div>
+
+ <div>Currency Picker: <input type="checkbox" data-link="chooseCurrency" /></div>
<div class="box" data-link="visible{:chooseCurrency}">
<div class="subhead">Choose Currency:</div>
@@ -38,6 +60,7 @@
<input type="radio" name="currencyPicker" value="EUR" data-link="currency" />Euro<br/>
<input type="radio" name="currencyPicker" value="GBP" data-link="currency" />Pound<br/>
</div>
+
</div>
</script>
@@ -49,7 +72,8 @@
};
var model = {
- selectedMovie: -1,
+ name: "",
+ selectedMovie: "none",
currency: "US",
convertedPrice: function(index, curCode) {
var currency = currencies[curCode];
@@ -72,8 +96,17 @@
}
]
};
+ contextHelpers = {
+ currencies: currencies,
+ selectedTitle: function(selectedMovie) {
+ return (selectedMovie!=="none") ? model.movies[selectedMovie].title : "";
+ },
+ and: function(a, b) {
+ return !!a && !!b;
+ }
+ }
- $("#moviePriceTemplate").link( model, "#moviePrices", {currencies: currencies} );
+ $("#moviePriceTemplate").link( model, "#moviePrices", contextHelpers );
</script>
</body>
</html>
View
38 jquery.views.js
@@ -7,7 +7,7 @@
* Copyright 2012, Boris Moore
* Released under the MIT License.
*/
-// informal pre beta commit counter: 7
+// informal pre beta commit counter: 8
this.jQuery && jQuery.link || (function( global, undefined ) {
// global is the this object, which is window when running in the usual browser environment.
@@ -37,6 +37,7 @@ var versionNumber = "v1.0pre",
html: "html",
text: "text"
},
+ valueBinding = { from: { fromAttr: "value" }, to: { toAttr: "value" }};
oldCleanData = $.cleanData,
oldJsvDelimiters = jsv.delimiters,
@@ -249,7 +250,7 @@ function linkedView( view, parentElem, prevNode, parentElViews ) {
}
views = view.parent.views;
if ( $.isArray( views )) {
- i = view.index;
+ i = view.key;
viewsCount = views.length;
while ( i++ < viewsCount-1 ) {
observable( views[ i ] ).setProperty( "index", i );
@@ -257,6 +258,9 @@ function linkedView( view, parentElem, prevNode, parentElViews ) {
}
setArrayChangeLink( view );
}
+ if ( view.tmpl.presenter ) {
+ view.presenter = new view.tmpl.presenter( view.ctx, view );
+ }
}
return view;
}
@@ -299,7 +303,7 @@ function view_render( context ) {
self.removeViews();
self.nodes = [];
- renderAndLink( self, self.index, self.parent.views, self.data, tmpl.render( self.data, context, undefined, TRUE, self ), context );
+ renderAndLink( self, self.key, self.parent.views, self.data, tmpl.render( self.data, context, undefined, TRUE, self ), context );
setArrayChangeLink( self );
}
return self;
@@ -452,7 +456,7 @@ function view_link( data, parentNode, context, prevNode, nextNode, index ) {
parentNode = ("" + parentNode === parentNode ? $( parentNode )[0] : parentNode);
function linkSiblings( parent, prev, next, top ) {
- var view, isElem, type, key, parentElViews, nextSibling, onAfterCreate;
+ var view, isElem, type, key, parentElViews, nextSibling, onAfterCreate, open;
// If we are linking the parent itself (not just content from first onwards) then bind also the data-link attributes
if ( prev === undefined ) {
@@ -722,28 +726,12 @@ $.extend( jsv, {
linkAttr: "data-link",
merge: {
input: {
- from: {
- fromAttr: inputAttrib
- },
- to: {
- toAttr: "value"
- }
- },
- select: {
- from: {
- fromAttr: "value"
- },
- to: {
- toAttr: "value"
- }
+ from: { fromAttr: inputAttrib }, to: { toAttr: "value" }
},
+ textarea: valueBinding,
+ select: valueBinding,
optgroup: {
- from: {
- fromAttr: "label"
- },
- to: {
- toAttr: "label"
- }
+ from: { fromAttr: "label" }, to: { toAttr: "label" }
}
},
delimiters: function( openChars, closeChars ) {
@@ -887,7 +875,7 @@ $.extend({
while( l-- ) {
view = collData[ l ];
if ( view.parent === parentView ) {
- parentView.removeViews( view.index ); // NO - ONLY remove view if its top-level nodes are all.. (TODO)
+ parentView.removeViews( view.key ); // NO - ONLY remove view if its top-level nodes are all.. (TODO)
}
}
}
View
130 jsrender.js
@@ -6,7 +6,7 @@
* Copyright 2012, Boris Moore
* Released under the MIT License.
*/
-// informal pre beta commit counter: 7
+// informal pre beta commit counter: 8
this.jsviews || this.jQuery && jQuery.views || (function( window, undefined ) {
@@ -159,11 +159,11 @@ function renderTag( tag, parentView, converter, content, tagObject ) {
// View constructor
//=================
-function View( context, path, parentView, data, template, index ) {
+function View( context, path, parentView, data, template, key ) {
// Constructor for view object in view hierarchy. (Augmented by JsViews if JsViews is loaded)
var views = parentView.views,
// TODO: add this, as part of smart re-linking on existing content ($.link method), or remove completely
-// self = parentView.views[ index ];
+// self = parentView.views[ key ];
// if ( !self ) { ... }
self = {
tmpl: template,
@@ -171,18 +171,26 @@ function View( context, path, parentView, data, template, index ) {
parent: parentView,
data: data,
ctx: context,
+ // If the data is an array, this is an 'Array View' with a views array for each child 'Instance View'
+ // If the data is not an array, this is an 'Instance View' with a views 'map' object for any child nested views
views: $.isArray( data ) ? [] : {},
hlp: getHelper
};
- if ( $.isArray( views )) {
+ if ( $.isArray( views ) ) {
+ // Parent is an 'Array View'. Add this view to its views array
views.splice(
- self.index = index !== undefined
- ? index
- : views.length, 0, self
- );
+ // self.key = self.key - the index in the parent view array
+ self.key = self.index = key !== undefined
+ ? key
+ : views.length,
+ 0, self);
} else {
- views[ self.index = "_" + autoViewKey++ ] = self;
+ // Parent is an 'Instance View'. Add this view to its views object
+ // self.key = is the key in the parent view map
+ views[ self.key = "_" + autoViewKey++ ] = self;
+ // self.index = is index of the parent
+ self.index = parentView.index;
}
return self;
}
@@ -264,11 +272,11 @@ function converters( name, converterFn ) {
// renderContent
//=================
-function renderContent( data, context, path, index, parentView ) {
+function renderContent( data, context, path, key, parentView ) {
// Render template against data as a tree of subviews (nested template), or as a string (top-level template).
// tagName parameter for internal use only. Used for rendering templates registered as tags (which may have associated presenter objects)
var i, l, dataItem, newView, itemWrap, itemsWrap, itemResult, parentContext, tmpl, layout, onRender, props,
- swapContent = index === TRUE,
+ swapContent = key === TRUE,
self = this,
result = "";
@@ -284,7 +292,7 @@ function renderContent( data, context, path, index, parentView ) {
context = self.ctx || context;
parentView = parentView || self.view;
path = path || self.path;
- index = index || self.index;
+ key = key || self.key;
props = self.props;
} else {
tmpl = self.jquery && self[0] // This is a call $.fn.render
@@ -292,54 +300,56 @@ function renderContent( data, context, path, index, parentView ) {
}
parentView = parentView || jsv.topView;
parentContext = parentView.ctx;
- layout = tmpl.layout;
- if ( data === parentView ) {
- // Inherit the data from the parent view.
- // This may be the contents of an {{if}} block
- data = parentView.data;
- layout = TRUE;
- }
-
- // Set additional context on views created here, (as modified context inherited from the parent, and be inherited by child views)
- // Note: If no jQuery, extend does not support chained copies - so limit extend() to two parameters
- // TODO make this reusable also for use in JsViews, for adding context passed in with the link() method.
- context = (context && context === parentContext)
- ? parentContext
- : context
- // if context, make copy
- // If context, merge context with copied parentContext
- ? extend( extend( {}, parentContext ), context )
- : parentContext;
-
- if ( context.link === FALSE ) {
- // Override inherited value of link by an explicit setting in props: link=false
- // The child views of an unlinked view are also unlinked. So setting child back to true will not have any effect.
- context.onRender = FALSE;
- }
- if ( !tmpl.fn ) {
- tmpl = templates[ tmpl ] || templates( tmpl );
- }
- onRender = context.onRender;
-
if ( tmpl ) {
- if ( $.isArray( data ) && !layout ) {
- // Create a view for the array, whose child views correspond to each data item.
- // (Note: if index and parentView are passed in along with parent view, treat as
- // insert -e.g. from view.addViews - so parentView is already the view item for array)
- newView = swapContent ? parentView : (index !== undefined && parentView) || View( context, path, parentView, data, tmpl, index );
- for ( i = 0, l = data.length; i < l; i++ ) {
- // Create a view for each data item.
- dataItem = data[ i ];
- itemResult = tmpl.fn( dataItem, View( context, path, newView, dataItem, tmpl, (index||0) + i ), jsv );
- result += onRender ? onRender( itemResult, tmpl, props ) : itemResult;
+ layout = tmpl.layout;
+ if ( data === parentView ) {
+ // Inherit the data from the parent view.
+ // This may be the contents of an {{if}} block
+ data = parentView.data;
+ layout = TRUE;
+ }
+
+ // Set additional context on views created here, (as modified context inherited from the parent, and be inherited by child views)
+ // Note: If no jQuery, extend does not support chained copies - so limit extend() to two parameters
+ // TODO make this reusable also for use in JsViews, for adding context passed in with the link() method.
+ context = (context && context === parentContext)
+ ? parentContext
+ : context
+ // if context, make copy
+ // If context, merge context with copied parentContext
+ ? extend( extend( {}, parentContext ), context )
+ : parentContext;
+
+ if ( context.link === FALSE ) {
+ // Override inherited value of link by an explicit setting in props: link=false
+ // The child views of an unlinked view are also unlinked. So setting child back to true will not have any effect.
+ context.onRender = FALSE;
+ }
+ if ( !tmpl.fn ) {
+ tmpl = templates[ tmpl ] || templates( tmpl );
+ }
+ onRender = context.onRender;
+
+ if ( tmpl ) {
+ if ( $.isArray( data ) && !layout ) {
+ // Create a view for the array, whose child views correspond to each data item.
+ // (Note: if key and parentView are passed in along with parent view, treat as
+ // insert -e.g. from view.addViews - so parentView is already the view item for array)
+ newView = swapContent ? parentView : (key !== undefined && parentView) || View( context, path, parentView, data, tmpl, key );
+ for ( i = 0, l = data.length; i < l; i++ ) {
+ // Create a view for each data item.
+ dataItem = data[ i ];
+ itemResult = tmpl.fn( dataItem, View( context, path, newView, dataItem, tmpl, (key||0) + i ), jsv );
+ result += onRender ? onRender( itemResult, tmpl, props ) : itemResult;
+ }
+ } else {
+ // Create a view for singleton data object.
+ newView = swapContent ? parentView : View( context, path, parentView, data, tmpl, key );
+ result += (data || layout) ? tmpl.fn( data, newView, jsv ) : "";
}
- } else {
- // Create a view for singleton data object.
- newView = swapContent ? parentView : View( context, path, parentView, data, tmpl, index );
- result += (data || layout) ? tmpl.fn( data, newView, jsv ) : "";
+ parentView.topKey = newView.key;
+ return onRender ? onRender( result, tmpl, props, newView.key, path ) : result;
}
- parentView.topKey = newView.index;
- return onRender ? onRender( result, tmpl, props, newView.index, path ) : result;
}
return ""; // No tmpl. Could throw...
}
@@ -714,7 +724,7 @@ function compile( name, tmpl, parent, options ) {
}
//==== /end of function compile ====
-function TmplObject( markup, options, parent, index ) {
+function TmplObject( markup, options, parent, key ) {
// Template object constructor
// nested helper function
@@ -737,8 +747,8 @@ function TmplObject( markup, options, parent, index ) {
tmpl.templates = extend( extend( {}, parent.templates ), options.templates );
}
tmpl.parent = parent;
- tmpl.name = parent.name + "[" + index + "]";
- tmpl.index = index;
+ tmpl.name = parent.name + "[" + key + "]";
+ tmpl.key = key;
}
extend( tmpl, options );
Please sign in to comment.
Something went wrong with that request. Please try again.