dataset ui binding 수정, upload-support 추가

master
mjkhan21 4 months ago
parent 92c3f11c08
commit 8543fe2c5d

@ -287,24 +287,6 @@ var json = {
}
};
function upload(options) {
options.enctype = "multipart/form-data";
options.processData = options.contentType = false;
var data = options.data,
formData = new FormData();
for (var key in data) {
var val = data[key];
if (!Array.isArray(val))
formData.append(key, val);
else {
for (var i = 0; i < val.length; ++i)
formData.append(key, val[i]);
}
}
options.data = formData;
ajax.post(options);
}
$.fn.onEnterPress = function(handler) {
return this.each(function(){
$(this).keypress(function(evt){
@ -693,11 +675,13 @@ $.fn.setCurrentRow = function(val) {
class FormFields {
constructor(selector) {
this.form = document.querySelector(this.selector = selector);
this.children = ["input", "select", "textarea"].map(tag => this.selector + " " + tag).join(",");
this.children = ["input", "select", "textarea", "img", "span"].map(tag => this.selector + " " + tag).join(",");
this.datamap = [];
this.ctrl = null;
}
set(ctrl, obj) {
this.ctrl = ctrl;
let setChanged = evt => {
let input = evt.target,
name = input.getAttribute("data-map"),
@ -724,6 +708,8 @@ class FormFields {
option.selected = option.value == value;
}
break;
case "span": input.innerHTML = value; break;
case "img": input.src = value; break;
default: input.value = ifEmpty(value, ""); break;
}
input.addEventListener("change", setChanged);
@ -731,20 +717,30 @@ class FormFields {
}
get() {
let obj = {};
let obj = {},
current = this.ctrl.getCurrent();
if (!current)
return obj;
document.querySelectorAll(this.children).forEach(input => {
let property = input.name || input.id;
let value = input.value;
let field = input.getAttribute("data-map");
if (!field) return;
let property = input.name || input.id,
value = input.value;
value = current[field];
obj[property] = value;
return;
if ("radio" == input.type) {
if (input.checked)
obj[property] = value;
} else {
obj[property] = value;
}
// log(property, value, "radio" == input.type ? "radio" : "", input.checked ? "checked" : "");
});
return obj;
//return Object.fromEntries(new FormData(this.form).entries());
}
};
@ -806,63 +802,3 @@ function inputsInRange(fromSource, toSource) {
function ignore() {
console.log.apply(console, arguments);
}
function fileInput(conf) {
conf = conf || {};
var name = conf.name || "upload",
tag = "<input type='file' name='{name}'{multiple}{accept} style='display:none;'>"
.replace(/{name}/, name)
.replace(/{multiple}/, conf.multiple ? " multiple" : "")
.replace(/{accept}/, conf.accept ? " accept='" + conf.accept + "'" : ""),
obj = {
name:name,
input:null,
files:[],
select:function(onSelect) {
if (!obj.input) {
var input = obj.input = $(tag);
$("body").append(input);
input.change(function() {
var files = $(this).get(0).files,
length = files.length,
selected = [];
for (var i = 0; i < length; ++i) {
var file = files[i];
file.id = name + (obj.files.length);
selected.push(file);
obj.files.push(file);
}
onSelect(selected);
})
}
obj.input.click();
},
getFiles:function() {
return obj.files;
},
remove:function(id) {
if (!id) return;
var files = obj.files;
for (var i = 0; i < files.length; ++i) {
var file = files[i];
if (id == file.id) {
files.splice(i, 1);
break;
}
}
},
clear:function() {
if (obj.input)
$(obj.input).remove();
obj.input = null;
obj.files.forEach(function(file) {delete file;});
obj.files = [];
}
};
return obj;
}

@ -383,7 +383,12 @@ class Dataset {
this.conf = notEmpty(conf, "conf is required but missing");
notEmpty(conf.keymapper, "keymapper is required but missing");
let keymapper = conf.keymapper;
conf.keymapper = info => {
return keymapper(info) || info._tmpKey;
};
this._formats = new ValueFormat(conf.formats);
this._dataBinder = dataBinder.create(this, conf.inputs);
if (!conf.trace)
this.log = () => {};
@ -463,6 +468,8 @@ class Dataset {
*/
getData(key, option) {
let item = this._byKeys["key-" + key];
if (!item)
item = this.getTempItem();
if (!item || item.unreachable)
return null;
return "item" == option ? item : item.data;
@ -481,7 +488,6 @@ class Dataset {
* @returns {Dataset} the Dataset
*/
setData(obj) {
let state = this.state;
this._byKeys = {};
this._current = null;
@ -503,8 +509,7 @@ class Dataset {
});
*/
this.onDatasetChange(obj);
this.setState(obj.state || state);
// this.setState(!Array.isArray(obj) ? obj.state : state);
this.setState(obj.state);
this.onDirtiesChange(this.dirty);
return this;
@ -602,6 +607,14 @@ class Dataset {
return "item" == option ? current : current.data;
}
getTempItem(current) {
let found = this._items.filter(item => item.data._tmpKey);
found = found.length > 0 ? found[0] : null;
if (current)
this.setCurrent();
return found;
}
/**Sets the user data as current that is associated with the key.
* @param {string} key key to a user data
* After the data is set, the method
@ -616,8 +629,10 @@ class Dataset {
this._current = item;
if (diff || fire)
if (diff || fire) {
this._dataBinder.onCurrentChange(item);
this.onCurrentChange(item);
}
}
/**Returns the Dataset's current state in an object.
@ -835,6 +850,11 @@ class Dataset {
append(data) {
if (!data) return this;
let found = this.getTempItem(true);
if (found) {
return found;
}
let notDirty = !this.dirty,
array = Array.isArray(data) ? data : [data];
array.forEach(e => {
@ -842,6 +862,8 @@ class Dataset {
this._items.push(item);
let key = this.getKey(e);
if (!key)
e._tmpKey = key = new Date().getTime()
this._byKeys["key-" + key] = item;
item.state = "added";
});
@ -907,11 +929,14 @@ class Dataset {
if (changed.length > 0) {
if (!item.state)
item.state = "modified";
this._dataBinder.onModify(changed, item);
this.onModify(changed, item, current);
if (notDirty)
this.onDirtiesChange(true);
} else if (revert) {
this.onModify(Object.getOwnPropertyNames(data), item, current);
changed = Object.getOwnPropertyNames(data);
this._dataBinder.onModify(changed, item);
this.onModify(changed, item, current);
}
return this;
@ -948,8 +973,7 @@ class Dataset {
replace(replacement) {
if (isEmpty(replacement)) return this;
let before = this.dirty,
replacements = Array.isArray(replacement) ? replacement : [replacement],
let replacements = Array.isArray(replacement) ? replacement : [replacement],
replacing = [];
replacements.forEach(obj => {
let data = obj.data;
@ -977,10 +1001,7 @@ class Dataset {
replacing.push(newItem);
});
this.onReplace(replacing);
let after = this.dirty;
if (before != after)
this.onDirtiesChange(after);
this.setState();
return this;
}
@ -1109,7 +1130,9 @@ class Dataset {
* @returns {array} array of strings converted from the template using the property values of the user data
*/
inStrings(template, formatter) {
return this.getDataset("item")
let dataset = this.getDataset("item");
return dataset.filter(item => !item.data._tmpKey)
.map(item => item.inString(template, formatter));
}
@ -1229,21 +1252,23 @@ class DatasetControl {
notEmpty(conf.keymapper, "keymapper");
this.prefix = conf.prefix;
this.prefixName = conf.prefixName;
this._doctx = conf.doctx || "";
this.infoSize = conf.infoSize;
this.appendData = conf.appendData;
this.query = {};
conf.onDatasetChange = obj => this.onDatasetChange(obj),
conf.onCurrentChange = item => this.onCurrentChange(item),
conf.onSelectionChange = selected => this.onSelectionChange(selected),
conf.onAppend = items => this.onAppend(items),
conf.onDatasetChange = obj => this.onDatasetChange(obj);
conf.onCurrentChange = item => this.onCurrentChange(item);
conf.onSelectionChange = selected => this.onSelectionChange(selected);
conf.onAppend = items => this.onAppend(items);
conf.onModify = (props, modified, current) => {
let info = this.dataset.getCurrent("item");
if (!info || "added" == info.state)
return;
this.onModify(props, modified, current);
}
};
conf.onReplace = obj => this.onReplace(obj);
this.dataset = new Dataset(conf);
@ -1269,14 +1294,18 @@ class DatasetControl {
this._load();
}
_load() {
_load(state, prev) {
if (!this.query.pageNum)
this.query.pageNum = 1;
if (prev)
this.query.pageNum = Math.max(1, this.query.pageNum - 1);
ajax.get({
url:this.urls.load,
data:this.query,
success:resp => {
if (!prev)
resp.state = state;
if (!this.appendData || this.query.pageNum == 1)
this.setData(resp);
else {
@ -1286,6 +1315,10 @@ class DatasetControl {
});
}
reload(prev) {
this._load(this.dataset.state, prev);
}
download(type) {
this.query.download = type || "xls";
let query = toQuery(this.query);
@ -1309,6 +1342,10 @@ class DatasetControl {
return this.dataset.getCurrent(option);
}
currentToObject() {
return this.dataset._dataBinder.toObject(this.getCurrent("item"));
}
setCurrent(key) {
this.dataset.setCurrent(key);
}
@ -1326,7 +1363,6 @@ class DatasetControl {
}
getInfo(params) {
let info = this.dataset.getCurrent("item");
if (this.urls.getInfo)
ajax.get({
url:this.urls.getInfo,
@ -1339,12 +1375,16 @@ class DatasetControl {
title: this.prefixName + " 정보",
content:resp,
size:this.infoSize,
init:() => this.setInfo(info)
init:() => {
let current = this.getCurrent("item");
this.dataset._dataBinder.onCurrentChange(current);
this.setInfo(current);
}
});
}
});
else
this.setInfo(info);
this.setInfo(this.getCurrent("item"));
}
setInfo(info) {}
@ -1370,6 +1410,10 @@ class DatasetControl {
debug("on modify", props, "modified", modified, "current", current);
}
onReplace(replacing) {
debug("on replace", replacing);
}
save(info) {
if (!info) return;
let item = this.getCurrent("item"),
@ -1386,7 +1430,7 @@ class DatasetControl {
if (resp.saved) {
dialog.alert("저장됐습니다.");
dialog.close(this.prefixed("dialog"));
this._load();
this.reload();
}
}
@ -1408,41 +1452,108 @@ class DatasetControl {
onRemove(selected, resp) {
if (resp.saved)
this._load();
this.reload(selected.length == this.dataset.length);
}
bindInputs(obj, selector) {
let inputs = ["input", "select", "textarea"].map(tag => selector + " " + tag).join(","),
setChanged = evt => {
let input = evt.target,
name = input.getAttribute("data-map"),
val = input.value;
this.setValue(name, val);
};
_selector(selector) {
let doctx = this._doctx;
return doctx ? "*[data-doctx='" + doctx + "'] " + selector : selector;
}
document.querySelectorAll(inputs).forEach(input => {
let prop = input.getAttribute("data-map")
|| input.name
|| input.id;
if (!prop) return;
input.removeEventListener("change", setChanged);
let dataItem = obj instanceof DataItem,
value = dataItem ? obj.getValue(prop) : obj[prop],
inputType = (input.type || input.tagName || "").toLowerCase();
switch (inputType) {
case "radio": input.checked = value && value == input.value; break;
case "checkbox": input.checked = value && value == input.value; break;
case "select":
for(let option of input.options) {
option.selected = option.value == value;
}
break;
default: input.value = ifEmpty(value, ""); break;
}
input.addEventListener("change", setChanged);
});
querySelector(selector) {
return document.querySelector(this._selector(selector));
}
querySelectorAll(selector) {
return document.querySelectorAll(this._selector(selector));
}
}
var dataBinder = {
type: input => {
return input ? (input.getAttribute("type") || input.tagName).toLowerCase() : "";
},
dataMap: input => {
return input.getAttribute("data-map");
},
property: input => {
return input.name || input.id;
},
setValue: (input, value) => {
value = value || "";
switch (dataBinder.type(input)) {
case "radio":
case "checkbox": input.checked = value && value == input.value; break;
case "select":
for (let option of input.options) {
option.selected = option.value == value;
}
break;
case "img":
if (!(input.src || "").endsWith(value))
input.src = value; break;
default:
if (input.value !== undefined)
input.value = value;
else
input.innerHTML = value;
}
},
create: (dataset, selector) => {
if (!dataset || isEmpty(selector))
return {
onCurrentChange: item => {},
onModify: (changed, item) => {},
toObject: () => null
};
let obj = {
selector: selector,
setChanged: (evt) => {
let input = evt.target,
prop = dataBinder.dataMap(input),
val = input.value;
dataset.setValue(prop, val);
}
};
obj.onCurrentChange = (current) => {
if (!current) return;
document.querySelectorAll(obj.selector).forEach(input => {
let prop = dataBinder.dataMap(input);
if (!prop) return;
input.removeEventListener("change", obj.setChanged);
dataBinder.setValue(input, current.getValue(prop));
input.addEventListener("change", obj.setChanged);
});
};
obj.onModify = (changed, item) => {
document.querySelectorAll(obj.selector).forEach(input => {
let prop = dataBinder.dataMap(input);
if (!changed.includes(prop)) return;
dataBinder.setValue(input, item.getValue(prop));
});
};
obj.toObject = (item) => {
let result = {};
document.querySelectorAll(obj.selector).forEach(input => {
let dataMap = dataBinder.dataMap(input),
property = dataBinder.property(input) || dataMap;
result[property] = item.data[dataMap];
});
return result;
}
return obj;
},
};

@ -255,7 +255,7 @@ class TabControl extends Dataset {
}
}
open(url) {
open(url, query) {
if (!url
|| url.startsWith("javascript:void")
|| url == this.sticky.url)
@ -265,8 +265,9 @@ class TabControl extends Dataset {
if (tab)
return this.setCurrent(tab.data.index);
let str = url + (query ? "?" + query : "");
ajax.get({
url: wctx.url(url),
url: wctx.url(str),
success: resp => {
let menu = this.getMenu(url);
if (!menu)
@ -280,7 +281,7 @@ class TabControl extends Dataset {
menu.content = resp;
this.addData([menu]);
this.open(url);
this.open(url, query);
}
});
}

@ -0,0 +1,110 @@
function upload(options) {
options.enctype = "multipart/form-data";
options.processData = options.contentType = false;
var data = options.data,
formData = new FormData();
for (var key in data) {
var val = data[key];
if (!Array.isArray(val))
formData.append(key, val);
else {
for (var i = 0; i < val.length; ++i)
formData.append(key, val[i]);
}
}
options.data = formData;
ajax.post(options);
}
function fileInput(conf) {
conf = conf || {};
var name = conf.name || "upload",
tag = "<input type='file' name='{name}'{multiple}{accept} style='display:none;'>"
.replace(/{name}/, name)
.replace(/{multiple}/, conf.multiple ? " multiple" : "")
.replace(/{accept}/, conf.accept ? " accept='" + conf.accept + "'" : ""),
obj = {
name:name,
input:null,
files:[],
select:function(onSelect) {
if (!obj.input) {
var input = obj.input = $(tag);
$("body").append(input);
input.change(function() {
var files = $(this).get(0).files,
length = files.length,
selected = [];
for (var i = 0; i < length; ++i) {
var file = files[i];
file.id = name + (obj.files.length);
selected.push(file);
obj.files.push(file);
}
if (onSelect)
onSelect(selected);
})
}
obj.input.click();
},
getFiles:function() {
return obj.files;
},
getURLs:function() {
try {
return obj.files.map(file => (window.URL || window.webkitURL).createObjectURL(file));
} catch (e) {
return [];
}
},
remove:function(id) {
if (!id) return;
var files = obj.files;
for (var i = 0; i < files.length; ++i) {
var file = files[i];
if (id == file.id) {
files.splice(i, 1);
break;
}
}
},
clear:function() {
if (obj.input)
$(obj.input).remove();
obj.input = null;
obj.files.forEach(function(file) {delete file;});
obj.files = [];
}
};
return obj;
}
function uploadSupport(selector) {
let input = $(selector);
if (!input)
throw "Element not found: " + selector;
let fileset = new Dataset({
keymapper: info => info.id
});
input.change(function() {
var files = $(this).get(0).files,
length = files.length,
array = []
for (var i = 0; i < length; ++i) {
var file = files[i];
file.id = "file-" + new Date().getTime() + "-" + i;
file.url = (window.URL || window.webkitURL).createObjectURL(file);
array.push(file);
}
fileset.setData(array);
});
return fileset;
}
Loading…
Cancel
Save