Skip to content

Commit

Permalink
Implementation of the select element
Browse files Browse the repository at this point in the history
Close #304
  • Loading branch information
fbasso committed Oct 8, 2014
1 parent d06b1bf commit 075038e
Show file tree
Hide file tree
Showing 13 changed files with 743 additions and 28 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ To work on the playground the most simple option is to open 2 terminal windows:
- one running `grunt docs:playground` to build the playground and launch the webserver
- another one running `grunt docs:watch` to watch the file changes and copy the changed files to the hashspace-gh-pages folder that is referenced by the webserver launched in the first terminal

Then you can use `http://localhost:8000?dev=true` in your favorite browser to get the development version of hashspace

[key_features_blog]: http://ariatemplates.com/blog/2012/11/key-features-for-client-side-templates/
[todomvc]: http://addyosmani.github.com/todomvc/
[angular]:http://angularjs.org/
Expand Down
47 changes: 31 additions & 16 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ It accepts 3 parameters:
* `options {Object}` supported options are:
* `includeSyntaxTree {Boolean}` if true, the result object will contain the syntax tree generated by the compiler.
* `bypassJSvalidation {Boolean}` if true, the validation of the generated JS file (including non-template code) is bypassed - default:false.
* `mode {String}` the type of module system the code shold comply with: either "commonJS" or "global"
* `mode {String}` the type of module system the code shold comply with: either "commonJS" or "global"
* `globalRef {String}` the name of the runtime global reference when the "global" mode is used (default: "hsp")


The returned object contains:
* `errors {Array}` the error list - each error having the following structure:
* `description {String}` a message describing the error
* `line {Number}` the error line number
* `column {Number}` the error column number
* `column {Number}` the error column number
* `code {String}` a code extract showing where the error occurs (optional)
* `code {String}` the generated JavaScript code
* `syntaxTree {JSON}` the syntax tree generated by the parser (optional)
Expand Down Expand Up @@ -424,6 +424,21 @@ Doing it this way also allows the engine to automatically detect and create butt
<input type="radio" model="{data.value}" value="{data.choice3}" />
```

##### Select

Selects elements can be bound with the model or value attribute.
They can only take values available inside the option list.
Trying to set the data model to a value not existing in the options will have no effect (the value is unchanged)
so that the select value and the data model will always be synchronized.

```html
<select model="{data.value}">
{foreach item in data.options}
<option value="{item.value}">{item.label}</option>
{/foreach}
</select>
```

---

#### Gestures event handlers attributes
Expand Down Expand Up @@ -603,7 +618,7 @@ Hashspace natively supports different types of attributes:

In order to use a `template` attribute, the template controller has to declare an attribute of such a type.

When the component content is defined, it is automatically bound to this attribute (if only a `template` attribute is defined), or to the only attribute having the defaultContent flag set to true.
When the component content is defined, it is automatically bound to this attribute (if only a `template` attribute is defined), or to the only attribute having the defaultContent flag set to true.

---

Expand Down Expand Up @@ -836,15 +851,15 @@ var ClassC = klass({
$constructor : function (incr) {
ClassB.$constructor.call(this);
this.idx += incr;
}
});
}
});
```

#### Model updates

##### Object update

To implement data-binding, Hashspace reprocesses JavaScript to introduce a partial polyfill to Object.observe and detect changes that occur to JavaScript objects. Hashspace actually uses a transpiler to encapsulate assignments with an internal `$set()` method that performs the assignment and notifies the potential observers.
To implement data-binding, Hashspace reprocesses JavaScript to introduce a partial polyfill to Object.observe and detect changes that occur to JavaScript objects. Hashspace actually uses a transpiler to encapsulate assignments with an internal `$set()` method that performs the assignment and notifies the potential observers.

In the mid-term the $set() utility will become obsolete, once the Object.observe feature is implemented by all web-browsers, and Hashspace will rely on the browser's Object.observe implementation.

Expand Down Expand Up @@ -1030,7 +1045,7 @@ A test context is a function object that exposes the following properties and me

It performs the assignment, notifies the potential observers and forces an hashspace refresh.

Parameters:
Parameters:
* {Object} `container` the object that contains a property to be set
* {String} `property` the property to be set
* {Object} `value` the value to be assigned to the given property
Expand All @@ -1045,7 +1060,7 @@ A test context is a function object that exposes the following properties and me

This method returns an array (if the parameter is not specified), otherwise it returns the corresponding log message.

Parameters:
Parameters:
* {integer} `idx` the position of the log message (first index = 0)

Furthermore, the logs object exposes the following method:
Expand All @@ -1055,11 +1070,11 @@ A test context is a function object that exposes the following properties and me

#### Selector accessor `.(selector)`

Using the `TestContext` function it is possible to retrive an array of DOM elements, filtered according to the provided selector (as it is done in jQuery by means of the `$` object); it returns a `SelectionWrapper` object.
Using the `TestContext` function it is possible to retrive an array of DOM elements, filtered according to the provided selector (as it is done in jQuery by means of the `$` object); it returns a `SelectionWrapper` object.

i.e:
```javascript
var HEAD = testCtxt(".panel .head");
var HEAD = testCtxt(".panel .head");
```

---
Expand All @@ -1072,15 +1087,15 @@ An instance of `SelectionWrapper` provides the following methods:

It permits to further refine the selection by applying a new selector.

Parameters:
Parameters:
* {String} `selector`: the selector expression (jquery selector syntax)


* ###### `text(trim)`

It returns the textual content of the selection (by recursively going through all DOM sub-nodes) and concatenates the different text node content.

Parameters:
Parameters:
* {Boolean} `trim`: whether the returned text has to be trimmed - true by default


Expand All @@ -1093,23 +1108,23 @@ An instance of `SelectionWrapper` provides the following methods:

It returns the value of an attribute of the selected node (it only works on single-element selections).

Parameters:
Parameters:
* {String} `attName` the name of the attribute - e.g. "title"


* ###### `hasClass(cssClassName)`

This method tells if the first element in the selection is assigned a specified CSS class.

Parameters:
Parameters:
* {String} `cssClassName` the class name to check


* ###### `item(idx)`

It returns the selection corresponding to the nth element in the selection.

Parameters:
Parameters:
* {integer} `idx` the position of the element (first index = 0)


Expand All @@ -1127,7 +1142,7 @@ An instance of `SelectionWrapper` provides the following methods:

It simulates a type event and triggers an hashspace refresh

Parameters:
Parameters:
* {String} `text` the text to be typed


Expand Down
4 changes: 4 additions & 0 deletions docs/samples/inputonupdate/inputonupdate.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
var hsp=require("hsp/rt");
var sample = require('./inputonupdate.hsp');
var fireEvent = require("hsp/utils/eventgenerator").fireEvent;

Expand All @@ -21,6 +22,7 @@ describe('"Input onupdate" sample', function () {
expect(span.innerHTML).to.equal("Oninput result: ");
input.value = "a";
fireEvent("keyup", input);
hsp.refresh();

clock.tick(300);
expect(span.innerHTML).to.equal("Oninput result: ");
Expand All @@ -39,6 +41,7 @@ describe('"Input onupdate" sample', function () {
expect(span.innerHTML).to.equal("Oninput result: ");
input.value = "a";
fireEvent("keyup", input);
hsp.refresh();

clock.tick(1500);
expect(span.innerHTML).to.equal("Oninput result: ");
Expand All @@ -57,6 +60,7 @@ describe('"Input onupdate" sample', function () {
expect(span.innerHTML).to.equal("Oninput result: ");
textarea.value = "a";
fireEvent("keyup", textarea);
hsp.refresh();

clock.tick(300);
expect(span.innerHTML).to.equal("Oninput result: ");
Expand Down
7 changes: 7 additions & 0 deletions docs/samples/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ module.exports = [{
src : "inputsample.hsp",
main : true
}]
}, {
title : "Select fields and options elements with bi-directional bindings",
folder : "selectsample",
files : [{
src : "selectsample.hsp",
main : true
}]
}, {
title : "Multi-line inputs: textarea elements",
folder : "textarea",
Expand Down
14 changes: 14 additions & 0 deletions docs/samples/selectsample/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

Hashspace listens to the change event of the select elements in order to synchronize its value with the data referenced through the value or model expression.
The options can also be bound, and changing the options list can impact the select value.

For example, in the following select, we can add or remove the fourth option.
- If we try to set the data model value to 'four' when the option doesn't exist, the select value and the data model will remain unchanged,
- If we remove the option 'four' when this one is selected, the value will be set automatically to the first one.

[#output]

**Note:**
- An invalid value (not existing in its options) can't be set in the data model.
- The options list can be change completely. In this case, a valid select value will be kept, otherwise, the first one will be selected.

84 changes: 84 additions & 0 deletions docs/samples/selectsample/selectsample.hsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template selectSample(data)>
<div class="info2">The following select and its options are synchronized:</div>
<div class="section">
Select with bound value:
<select model="{data.selectedValue}" style="width:100px">
{foreach item in data.options}
<option value="{item.value}">{item.label}</option>
{/foreach}
</select>
</div>
<div class="section">
The selected value is <span class="info">{data.selectedValue}</span>,
the selected text is <span class="info">{getSelectedText(data.selectedValue)}</span>
</div>
<div class="info2">You can add or remove options, and select one by changing the data model value:</div>
<div class="section">
<button onclick="{addOption('four', 'Four')}">Add option 'four'</button>
<button onclick="{selectOption('four')}">Select option 'four'</button>
<button onclick="{removeOption('four')}">Remove option 'four'</button>
</div>
</template>

<script>
var d={
selectedValue:"two",
options : [
{
value: "one",
label: "One"
},
{
value: "two",
label: "Two"
},
{
value: "three",
label: "Three"
}
]
};

// Needed by the playground application.
// Update it, but do not remove it!
module.exports = {
template: selectSample,
data: [d]
};

var getSelectedText = function(value) {
var options = d.options;
for(var i = 0; i < options.length; i++) {
var option = options[i];
if (option.value == value) {
return option.label;
}
}
return "no selection";
}
var addOption = function(value, label) {
var options = d.options;
for(var i = 0; i < options.length; i++) {
var option = options[i];
if (option.value == value) {
return;
}
}
options.push({value: value, label: label});
};

var selectOption = function(value) {
d.selectedValue = value;
};

var removeOption = function(value) {
var options = d.options;
for(var i = 0; i < options.length; i++) {
var option = options[i];
if (option.value == value) {
options.splice(i, 1);
return;
}
}
};
</script>
19 changes: 19 additions & 0 deletions docs/samples/selectsample/selectsample.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var hashTester = require('hsp/utils/hashtester');
var sample = require('./selectsample.hsp');

describe('"Select" sample', function () {

var h;
beforeEach(function () {
h = hashTester.newTestContext();
sample.template.apply(this, sample.data).render(h.container);
});

afterEach(function () {
h.$dispose();
});

it('should render "Select"', function () {

});
});
2 changes: 1 addition & 1 deletion hsp/rt/attributes/onupdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var OnUpdateHandler = klass({

$setValue: function(name, value) {
if (name === "update-timeout") {
var valueAsNumber = parseInt(value);
var valueAsNumber = parseInt(value, 10);
if (!isNaN(valueAsNumber)) {
this.timerValue = valueAsNumber;
}
Expand Down

0 comments on commit 075038e

Please sign in to comment.