mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
widgets now work properly
This commit is contained in:
parent
85f13d602e
commit
d717020911
7 changed files with 134 additions and 49 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue