aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisko Hevery2010-04-02 11:10:36 -0700
committerMisko Hevery2010-04-02 11:10:36 -0700
commitd717020911a350a5ea3c0a985c57d56c8fcad607 (patch)
tree0b0a9f14cdfdcdf0dfb4c3fd607daf5ab9f9901d
parent85f13d602e31424b2e2d18172872f14a24c31135 (diff)
downloadangular.js-d717020911a350a5ea3c0a985c57d56c8fcad607.tar.bz2
widgets now work properly
-rw-r--r--scenario/widgets.html14
-rw-r--r--src/Angular.js8
-rw-r--r--src/Scope.js3
-rw-r--r--src/Widgets.js78
-rw-r--r--src/services.js42
-rw-r--r--test/servicesSpec.js28
-rw-r--r--test/widgetsSpec.js10
7 files changed, 134 insertions, 49 deletions
diff --git a/scenario/widgets.html b/scenario/widgets.html
index 5c11a2ee..c2042b68 100644
--- a/scenario/widgets.html
+++ b/scenario/widgets.html
@@ -32,7 +32,7 @@
<td>radio</td>
<td>
<input type="radio" name="gender" value="female"/> Female <br/>
- <input type="radio" name="gender" value="male"/> Male
+ <input type="radio" name="gender" value="male" checked="checked"/> Male
</td>
<td>gender={{gender}}</td>
</tr>
@@ -42,7 +42,9 @@
<input type="checkbox" name="checkbox.tea" checked value="on"/> Tea<br/>
<input type="checkbox" name="checkbox.coffee" value="on"/> Coffe
</td>
- <td>checkbox={{checkbox}}</td>
+ <td>
+ <pre>checkbox={{checkbox}}</pre>
+ </td>
</tr>
<tr>
<td>select</td>
@@ -71,10 +73,10 @@
<td>ng-action</td>
<td>
<form ng-init="button.count = 0">
- <input type="button" value="button" ng-action="button.count = button.count + 1"/> <br/>
- <input type="submit" value="submit" ng-action="button.count = button.count + 1"/><br/>
- <input type="image" src="" ng-action="button.count = button.count + 1"/><br/>
- <a href="" ng-action="button.count = button.count + 1">action</a>
+ <input type="button" value="button" ng-change="button.count = button.count + 1"/> <br/>
+ <input type="submit" value="submit" ng-change="button.count = button.count + 1"/><br/>
+ <input type="image" src="" ng-change="button.count = button.count + 1"/><br/>
+ <a href="" ng-click="button.count = button.count + 1">action</a>
</form>
</td>
<td>button={{button}}</td>
diff --git a/src/Angular.js b/src/Angular.js
index 5b5aa87b..4e3266eb 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -398,6 +398,14 @@ function parseKeyValue(keyValue) {
return obj;
}
+function toKeyValue(obj) {
+ var parts = [];
+ foreach(obj, function(value, key){
+ parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
+ });
+ return parts.length ? parts.join('&') : '';
+};
+
function angularInit(config){
if (config.autobind) {
compile(window.document, config).$init();
diff --git a/src/Scope.js b/src/Scope.js
index ba86e24f..ae5bd384 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -102,6 +102,7 @@ function createScope(parent, Class) {
instance = new Behavior();
extend(api, {
+ 'this': instance,
$parent: parent,
$bind: bind(instance, bind, instance),
$get: bind(instance, getter, instance),
@@ -162,7 +163,7 @@ function createScope(parent, Class) {
behavior.$root = instance;
behavior.$parent = instance;
}
-
+ (parent.$onEval || noop)(instance.$eval);
Class.apply(instance, slice.call(arguments, 2, arguments.length));
return instance;
diff --git a/src/Widgets.js b/src/Widgets.js
index f172eae2..e42d981c 100644
--- a/src/Widgets.js
+++ b/src/Widgets.js
@@ -85,8 +85,8 @@ function optionsAccessor(scope, element) {
function noopAccessor() { return { get: noop, set: noop }; }
-var textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''),
- buttonWidget = inputWidget('click', noopAccessor, noopAccessor, undefined),
+var textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, initWidgetValue('')),
+ buttonWidget = inputWidget('click', noopAccessor, noopAccessor, noop),
INPUT_TYPE = {
'text': textWidget,
'textarea': textWidget,
@@ -96,29 +96,42 @@ var textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''),
'submit': buttonWidget,
'reset': buttonWidget,
'image': buttonWidget,
- 'checkbox': inputWidget('click', modelAccessor, checkedAccessor, false),
- 'radio': inputWidget('click', modelAccessor, radioAccessor, undefined),
- 'select-one': inputWidget('click', modelAccessor, valueAccessor, null),
- 'select-multiple': inputWidget('click', modelAccessor, optionsAccessor, [])
+ 'checkbox': inputWidget('click', modelAccessor, checkedAccessor, initWidgetValue(false)),
+ 'radio': inputWidget('click', modelAccessor, radioAccessor, radioInit),
+ 'select-one': inputWidget('change', modelAccessor, valueAccessor, initWidgetValue(null)),
+ 'select-multiple': inputWidget('change', modelAccessor, optionsAccessor, initWidgetValue([]))
// 'file': fileWidget???
};
-function inputWidget(events, modelAccessor, viewAccessor, initValue) {
+function initWidgetValue(initValue) {
+ return function (model, view) {
+ var value = view.get() || copy(initValue);
+ if (isUndefined(model.get()) && isDefined(value))
+ model.set(value);
+ };
+}
+
+function radioInit(model, view) {
+ var modelValue = model.get(), viewValue = view.get();
+ if (isUndefined(modelValue)) model.set(null);
+ if (viewValue != null) model.set(viewValue);
+}
+
+function inputWidget(events, modelAccessor, viewAccessor, initFn) {
return function(element) {
var scope = this,
model = modelAccessor(scope, element),
view = viewAccessor(scope, element),
- action = element.attr('ng-change') || '',
- value = view.get() || copy(initValue);
- if (isUndefined(model.get()) && isDefined(value)) model.set(value);
+ action = element.attr('ng-change') || '';
+ initFn(model, view);
this.$eval(element.attr('ng-init')||'');
element.bind(events, function(){
model.set(view.get());
scope.$tryEval(action, element);
scope.$root.$eval();
- // if we have no initValue than we are just a button,
+ // if we have noop initFn than we are just a button,
// therefore we want to prevent default action
- return isDefined(initValue);
+ return initFn != noop;
});
view.set(model.get());
scope.$watch(model.get, view.set);
@@ -137,3 +150,44 @@ angularWidget('SELECT', function(element){
this.descend(true);
return inputWidgetSelector.call(this, element);
});
+
+
+angularWidget('INLINE', function(element){
+ element.replaceWith(this.element("div"));
+ var compiler = this,
+ behavior = element.attr("behavior"),
+ template = element.attr("template"),
+ initExpr = element.attr("init");
+ return function(boundElement){
+ var scope = this;
+ boundElement.load(template, function(){
+ var templateScope = compiler.compile(boundElement)(boundElement, scope);
+ templateScope.$tryEval(initExpr, boundElement);
+ templateScope.$init();
+ });
+ };
+});
+
+angularWidget('INCLUDE', function(element){
+ element.replaceWith(this.element("div"));
+ var matches = [];
+ element.find("INLINE").each(function(){
+ matches.push({match: jQuery(this).attr("match"), element: jQuery(this)});
+ });
+ var compiler = this,
+ watchExpr = element.attr("watch");
+ return function(boundElement){
+ var scope = this;
+ this.$watch(watchExpr, function(value){
+ foreach(matches, function(inline){
+ if(inline.match == value) {
+ var template = inline.element.attr("template");
+ boundElement.load(template, function(){
+ var templateScope = compiler.compile(boundElement)(boundElement, scope);
+ templateScope.$init();
+ });
+ }
+ });
+ });
+ };
+});
diff --git a/src/services.js b/src/services.js
index 5d235b32..fc12b22b 100644
--- a/src/services.js
+++ b/src/services.js
@@ -1,34 +1,40 @@
angularService("$window", bind(window, identity, window));
-var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.]+)(:([0-9]+))?([^\?#]+)?(\?([^#]*))((#([^\?]*))(\?([^\?]*))?)$/;
+var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.]*)(:([0-9]+))?([^\?#]+)(\?([^#]*))?((#([^\?]*))?(\?([^\?]*))?)$/;
+var DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
angularService("$location", function(){
var scope = this;
function location(url){
if (isDefined(url)) {
var match = URL_MATCH.exec(url);
- dump(match);
- location.href = url;
- location.protocol = match[1];
- location.host = match[3];
- location.port = match[5];
- location.path = match[6];
- location.search = parseKeyValue(match[8]);
- location.hash = match[9];
- location.hashPath = match[11];
- location.hashSearch = parseKeyValue(match[13]);
- foreach(location, dump);
+ if (match) {
+ location.href = url;
+ location.protocol = match[1];
+ location.host = match[3] || '';
+ location.port = match[5] || DEFAULT_PORTS[location.href] || null;
+ location.path = match[6];
+ location.search = parseKeyValue(match[8]);
+ location.hash = match[9];
+ if (location.hash) location.hash = location.hash.substr(1);
+ location.hashPath = match[11] || '';
+ location.hashSearch = parseKeyValue(match[13]);
+ }
}
- var params = [];
- foreach(location.param, function(value, key){
- params.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
- });
- return (location.path ? location.path : '') + (params.length ? '?' + params.join('&') : '');
+ var hashKeyValue = toKeyValue(location.hashSearch);
+ return location.href +
+ (location.hashPath ? location.hashPath : '') +
+ (hashKeyValue ? '?' + hashKeyValue : '');
};
this.$config.location.watch(function(url){
location(url);
});
+ location(this.$config.location.get());
this.$onEval(PRIORITY_LAST, function(){
- scope.$config.location.set(location());
+ var href = location();
+ if (href != location.href) {
+ scope.$config.location.set(location());
+ location.href = href;
+ }
});
return location;
});
diff --git a/test/servicesSpec.js b/test/servicesSpec.js
index 88cbc947..dec4550f 100644
--- a/test/servicesSpec.js
+++ b/test/servicesSpec.js
@@ -14,21 +14,35 @@ describe("services", function(){
});
it("should inject $location", function(){
- scope.$location('http://host:1234/p/a/t/h?query=value#path?key=value');
- expect(scope.$location.href).toEqual("http://host:123/p/a/t/h?query=value#path?key=value");
+ scope.$location('http://host:123/p/a/t/h.html?query=value#path?key=value');
+ expect(scope.$location.href).toEqual("http://host:123/p/a/t/h.html?query=value#path?key=value");
expect(scope.$location.protocol).toEqual("http");
expect(scope.$location.host).toEqual("host");
- expect(scope.$location.port).toEqual("1234");
- expect(scope.$location.path).toEqual("/p/a/t/h");
+ expect(scope.$location.port).toEqual("123");
+ expect(scope.$location.path).toEqual("/p/a/t/h.html");
expect(scope.$location.search).toEqual({query:'value'});
expect(scope.$location.hash).toEqual('path?key=value');
expect(scope.$location.hashPath).toEqual('path');
expect(scope.$location.hashSearch).toEqual({key:'value'});
- scope.$anchor.path = 'page=http://path';
- scope.$anchor.param = {k:'a=b'};
+ scope.$location.hashPath = 'page=http://path';
+ scope.$location.hashSearch = {k:'a=b'};
- expect(scope.$anchor()).toEqual('page=http://path?k=a%3Db');
+ expect(scope.$location()).toEqual('http://host:123/p/a/t/h.html?query=value#path?key=valuepage=http://path?k=a%3Db');
+ });
+
+ it('should parse file://', function(){
+ scope.$location('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html');
+ expect(scope.$location.href).toEqual("file:///Users/Shared/misko/work/angular.js/scenario/widgets.html");
+ expect(scope.$location.protocol).toEqual("file");
+ expect(scope.$location.host).toEqual("");
+ expect(scope.$location.port).toEqual(null);
+ expect(scope.$location.path).toEqual("/Users/Shared/misko/work/angular.js/scenario/widgets.html");
+ expect(scope.$location.search).toEqual({});
+ expect(scope.$location.hash).toEqual('');
+ expect(scope.$location.hashPath).toEqual('');
+ expect(scope.$location.hashSearch).toEqual({});
+ expect(scope.$location()).toEqual('file:///Users/Shared/misko/work/angular.js/scenario/widgets.html');
});
});
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index 6123e229..6c47e5dd 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -114,23 +114,23 @@ describe("input widget", function(){
it('should type="checkbox"', function(){
compile('<input type="checkbox" name="checkbox" checked ng-change="action = true"/>');
- expect(scope.$get('checkbox')).toEqual(true);
+ expect(scope.checkbox).toEqual(true);
element.click();
- expect(scope.$get('checkbox')).toEqual(false);
- expect(scope.$get('action')).toEqual(true);
+ expect(scope.checkbox).toEqual(false);
+ expect(scope.action).toEqual(true);
element.click();
- expect(scope.$get('checkbox')).toEqual(true);
+ expect(scope.checkbox).toEqual(true);
});
it('should type="radio"', function(){
compile('<div>' +
'<input type="radio" name="chose" value="A" ng-change="clicked = 1"/>' +
'<input type="radio" name="chose" value="B" checked ng-change="clicked = 2"/>' +
+ '<input type="radio" name="chose" value="C" ng-change="clicked = 3"/>' +
'</div>');
var a = element[0].childNodes[0];
var b = element[0].childNodes[1];
expect(scope.chose).toEqual('B');
- expect(scope.clicked).not.toBeDefined();
scope.chose = 'A';
scope.$eval();
expect(a.checked).toEqual(true);