UNPKG

logic-bind-model

Version:
713 lines (628 loc) 28 kB
/**** bind-command-ajax.js | BindCommand ****/ //============================================================== import { ExtendError } from 'logic-entity'; import { Util } from './util-wrap.js'; import { BaseBindCommand } from './base-bind-command.js'; // import { OUT_TYPE } from './base-bind-command.js'; // import { SCHEMA_TYPE } from './base-bind-command.js'; // import { getOptionNumber } from './base-bind-command.js'; import axios from 'axios'; var EXEC_STATE = { INIT: 0, ON_EXECUTE: 1, BEGIN: 2, VALID: 3, BIND: 4, RESULT: 5, OUTPUT: 6, END: 7, ON_EXECUTED: 8 }; var BindCommand = (function (_super) { /** * 바인드 명령 Ajax * * @constructs BindCommand * @extends BaseBindCommand * @param {BaseBindModel} p_BaseBindModel 출력 옵션 * @param {obejct | number | string} p_outputOption * @param {Entity} p_baseTable */ function BindCommand(p_BaseBindModel, p_outputOption, p_baseTable) { _super.call(this, p_BaseBindModel, p_baseTable); var config = { url: null, // 요청 경로 method: null, // 전송 방법 : GET, POST TODO: method 교체 요망 responseType: null // TODO: responseType 으로 교체 요망 }; // Object.defineProperty(this, '_ended', { value: false, writable: true, enumerable: false }); /** * config 설정값 (jquery의 config 과 동일) * * @member {Object} BindCommand#config */ Object.defineProperty(this, 'config', { get: function() { return config; }, set: function(nVal) { if (typeof nVal === 'object') { if (typeof nVal['url'] === 'string') config['url'] = nVal['url']; if (typeof nVal['method'] === 'string') config['method'] = nVal['method']; if (typeof nVal['responseType'] === 'string') config['responseType'] = nVal['responseType']; for (var prop in nVal) { if (prop === 'url' || prop === 'method' || prop === 'responseType') continue; config[prop] = nVal[prop]; } } else throw new ExtendError(/EL06161/, null, [this.constructor.name]); }, configurable: true, enumerable: true }); /** * config.url 의 값에 설정한다. * * @member {String} BindCommand#url */ Object.defineProperty(this, 'url', { get: function() { return config.url; }, set: function(nVal) { if (!(_isString(nVal))) throw new ExtendError(/EL06162/, null, [this.constructor.name]); config.url = nVal; }, configurable: true, enumerable: true }); // outputOption 설정 if (p_outputOption) this.outputOption = p_outputOption; // 예약어 등록 this.$KEYWORD = ['config', 'url']; this.$KEYWORD = ['_execValid', '_execBind', '_execOutput']; this.$KEYWORD = ['_ajaxSuccess', '_execError', '_ajaxComplete', '_ajaxCall']; } Util.inherits(BindCommand, _super); BindCommand._UNION = []; BindCommand._NS = 'Meta.Bind'; BindCommand._PARAMS = ['_model', 'outputOption', '_baseTable']; // local function function _isString(obj) { // 공백아닌 문자 여부 if (typeof obj === 'string' && obj.length > 0) return true; return false; } function _isObject(obj) { if (obj !== null && typeof obj === 'object' && !Array.isArray(obj)) return true; return false; } /** * execute() 실행시 처음으로 실행됩니다. * * @protected */ BindCommand.prototype._execBegin = function() { this.state = EXEC_STATE.ON_EXECUTE; this._model._onExecute(this._model, this); this._onExecute(this._model, this); // '실행 시작' 이벤트 발생 this.state = EXEC_STATE.BEGIN; if (typeof this.cbBegin === 'function' ) { this.cbBegin.call(this, this._model, this); } else if (typeof this._model.cbBaseBegin === 'function') { this._model.cbBaseBegin.call(this, this._model, this); } }; /** * cbValid 콜백함수를 실행하고 view(MetaView)의 유효성을 검사합니다. * * @returns {boolean} 유효성 검사 결과 * @protected */ BindCommand.prototype._execValid = function() { var result = {}; // 오류 참조 변수 var value = null; var bReturn = true; this.state = EXEC_STATE.VALID; // 콜백 검사 (valid) if (typeof this.cbValid === 'function') { bReturn = this.cbValid.call(this, this.valid, this, this._model); } else if (typeof this._model.cbBaseValid === 'function') { bReturn = this._model.cbBaseValid.call(this, this.valid, this, this._model); } // undefined 회신을 안할 경우 // bReturn = typeof bReturn !== 'boolean' ? true : bReturn; // if (bReturn === false) // valid 검사 결과 // if (!bReturn) { // this._execFail('valid 검사가 실패하였습니다.'); // return false; // } if (!bReturn) { // undefind 는 종료하지만, 실패 처리는 하지 않는다. if (bReturn === false) this._execFail('valid 검사가 실패하였습니다.'); return false; } // 아이템 검사 for(var i = 0; i < this.valid.columns.count; i++) { value = this.valid.columns[i].value; // 공백 && isNotNull = false => 검사 넘어감 // 공백 && isNotNull = true => 오류 리턴 // 값존재시 => 검사 수행 // if (value.length > 0 || this.valid.columns[i].isNotNull) { // if (typeof this.valid.columns[i].valid(value, result, 2) !== 'undefined') { result = this.valid.columns[i].valid(value); if (result) { this._execFail(result.msg); return false; } } return true; }; /** * cbBind 콜백함수를 실행하고, ajax 을 호출합니다. * * @returns {Promise} 프로미스 객체를 리턴합니다. * @protected */ BindCommand.prototype._execBind = function() { var value; var column; var config = {}; this.state = EXEC_STATE.BIND; // 기본값 못가져오는 오류 변경함 config.url = this.config.url || this._model.baseConfig.url; config.method = this.config.method || this._model.baseConfig.method; config.responseType = this.config.responseType || this._model.baseConfig.responseType; config.method = config.method.toUpperCase(); // 대문자로 변경 for (var prop in this.config) { if (typeof config[prop] !== 'undefined') continue; config[prop] = this.config[prop]; } for (var prop2 in this._model.baseConfig) { if (typeof config[prop2] !== 'undefined') continue; config[prop2] = this._model.baseConfig[prop2]; } // if (!_isObject(config.data)) config.data = {}; // for(var i = 0; i < this.bind.columns.count; i++) { // var dataName = ''; // column = this.bind.columns[i]; // value = column.value || column.default; // dataName = column.alias; // // data가 bind Column 보다 우선순위가 높음 // if (typeof config.data[dataName] === 'undefined') config.data[dataName] = value; // 별칭에 설정, 없을시 기본 name // } // axios 환경에 맞게 설정 if (config.method === 'GET') { if (!_isObject(config.params)) config.params = {}; for(var i = 0; i < this.bind.columns.count; i++) { var dataName = ''; column = this.bind.columns[i]; value = column.value || column.default; dataName = column.alias; if (!(typeof value === 'string' || typeof value === 'number')) continue; // 문자열, 숫자만 전송 // params가 bind Column 보다 우선순위가 높음 if (typeof config.params[dataName] === 'undefined') config.params[dataName] = value; // 별칭에 설정, 없을시 기본 name } } else { if (!_isObject(config.data)) config.data = {}; for(var i = 0; i < this.bind.columns.count; i++) { var dataName = ''; column = this.bind.columns[i]; value = column.value || column.default; dataName = column.alias; // data가 bind Column 보다 우선순위가 높음 if (typeof config.data[dataName] === 'undefined') config.data[dataName] = value; // 별칭에 설정, 없을시 기본 name } } // 콜백 검사 (bind) if (typeof this.cbBind === 'function') { this.cbBind.call(this, this.bind, this, config); } else if (typeof this._model.cbBaseBind === 'function') { this._model.cbBaseBind.call(this, this.bind, this, config); } return this._ajaxCall(config); // Ajax 호출 (web | node) }; /** * ajax 호출하고 성공시, cbResult 콜백함수로 결과(data)를 변경합니다. * * @param {object} p_data 데이터 * @param {object} p_res response 객체 * @returns {object} data * @protected */ BindCommand.prototype._execResult = function(p_data, p_res) { var data = p_data; this.state = EXEC_STATE.RESULT; if (typeof this.cbResult === 'function' ) { data = this.cbResult.call(this, p_data, this, p_res) || p_data; } else if (typeof this._model.cbBaseResult === 'function' ) { data = this._model.cbBaseResult.call(this, p_data, this, p_res) || p_data; } return data; }; /** * 결과 data 로 outputs ViewCollection 을 설정하고, cbOutput 콜백함수를 호출합니다. * * @param {object} p_data data * @param {object} p_res response 객체 * @protected */ BindCommand.prototype._execOutput = function(p_data, p_res) { var _this = this; var data = p_data; var option = this.outputOption.option; var index = this.outputOption.index; var schema = this.outputOption.schema; // var loadOption = (option === 1) ? 3 : (option === 2 || option === 3) ? 2 : 0; var loadOption = (option === 'ALL') ? 3 : (option === 'PICK' || option === 'VIEW') ? 2 : 0; // TODO: result 타입 검사 추가 this.state = EXEC_STATE.OUTPUT; // 1. 초기화 : opt = 1 for (var i = 0; this._outputs.count > i; i++) { if (loadOption === 1) this._outputs[i].clear(); // 전체 초기화 (item, rows) else this._outputs[i].rows.clear(); // Row 초기화 } /** * - {columns, row} * - {props: {colums, rows}, ... } * - [ {columns, rows}, ...] * - [ {props: {colums, rows} } ] = > X */ // 2-1. schema 타입에 따라 rows 로 변경 if (schema === 'AUTO') { if (_isObject(data) && typeof data['rows'] === 'undefined') { data = { 'rows': data }; } else if (Array.isArray(data) && data.length > 0) { for (var j = 0; j < data.length; j++) { if (!_isObject(data[j])) throw new ExtendError(/EL06167/, null, [j, typeof data[j]]); // 순서, 타입 } data = { 'rows': data }; // 배열인 경우 rows 로 변경 } } else if (schema === 'DATA') { if (_isObject(data)) { data = { 'rows': data }; } else if (Array.isArray(data) && data.length > 0) { // 중복 for (var k = 0; k < data.length; k++) { if (!_isObject(data[k])) throw new ExtendError(/EL06167/, null, [k, typeof data[k]]); // 순서, 타입 } data = { 'rows': data }; } } else if (schema === 'ENTITY') { if (Array.isArray(data) && data.length > 0) { var list = []; for (var o = 0; o < data.length; o++) { if (!_isObject(data[o])) throw new ExtendError(/EL06167/, null, [o, typeof data[o]]); // 순서, 타입 if (typeof data[o]['rows'] === 'undefined') { list.push({ 'rows': data[o] }); } else { list.push(data[o]); // rows 가 있는 경우 그대로 사용 } } data = list; } } // 2-2. 결과 MetaView 에 로딩 if ($isEntitySchema(data)) { // 단일 엔티티 $readOutput(data, 0, loadOption); } else if (Array.isArray(data)) { // 복수 엔티티 for (var p = 0; p < data.length; p++) { $readOutput(data[p], p, loadOption); } } else { throw new ExtendError(/EL06163/, null, [typeof data]); } // if ($isEntitySchema(data)) { // $readOutput(data, 0, loadOption); // } else { // if (Array.isArray(data)) { // for (var j = 0; j < data.length; j++) { // $readOutput(data[j], j, loadOption); // } // } else if (_isObject(data)){ // var k = 0; // for (var prop in data) { // $readOutput(data[prop], k, loadOption); // k++; // } // } else { // throw new ExtendError(/EL06163/, null, [typeof data]); // } // } // 3. 존재하는 아이템 중에 지정된 값으로 설정 if (option === 'VIEW') { if (Array.isArray(index)) { for (var m = 0; m < this._outputs.count && m < index.length; m++) { $setOutputValue(index[m], m); } } else { for (var n = 0; this._outputs.count > n; n++) { $setOutputValue(index, n); } } } // 콜백 검사 (Output) if (typeof this.cbOutput === 'function' ) { this.cbOutput.call(this, this._outputs, this, p_res); } else if (typeof this._model.cbBaseOutput === 'function' ) { this._model.cbBaseOutput.call(this, this._outputs, this, p_res); } // inner function function $isEntitySchema(target) { if (target['rows'] || target['columns'] ) return true; return false; } function $readOutput(entity, cnt, readOpt) { // var idx = cnt > 0 ? cnt - 1 : 0; // var idx = cnt - 1; if (readOpt === 3 && typeof _this._outputs[cnt] === 'undefined') { _this.newOutput(); } _this._outputs[cnt].read(entity, readOpt); } function $setOutputValue(rowIdx, i) { if (typeof rowIdx !== 'number') throw new ExtendError(/EL06164/, null, [i, typeof rowIdx]); if (_this._outputs[i].columns.count === 0) throw new ExtendError(/EL06165/, null, [i]); if (_this._outputs[i].rows.count - 1 < rowIdx) throw new ExtendError(/EL06166/, null, [i, rowIdx]); _this._outputs[i].setValue(_this._outputs[i].rows[rowIdx]); } }; /** * excute() 실행 후 마지막으로 cbEnd 콜백함수를 호출합니다. * * @param {object} p_status 상태값 * @param {object} p_res response * @protected */ BindCommand.prototype._execEnd = function(p_status, p_res) { // if (this._ended) return; // this._ended = true; try { if (this.state > 0) this.state = EXEC_STATE.END; if (typeof this.cbEnd === 'function' ) { this.cbEnd.call(this, p_status, this, p_res); } else if (typeof this._model.cbBaseEnd === 'function') { this._model.cbBaseEnd.call(this, p_status, this, p_res); } if (this.state > 0) this.state = EXEC_STATE.ON_EXECUTED; this._onExecuted(this._model, this); this._model._onExecuted(this._model, this); } catch (err) { // var msg = 'Err: _execEnd(cmd='+ this.name +') message:'+ err.message; // this._execError(msg, p_status, p_res); if (this.state > 0) this.state = this.state * -1; // this._execError.call(this, err); throw new ExtendError(/EL06168/, err, [p_status]); } }; /** * 오류 발생시 호출됩니다. (cbError 콜백함수 호출) * * @param {object} p_error 에러 객체 * @protected */ BindCommand.prototype._execError = function(p_error) { // var msg = p_error; if (this.state > 0) this.state = this.state * -1; // if (p_res && p_res.statusText) msg += ', statusText: '+ p_res.statusText; // this._model.cbError.call(this, msg, p_status, p_res); this._model.cbError.call(this, p_error); }; /** * excute() 실행시 유효성 검사가 실패하면 호출됩니다. * * @param {string} p_msg 실패 메세지 */ BindCommand.prototype._execFail = function(p_msg) { if (this.state > 0) this.state = this.state * -1; this._model.cbFail.call(this, p_msg, this.valid); }; /** * ajax 를 호출합니다. (axios) * * @param {object} p_config axios 설정 * @protected */ BindCommand.prototype._ajaxCall = function(p_config) { var _this = this; var config = {}; var lastResponse = null; // url, method, data 제외한 기타 config 설정 for (var prop in p_config) { if (prop === 'url' || prop === 'method' || prop === 'data') continue; config[prop] = p_config[prop]; } var method = p_config.method && p_config.method.toUpperCase(); var axiosCall; if (method === 'GET' || method === 'DELETE') { axiosCall = axios[method.toLowerCase()](p_config.url, config); } else if (method === 'POST' || method === 'PUT' || method === 'PATCH') { axiosCall = axios[method.toLowerCase()](p_config.url, p_config.data, config); } else { throw new ExtendError(/EL06169/, null, [p_config.method]); } return axiosCall .then(function(res) { lastResponse = res; return _this._ajaxSuccess.call(_this, res.data, res.status, res); }) .catch(function(err) { if (err.response) lastResponse = err.response; _this._execError.call(_this, err); return Promise.reject(err); }) .finally(function() { _this._execEnd.call(_this, lastResponse ? lastResponse.status : 500, lastResponse); }); }; // BindCommand.prototype._ajaxCall = function(p_config) { // var _this = this; // var config = {}; // var lastResponse = null; // for (var prop in p_config) { // if (prop === 'url' || prop === 'method' || prop === 'data') continue; // config[prop] = p_config[prop]; // } // if (p_config.method === 'GET') { // 요청 // return axios.get(p_config.url, config) // .then(function(res){ // lastResponse = res; // return _this._ajaxSuccess.call(_this, res.data, res.status, res); // }) // .catch(function(err){ // if (err.response) lastResponse = err.response; // _this._execError.call(_this, err); // return Promise.reject(err); // }) // .finally(function() { // _this._execEnd.call(_this, lastResponse.status, _this, lastResponse); // }); // } else if (p_config.method === 'DELETE') { // 삭제 // return axios.delete(p_config.url, config) // .then(function(res){ // lastResponse = res; // return _this._ajaxSuccess.call(_this, res.data, res.status, res); // }) // .catch(function(err){ // if (err.response) lastResponse = err.response; // _this._execError.call(_this, err); // return Promise.reject(err); // }) // .finally(function() { // _this._execEnd.call(_this, lastResponse.status, _this, lastResponse); // }); // } else if (p_config.method === 'POST') { // 추가 // return axios.post(p_config.url, p_config.data, config) // .then(function(res){ // lastResponse = res; // return _this._ajaxSuccess.call(_this, res.data, res.status, res); // }) // .catch(function(err){ // if (err.response) lastResponse = err.response; // _this._execError.call(_this, err); // return Promise.reject(err); // }) // .finally(function() { // _this._execEnd.call(_this, lastResponse.status, _this, lastResponse); // }); // } else if (p_config.method === 'PUT') { // 수정 // return axios.put(p_config.url, p_config.data, config) // .then(function(res){ // lastResponse = res; // return _this._ajaxSuccess.call(_this, res.data, res.status, res); // }) // .catch(function(err){ // if (err.response) lastResponse = err.response; // _this._execError.call(_this, err); // return Promise.reject(err); // }) // .finally(function() { // _this._execEnd.call(_this, lastResponse.status, _this, lastResponse); // }); // } else if (p_config.method === 'PATCH') { // 일부 수정 // return axios.patch(p_config.url, p_config.data, config) // .then(function(res){ // lastResponse = res; // return _this._ajaxSuccess.call(_this, res.data, res.status, res); // }) // .catch(function(err){ // if (err.response) lastResponse = err.response; // _this._execError.call(_this, err); // return Promise.reject(err); // }) // .finally(function() { // _this._execEnd.call(_this, lastResponse.status, _this, lastResponse); // }); // } else { // throw new ExtendError(/EL06169/, null, [p_config.method]); // } // }; /** * ajax 호출이 성공할 경우 호출됩니다. * * @param {*} p_data 데이터 * @param {*} p_status 상태값 * @param {*} p_res response * @protected */ BindCommand.prototype._ajaxSuccess = function(p_data, p_status, p_res) { var option = this.outputOption.option; var data; data = typeof p_data === 'object' ? p_data : JSON.parse(JSON.stringify(p_data)); data = this._execResult.call(this, data, p_res); if (option !== 'SEND') this._execOutput.call(this, data, p_res); return data; // 결과 데이터 반환 }; /** * 현재 객체의 guid 타입의 객체를 가져옵니다. * - 순환참조는 $ref 값으로 대체된다. * * @param {number} p_vOpt 가져오기 옵션 * - opt = 0 : 참조 구조의 객체 (_guid: Yes, $ref: Yes) * - opt = 1 : 소유 구조의 객체 (_guid: Yes, $ref: Yes) * - opt = 2 : 소유 구조의 객체 (_guid: No, $ref: No) * 객체 비교 : equal(a, b) * a.getObject(2) == b.getObject(2) * @param {object | array<object>} [p_owned] 현재 객체를 소유하는 상위 객체들 * @returns {object} */ BindCommand.prototype.getObject = function(p_vOpt, p_owned) { var obj = _super.prototype.getObject.call(this, p_vOpt, p_owned); // var vOpt = p_vOpt || 0; // var owned = p_owned ? [].concat(p_owned, obj) : [].concat(obj); obj['config'] = this.config; return obj; }; /** * 현재 객체를 초기화 후, 지정한 guid 타입의 객체를 사용하여 설정합니다. * * @param {object} p_oGuid guid 타입의 객체 * @param {object} [p_origin] 현재 객체를 설정하는 원본 guid 객체 * 기본값은 p_oGuid 객체와 동일 */ BindCommand.prototype.setObject = function(p_oGuid, p_origin) { _super.prototype.setObject.call(this, p_oGuid, p_origin); // var origin = p_origin ? p_origin : p_oGuid; // var entity; this.config = p_oGuid['config']; }; /** * command 을 실행합니다. * 실행 순서 <정상흐름> * _execBegin() >> _execValid() >> execBind() >> * [콜백] _execResult() >> _execOutput() >> _execEnd() * * @param {object | string | number} [p_outOpt] 출력 옵션 * @param {object | string} [p_config] axios 설정 또는 url * @returns {Promise} 프로미스 객체 */ BindCommand.prototype.execute = function(p_outOpt, p_config) { var _this = this; var isFail = false; // var outOpt; try { this.state = EXEC_STATE.INIT; if (p_outOpt) this.outputOption = p_outOpt; // config 설정 if (_isString(p_config)) this.url = p_config; else if (_isObject(p_config)) this.config = p_config; this._execBegin(); if (!this._execValid()) { isFail = true; // this.state = this.state * -1; this._execEnd.call(this); // return null; return Promise.resolve(null); } return this._execBind(); } catch (err) { if (this.state > 0) this.state = this.state * -1; // var msg = 'Err:execue(cmd='+ _this.name +') message:'+ err.message; // this._execError(msg); // this._execError.call(this, err); if (!isFail) this._execEnd.call(this); this._execEnd.call(this); return Promise.reject(err); // 에러 → 실패한 Promise 반환 } }; /** * execute 메소드 별칭 */ BindCommand.prototype.exec = BindCommand.prototype.execute; return BindCommand; }(BaseBindCommand)); export default BindCommand; export { BindCommand };