mirror of
https://github.com/zhigang1992/deployd.git
synced 2026-04-29 01:45:51 +08:00
Added Data editor
This commit is contained in:
@@ -75,7 +75,7 @@
|
||||
if (obj) {
|
||||
for (var k in obj) {
|
||||
if (obj.hasOwnProperty(k)) {
|
||||
if (typeof obj[k] === 'object') {
|
||||
if (typeof obj[k] !== 'string') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,74 @@
|
||||
<h1>Data</h1>
|
||||
<div id="data" class="well">
|
||||
<h3>Data</h3>
|
||||
<div id="current-data">
|
||||
<div class="table-container">
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead class="type-icons">
|
||||
<tr>
|
||||
<td class="code">
|
||||
<i class="icon-white icon-hack string" data-bind="tooltip: 'string'"></i>
|
||||
id
|
||||
</td>
|
||||
<!-- ko foreach: properties -->
|
||||
<td class="code">
|
||||
<i class="icon-white icon-hack" data-bind="tooltip: typeLabel, cssNamed: type"></i>
|
||||
<span data-bind="text: name"></span>
|
||||
</td>
|
||||
<!-- /ko -->
|
||||
<td></td>
|
||||
</tr>
|
||||
<tbody data-bind="foreach: collection">
|
||||
<tr>
|
||||
<td data-bind="text: id"></td>
|
||||
<!-- ko foreach: $root.properties -->
|
||||
<td data-bind="event: {dblclick: $parent._onDoubleclick}">
|
||||
<span data-bind="text: $parent.c_formatted(name, type), visible: !$parent.c_editing()"></span>
|
||||
<div data-bind="visible: $parent.c_editing, template: 'data-cell-edit-template'"></div>
|
||||
</td>
|
||||
<!-- /ko -->
|
||||
<td>
|
||||
<a href="#" class="btn" data-bind="click: c_toggleEditing, visible: !c_editing()"><i class="icon-edit"></i> edit</a>
|
||||
<a href="#" class="btn" data-bind="click: $root.deleteRow, visible: !c_editing()"><i class="icon-remove-sign"></i> delete</a>
|
||||
|
||||
<a href="#" class="btn btn-primary" data-bind="click: $root.saveRow, visible: c_editing"><i class="icon-white icon-ok-sign"></i> done</a>
|
||||
<a href="#" class="btn" data-bind="click: c_toggleEditing, visible: c_editing">cancel</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr data-bind="visible: !collection().length">
|
||||
<td data-bind="attr: {colspan: properties().length + 2}">
|
||||
<div class="alert alert-info" style="margin: 40px auto; width: 120px;">This collection is empty.</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr data-bind="with: newRow">
|
||||
<td>...</td>
|
||||
<!-- ko foreach: $root.properties -->
|
||||
<td>
|
||||
<div data-bind="template: 'data-cell-edit-template'"></div>
|
||||
</td>
|
||||
<!-- /ko -->
|
||||
<td>
|
||||
<a href="#" class="btn btn-success" data-bind="click: $root.saveRow"><i class="icon-white icon-plus"></i> Add</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<input id="current-data-querystring" data-bind="value: queryString, valueUpdate: 'afterkeydown', tooltip: {placement: 'left', title: queryError, trigger: 'focus'}" type="text" placeholder="Query string" />
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="data-cell-edit-template">
|
||||
<div class="data-cell-input">
|
||||
<i class="icon-white icon-warning-sign" data-bind="tooltip: {title: $parent.c_errors()[name]}, visible: $parent.c_errors()[name]"></i>
|
||||
<!-- ko if: type === 'string' --> <input type="text" data-bind="value: $parent[name], hasfocus: $parent.c_focus() === name, event: {keydown: $parent._onKeypress}", /> <!-- /ko -->
|
||||
<!-- ko if: type === 'boolean' --> <input type="checkbox" data-bind="checked: $parent[name]" /> <!-- /ko -->
|
||||
<!-- ko if: type === 'date' --> <input type="datetime" data-bind="value: $parent[name], hasfocus: $parent.c_focus() === name, event: {keydown: $parent._onKeypress}" /> <!-- /ko -->
|
||||
<!-- ko if: type === 'number' --> <input type="number" data-bind="value: $parent[name], hasfocus: $parent.c_focus() === name, event: {keydown: $parent._onKeypress}" /> <!-- /ko -->
|
||||
<!-- ko if: type === 'password' --> <input type="password" placeholder="*******" data-bind="value: $parent[name], hasfocus: $parent.c_focus() === name, event: {keydown: $parent._onKeypress}" /> <!-- /ko -->
|
||||
<!-- ko if: type === 'object' --> {...} <!-- /ko -->
|
||||
<!-- ko if: type === 'array' --> [...] <!-- /ko -->
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
245
lib/resources/collection/dashboard/js/data.js
Normal file
245
lib/resources/collection/dashboard/js/data.js
Normal file
@@ -0,0 +1,245 @@
|
||||
(function() {
|
||||
|
||||
var resource = Context.resourceId;
|
||||
|
||||
var createRow = function(data, props, vm) {
|
||||
|
||||
var self = {};
|
||||
|
||||
function map(data, props) {
|
||||
var defaults = {id: null};
|
||||
props.forEach(function(prop) {
|
||||
defaults[prop.name] = undefined;
|
||||
});
|
||||
|
||||
data = _.defaults(data, defaults);
|
||||
|
||||
ko.mapping.fromJS(data, {}, self);
|
||||
}
|
||||
|
||||
map(data, props);
|
||||
|
||||
self.c_editing = ko.observable(false);
|
||||
self.c_focus = ko.observable();
|
||||
self.c_errors = ko.observable({});
|
||||
|
||||
self.c_formatted = function(name, type) {
|
||||
var name = ko.utils.unwrapObservable(name);
|
||||
var type = ko.utils.unwrapObservable(type);
|
||||
var value = ko.utils.unwrapObservable(self[name]);
|
||||
if (type === 'password' || typeof value === 'undefined') {
|
||||
return '...';
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
self.c_toggleEditing = function() {
|
||||
if (self.c_editing() && !self.isNew) {
|
||||
vm.revertRow(self);
|
||||
} else {
|
||||
self.c_editing(true);
|
||||
}
|
||||
};
|
||||
|
||||
self.c_remapProps = function(props) {
|
||||
var data = ko.mapping.toJS(self);
|
||||
map(data, props);
|
||||
};
|
||||
|
||||
self._onKeypress = function(data, e) {
|
||||
if (e.which == 13) {
|
||||
setTimeout(function() {
|
||||
vm.saveRow(self);
|
||||
}, 1);
|
||||
} else if (e.which == 27 && !self.isNew) {
|
||||
setTimeout(function() {
|
||||
vm.revertRow(self);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
self._onDoubleclick = function(data, e) {
|
||||
if (!self.c_editing()) {
|
||||
self.c_editing(true);
|
||||
self.c_focus(data.name);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
var create = function() {
|
||||
|
||||
var self = {
|
||||
properties: ko.observableArray()
|
||||
, collection: ko.observableArray()
|
||||
, queryString: ko.observable()
|
||||
, queryError: ko.observable("")
|
||||
, queryObj: ko.observable({})
|
||||
, isUser: Context.resourceType === "UserCollection"
|
||||
};
|
||||
|
||||
var collectionMapping = {
|
||||
'collection': {
|
||||
key: function(data) {
|
||||
return ko.utils.unwrapObservable(data.id);
|
||||
}
|
||||
, create: function(options) {
|
||||
return createRow(options.data, ko.mapping.toJS(self.properties), self)
|
||||
}
|
||||
, update: function(options) {
|
||||
if (!options.target.c_editing()) {
|
||||
return options.target;
|
||||
} else {
|
||||
return options.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.newRow = createRow({}, [], self);
|
||||
self.newRow.isNew = true;
|
||||
|
||||
self.deleteRow = function(data) {
|
||||
var row = data.id();
|
||||
dpd(resource).del(row, function(res, err) {
|
||||
if (err) return ui.error("Could not save", err.message).sticky();
|
||||
ui.notify("Deleted row", $('<a href="#">Undo</a>').click(function() {
|
||||
self.saveRow(data);
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
self.saveRow = function(data) {
|
||||
var rowData = ko.mapping.toJS(data);
|
||||
dpd(resource).post(rowData, function(res, err) {
|
||||
if (err) {
|
||||
if (err.errors) {
|
||||
data.c_errors(err.errors);
|
||||
} else {
|
||||
ui.error("Could not save", err.message).sticky();
|
||||
}
|
||||
} else {
|
||||
if (!data.id()) {
|
||||
self.properties().forEach(function(prop) {
|
||||
if (data[prop.name]) {
|
||||
data[prop.name](null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
data.c_editing(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.revertRow = function(data) {
|
||||
var row = data.id();
|
||||
dpd(resource).get(row, function(res, err) {
|
||||
if (err) return;
|
||||
ko.mapping.fromJS(res, {}, data);
|
||||
data.c_editing(false);
|
||||
});
|
||||
};
|
||||
|
||||
function loadProperties() {
|
||||
dpd('__resources').get(resource, function(res, err) {
|
||||
var props = CollectionUtil.propsToArray(res.properties);
|
||||
|
||||
if (self.isUser) {
|
||||
props.unshift({
|
||||
name: 'email'
|
||||
, type: 'string'
|
||||
, typeLabel: 'string'
|
||||
}, {
|
||||
name: 'password'
|
||||
, type: 'password'
|
||||
, typeLabel: 'password'
|
||||
});
|
||||
}
|
||||
|
||||
self.collection().forEach(function(row) {
|
||||
row.c_remapProps(props);
|
||||
});
|
||||
self.newRow.c_remapProps(props);
|
||||
|
||||
self.properties(props);
|
||||
});
|
||||
}
|
||||
|
||||
function loadCollection() {
|
||||
dpd(resource).get(self.queryObj(), function(res, err) {
|
||||
ko.mapping.fromJS({
|
||||
collection: res
|
||||
}
|
||||
, collectionMapping, self
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
dpd.on(resource + ':changed', loadCollection);
|
||||
|
||||
loadProperties();
|
||||
|
||||
ko.computed(function() {
|
||||
var queryString = self.queryString() || ''
|
||||
, queryObj = {};
|
||||
|
||||
self.queryError("");
|
||||
|
||||
if (queryString.indexOf('?') === 0) queryString = queryString.slice(1);
|
||||
|
||||
if (queryString.indexOf('{') === 0) {
|
||||
try {
|
||||
queryObj = JSON.parse(queryString);
|
||||
} catch (ex) {
|
||||
self.queryError(ex);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
var vars = queryString.split("&");
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split("=");
|
||||
queryObj[pair[0]] = unescape(pair[1]);
|
||||
}
|
||||
}
|
||||
|
||||
self.queryObj(queryObj);
|
||||
}, self).extend({throttle: 500});
|
||||
|
||||
|
||||
//Hack - this would take a fairly complex custom binding to do the right way
|
||||
setTimeout(function() {
|
||||
ko.computed(function() {
|
||||
var error = self.queryError();
|
||||
var $field = $('#current-data-querystring');
|
||||
if (error) {
|
||||
$field.attr('data-original-title', error).tooltip('fixTitle')
|
||||
.tooltip('show');
|
||||
} else {
|
||||
$field.attr('data-original-title', '').tooltip('fixTitle')
|
||||
.tooltip('hide');
|
||||
}
|
||||
}, self);
|
||||
}, 1);
|
||||
|
||||
|
||||
loadCollection();
|
||||
|
||||
self.queryObj.subscribe(function() {
|
||||
loadCollection();
|
||||
});
|
||||
|
||||
ko.applyBindings(self);
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
create();
|
||||
|
||||
})();
|
||||
@@ -224,12 +224,7 @@ function create() {
|
||||
var propertiesJson = res.properties
|
||||
, propertiesArray;
|
||||
|
||||
propertiesArray = Object.keys(propertiesJson || {}).reduce(function(prev, cur) {
|
||||
prev.push(propertiesJson[cur]);
|
||||
return prev;
|
||||
}, []).sort(function(a, b) {
|
||||
return a.order - b.order;
|
||||
});
|
||||
propertiesArray = CollectionUtil.propsToArray(propertiesJson);
|
||||
|
||||
ko.mapping.fromJS({
|
||||
properties: propertiesArray
|
||||
|
||||
18
lib/resources/collection/dashboard/js/util.js
Normal file
18
lib/resources/collection/dashboard/js/util.js
Normal file
@@ -0,0 +1,18 @@
|
||||
window.CollectionUtil = (function() {
|
||||
|
||||
var exports = {};
|
||||
|
||||
exports.propsToArray = function(propertiesJson) {
|
||||
var propertiesArray = Object.keys(propertiesJson || {}).reduce(function(prev, cur) {
|
||||
prev.push(propertiesJson[cur]);
|
||||
return prev;
|
||||
}, []).sort(function(a, b) {
|
||||
return a.order - b.order;
|
||||
});
|
||||
|
||||
return propertiesArray;
|
||||
}
|
||||
|
||||
return exports;
|
||||
|
||||
})();
|
||||
@@ -1,5 +1,5 @@
|
||||
<div id="properties" class="container tab-pane active">
|
||||
<div class="panel well stacked">
|
||||
<div id="properties">
|
||||
<div class="panel well">
|
||||
<h3>
|
||||
Properties
|
||||
</h3>
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
|
||||
<div id="property-now-what" class="alert alert-info hide" data-bind="visible: properties().length && !otherItems()">
|
||||
When you're done adding properties, you should <a href="../data" class="cta-link">add some data</a> to this collection.
|
||||
When you're done adding properties, you should <a data-bind="attr: {href: '/dashboard/' + Context.resourceId + '/data'}" class="cta-link">add some data</a> to this collection.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ Collection.dashboard = {
|
||||
'/js/lib/knockout-2.1.0.js'
|
||||
, '/js/lib/knockout.mapping.js'
|
||||
, '/js/lib/knockout-util.js'
|
||||
, '/js/util.js'
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user