Skip to content

Commit

Permalink
panel widget: added data-opened bidi databinding
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobias Bosch committed Mar 15, 2013
1 parent 0f824d8 commit 87cd403
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 128 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Expand Up @@ -5,7 +5,9 @@ Changelog
-----
Features:
- upgrade to angular 1.0.5, jqm 1.3 and jquery 1.9
* `table` widget does not refresh if new rows are added. Jqm will add this in jqm 1.3.1 (https://github.com/jquery/jquery-mobile/issues/5570).
- popup widget has bidi databinding for `data-opened` attribute.
- panel widget has bidi databinding for `data-opened` attribute.
- Refactored build system to grunt.js, testacular and travis-ci.
- Added provider `jqmNgWidget` to easily adapter new jqm plugins with angular.
Also automatically detects widgets of jqm plugins and registers angular directives for them.
Expand Down
9 changes: 8 additions & 1 deletion README.md
Expand Up @@ -370,12 +370,19 @@ Note: `pagerId.cache` stores the last result that was returns for a `list | page

### widget `popup`

- the jqm adapter does not change the url when a popup is opened, and therefore does not go back when the popup is closed. This is due to the fact that popups cannot be addressed using a url, in contrast to dialogs.
- the jqm adapter does not change the url when a popup is opened/closed. This is due to the fact that the jqm adatper assumes that urls represent routings for pages, and not parts of pages.

- The new attribute `data-opened` has bidirectional data binding for opening/closing the popup, e.g.

<div data-role="popup" data-opened="someProperty">...</div>

### widget `panel`

- the jqm adapter does not change the url when a panel is opened/closed. This is due to the fact that the jqm adatper assumes that urls represent routings for pages, and not parts of pages.

- The new attribute `data-opened` has bidirectional data binding for opening/closing the panel, e.g.

<div data-role="panel" data-opened="someProperty">...</div>


### widget `slider` in a `<select>`
Expand Down
83 changes: 47 additions & 36 deletions compiled/jquery-mobile-angular-adapter-standalone.js
Expand Up @@ -36249,6 +36249,7 @@ factory(window.jQuery, window.angular);
jqmNgWidgetProvider.widget("controlgroup", ["jqmNgWidget", controlgroupWidget]);
jqmNgWidgetProvider.widget("slider", ["jqmNgWidget", "$timeout", sliderWidget]);
jqmNgWidgetProvider.widget("popup", ["jqmNgWidget", "$parse", popupWidget]);
jqmNgWidgetProvider.widget("panel", ["jqmNgWidget", "$parse", panelWidget]);
}]);

function defaultWidget(jqmNgWidet) {
Expand All @@ -36265,51 +36266,61 @@ factory(window.jQuery, window.angular);
link: function(widgetName, scope, iElement, iAttrs, ngModelCtrl, selectCtrl) {
jqmNgWidet.createWidget(widgetName, iElement, iAttrs);
jqmNgWidet.bindDefaultAttrsAndEvents(widgetName, scope, iElement, iAttrs, ngModelCtrl);
bindOpenedAttribute(scope, iElement, iAttrs);
addOpenedBinding("popup", $parse, scope, iElement, iAttrs, '_');
}
};
}

function bindOpenedAttribute(scope, iElement, iAttrs) {
var syncing = false;
if (iAttrs.opened) {
var openedGetter = $parse(iAttrs.opened),
openedSetter = openedGetter.assign;

scope.$watch(openedGetter, updateWidget);
if (openedSetter) {
updateScopeOn("_open", true);
updateScopeOn("_close", false);
}
function panelWidget(jqmNgWidet, $parse) {
return {
link: function(widgetName, scope, iElement, iAttrs, ngModelCtrl, selectCtrl) {
jqmNgWidet.createWidget(widgetName, iElement, iAttrs);
jqmNgWidet.bindDefaultAttrsAndEvents(widgetName, scope, iElement, iAttrs, ngModelCtrl);
addOpenedBinding("panel", $parse, scope, iElement, iAttrs, '');
}
};
}

function updateScopeOn(methodName, scopeValue) {
var widget = iElement.data($.mobile.popup.prototype.widgetFullName),
_old = widget[methodName];
widget[methodName] = function() {
var res = _old.apply(this, arguments);
if (!syncing) {
syncing = true;
scope.$apply(function () {
openedSetter(scope, scopeValue);
});
syncing = false;
}
return res;
};
function addOpenedBinding(widgetName, $parse, scope, iElement, iAttrs, openCloseMethodPrefix) {
var syncing = false;
if (iAttrs.opened) {
var openedGetter = $parse(iAttrs.opened),
openedSetter = openedGetter.assign;

scope.$watch(openedGetter, updateWidget);
if (openedSetter) {
updateScopeOn(openCloseMethodPrefix+"open", true);
updateScopeOn(openCloseMethodPrefix+"close", false);
}
}

function updateWidget(opened) {
if (syncing) {
return;
}
syncing = true;
if (opened) {
iElement.popup("open");
} else {
iElement.popup("close");
function updateScopeOn(methodName, scopeValue) {
var widget = iElement.data($.mobile[widgetName].prototype.widgetFullName),
_old = widget[methodName];
widget[methodName] = function() {
var res = _old.apply(this, arguments);
if (!syncing) {
syncing = true;
scope.$apply(function () {
openedSetter(scope, scopeValue);
});
syncing = false;
}
syncing = false;
return res;
};
}

function updateWidget(opened) {
if (syncing) {
return;
}
syncing = true;
if (opened) {
iElement[widgetName]("open");
} else {
iElement[widgetName]("close");
}
syncing = false;
}
}

Expand Down
83 changes: 47 additions & 36 deletions compiled/jquery-mobile-angular-adapter.js
Expand Up @@ -813,6 +813,7 @@ factory(window.jQuery, window.angular);
jqmNgWidgetProvider.widget("controlgroup", ["jqmNgWidget", controlgroupWidget]);
jqmNgWidgetProvider.widget("slider", ["jqmNgWidget", "$timeout", sliderWidget]);
jqmNgWidgetProvider.widget("popup", ["jqmNgWidget", "$parse", popupWidget]);
jqmNgWidgetProvider.widget("panel", ["jqmNgWidget", "$parse", panelWidget]);
}]);

function defaultWidget(jqmNgWidet) {
Expand All @@ -829,51 +830,61 @@ factory(window.jQuery, window.angular);
link: function(widgetName, scope, iElement, iAttrs, ngModelCtrl, selectCtrl) {
jqmNgWidet.createWidget(widgetName, iElement, iAttrs);
jqmNgWidet.bindDefaultAttrsAndEvents(widgetName, scope, iElement, iAttrs, ngModelCtrl);
bindOpenedAttribute(scope, iElement, iAttrs);
addOpenedBinding("popup", $parse, scope, iElement, iAttrs, '_');
}
};
}

function bindOpenedAttribute(scope, iElement, iAttrs) {
var syncing = false;
if (iAttrs.opened) {
var openedGetter = $parse(iAttrs.opened),
openedSetter = openedGetter.assign;

scope.$watch(openedGetter, updateWidget);
if (openedSetter) {
updateScopeOn("_open", true);
updateScopeOn("_close", false);
}
function panelWidget(jqmNgWidet, $parse) {
return {
link: function(widgetName, scope, iElement, iAttrs, ngModelCtrl, selectCtrl) {
jqmNgWidet.createWidget(widgetName, iElement, iAttrs);
jqmNgWidet.bindDefaultAttrsAndEvents(widgetName, scope, iElement, iAttrs, ngModelCtrl);
addOpenedBinding("panel", $parse, scope, iElement, iAttrs, '');
}
};
}

function updateScopeOn(methodName, scopeValue) {
var widget = iElement.data($.mobile.popup.prototype.widgetFullName),
_old = widget[methodName];
widget[methodName] = function() {
var res = _old.apply(this, arguments);
if (!syncing) {
syncing = true;
scope.$apply(function () {
openedSetter(scope, scopeValue);
});
syncing = false;
}
return res;
};
function addOpenedBinding(widgetName, $parse, scope, iElement, iAttrs, openCloseMethodPrefix) {
var syncing = false;
if (iAttrs.opened) {
var openedGetter = $parse(iAttrs.opened),
openedSetter = openedGetter.assign;

scope.$watch(openedGetter, updateWidget);
if (openedSetter) {
updateScopeOn(openCloseMethodPrefix+"open", true);
updateScopeOn(openCloseMethodPrefix+"close", false);
}
}

function updateWidget(opened) {
if (syncing) {
return;
}
syncing = true;
if (opened) {
iElement.popup("open");
} else {
iElement.popup("close");
function updateScopeOn(methodName, scopeValue) {
var widget = iElement.data($.mobile[widgetName].prototype.widgetFullName),
_old = widget[methodName];
widget[methodName] = function() {
var res = _old.apply(this, arguments);
if (!syncing) {
syncing = true;
scope.$apply(function () {
openedSetter(scope, scopeValue);
});
syncing = false;
}
syncing = false;
return res;
};
}

function updateWidget(opened) {
if (syncing) {
return;
}
syncing = true;
if (opened) {
iElement[widgetName]("open");
} else {
iElement[widgetName]("close");
}
syncing = false;
}
}

Expand Down
83 changes: 47 additions & 36 deletions src/integration/widgetAdapters.js
Expand Up @@ -18,6 +18,7 @@
jqmNgWidgetProvider.widget("controlgroup", ["jqmNgWidget", controlgroupWidget]);
jqmNgWidgetProvider.widget("slider", ["jqmNgWidget", "$timeout", sliderWidget]);
jqmNgWidgetProvider.widget("popup", ["jqmNgWidget", "$parse", popupWidget]);
jqmNgWidgetProvider.widget("panel", ["jqmNgWidget", "$parse", panelWidget]);
}]);

function defaultWidget(jqmNgWidet) {
Expand All @@ -34,51 +35,61 @@
link: function(widgetName, scope, iElement, iAttrs, ngModelCtrl, selectCtrl) {
jqmNgWidet.createWidget(widgetName, iElement, iAttrs);
jqmNgWidet.bindDefaultAttrsAndEvents(widgetName, scope, iElement, iAttrs, ngModelCtrl);
bindOpenedAttribute(scope, iElement, iAttrs);
addOpenedBinding("popup", $parse, scope, iElement, iAttrs, '_');
}
};
}

function bindOpenedAttribute(scope, iElement, iAttrs) {
var syncing = false;
if (iAttrs.opened) {
var openedGetter = $parse(iAttrs.opened),
openedSetter = openedGetter.assign;

scope.$watch(openedGetter, updateWidget);
if (openedSetter) {
updateScopeOn("_open", true);
updateScopeOn("_close", false);
}
function panelWidget(jqmNgWidet, $parse) {
return {
link: function(widgetName, scope, iElement, iAttrs, ngModelCtrl, selectCtrl) {
jqmNgWidet.createWidget(widgetName, iElement, iAttrs);
jqmNgWidet.bindDefaultAttrsAndEvents(widgetName, scope, iElement, iAttrs, ngModelCtrl);
addOpenedBinding("panel", $parse, scope, iElement, iAttrs, '');
}
};
}

function updateScopeOn(methodName, scopeValue) {
var widget = iElement.data($.mobile.popup.prototype.widgetFullName),
_old = widget[methodName];
widget[methodName] = function() {
var res = _old.apply(this, arguments);
if (!syncing) {
syncing = true;
scope.$apply(function () {
openedSetter(scope, scopeValue);
});
syncing = false;
}
return res;
};
function addOpenedBinding(widgetName, $parse, scope, iElement, iAttrs, openCloseMethodPrefix) {
var syncing = false;
if (iAttrs.opened) {
var openedGetter = $parse(iAttrs.opened),
openedSetter = openedGetter.assign;

scope.$watch(openedGetter, updateWidget);
if (openedSetter) {
updateScopeOn(openCloseMethodPrefix+"open", true);
updateScopeOn(openCloseMethodPrefix+"close", false);
}
}

function updateWidget(opened) {
if (syncing) {
return;
}
syncing = true;
if (opened) {
iElement.popup("open");
} else {
iElement.popup("close");
function updateScopeOn(methodName, scopeValue) {
var widget = iElement.data($.mobile[widgetName].prototype.widgetFullName),
_old = widget[methodName];
widget[methodName] = function() {
var res = _old.apply(this, arguments);
if (!syncing) {
syncing = true;
scope.$apply(function () {
openedSetter(scope, scopeValue);
});
syncing = false;
}
syncing = false;
return res;
};
}

function updateWidget(opened) {
if (syncing) {
return;
}
syncing = true;
if (opened) {
iElement[widgetName]("open");
} else {
iElement[widgetName]("close");
}
syncing = false;
}
}

Expand Down
34 changes: 15 additions & 19 deletions test/devSnippetPage.html
Expand Up @@ -20,29 +20,25 @@
</head>
<body ng-app="ngm">
<div id="page1" data-role="dialog">
<div id="page1" data-role="page">
<div data-role="header">
<h1>Page1</h1>
</div>
<div data-role="content" ng-init="o=false;o2=false">
<table data-role="table" data-mode="reflow">
<thead>
<tr>
<th data-priority="1">Rank</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="l in [1,2]">
<th>{{l}}</th>
<td>Hello {{l}}</td>
</tr>
<tr>
<th>3</th>
<td>Hello 3</td>
</tr>
</table>
<div data-role="content">
<label>Opened
<input type="checkbox" ng-model="opened">
</label>

<div data-role="popup" data-opened="opened">
Popup
</div>
</div>


<div data-role="panel" id="defaultpanel" data-opened="opened">
<div class="panel-content">
Content
</div>
</div>
</div>

</body>
Expand Down

0 comments on commit 87cd403

Please sign in to comment.