AjaxContent.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. (function (global, p5VendorJs) {
  2. if (!p5VendorJs.React) throw "Missing React"
  3. if (!p5VendorJs.ReactDOM) throw "Missing ReactDOM"
  4. if (!p5VendorJs.createReactClass) throw "Missing createReactClass"
  5. var DBG = DBG || 0;
  6. var DBG1 = 1;
  7. /**
  8. * Loads props.url after mount.
  9. * If props.url changed: clear, start loding new url.
  10. * If props.url is empty: clear.
  11. *
  12. * Clear: show props.emptyUrlContent || ''
  13. *
  14. * @usage:
  15. * ReactDOM.render(
  16. * h(P5UI__AjaxContent, {
  17. * url: '',
  18. * }),
  19. * document.getElementById(HTML_ID)
  20. * )
  21. *
  22. * props.url: url | null
  23. * props.loading: html | null
  24. * props.emptyContent: html | null - TODO: show when url is not defined
  25. *
  26. */
  27. // var React = window.p5VendorJs.React;
  28. var createReactClass = window.p5VendorJs.createReactClass;
  29. var h = window.p5VendorJs.React.createElement;
  30. // componentDidMount: function () { console.log('MyWidget::componentDidMount...'); },
  31. // componentWillReceiveProps: function (nextProps) { console.log('MyWidget::componentWillReceiveProps(nextProps)...', nextProps); },
  32. // shouldComponentUpdate: function (nextProps, nextState) { console.log('MyWidget::shouldComponentUpdate(nextProps, nextState)...', nextProps, nextState); },
  33. // componentWillUpdate: function (nextProps, nextState) { console.log('MyWidget::componentWillUpdate(nextProps, nextState)...', nextProps, nextState); },
  34. // componentDidUpdate: function (prevProps, prevState) { console.log('MyWidget::componentDidUpdate(prevProps, prevState)...', prevProps, prevState); },
  35. // componentWillUnmount: function () { console.log('MyWidget::componentWillUnmount...'); },
  36. var P5UI__AjaxContent = createReactClass({
  37. _refContentEl: null,
  38. setContentElRef: function (el) {
  39. this._refContentEl = el;
  40. },
  41. getInitialState: function () {
  42. return {
  43. isLoading: false,
  44. postData: null, // if method = 'POST'
  45. response_type: null,
  46. response_msg: null,
  47. response_body: null,
  48. };
  49. },
  50. componentDidMount() {
  51. if (this.props.url) this._fetchContent();
  52. },
  53. componentDidUpdate(prevProps, prevState) {
  54. if (prevProps.url !== this.props.url) {
  55. this._fetchContent();
  56. }
  57. },
  58. _fetchContent: function () {
  59. this.setState({
  60. isLoading: true
  61. });
  62. // var _setState = this.setState.bind(this);
  63. var _handleFetchResponseSuccess = this._handleFetchResponseSuccess.bind(this);
  64. var _handleFetchResponseFail = this._handleFetchResponseFail.bind(this);
  65. var _handleFetchResponseJson = this._handleFetchResponseJson.bind(this);
  66. window.fetch(this.props.url,
  67. (!this.props.postData)
  68. ? {
  69. method: 'GET',
  70. headers: { 'Content-Type': 'application/json' },
  71. credentials: 'same-origin',
  72. }
  73. : {
  74. method: 'POST',
  75. headers: { 'Content-Type': 'application/json' },
  76. credentials: 'same-origin',
  77. body: this.props.postData, // JSON.stringify(this.props.postData)
  78. }
  79. ).then(function (response) {
  80. DBG1 && console.log("DBG:response", {
  81. status: response.status,
  82. response: response
  83. });
  84. response.text()
  85. .then(response.ok ? _handleFetchResponseSuccess : _handleFetchResponseFail)
  86. .catch(function (err) {
  87. _handleFetchResponseJson({
  88. response_type: 'error',
  89. response_msg: "" + err,
  90. response_body: null,
  91. })
  92. })
  93. }).then(function (data) {
  94. DBG1 && console.warn("DBG:response data", { data });
  95. }).catch(function (err) {
  96. DBG1 && console.log("Response Error #2:")
  97. DBG1 && console.error(err)
  98. })
  99. },
  100. _handleFetchResponseFail: function (responseText) {
  101. DBG1 && console.warn("DBG:response fail responseText", { responseText });
  102. if (!responseText) throw "Error";
  103. this._handleFetchResponseText(false, responseText)
  104. },
  105. _handleFetchResponseSuccess: function (responseText) {
  106. DBG1 && console.warn("DBG:response success responseText", { responseText });
  107. this._handleFetchResponseText(true, responseText)
  108. },
  109. _handleFetchResponseText: function (isOk, responseText) {
  110. DBG1 && console.warn("DBG:response _handleFetchResponseText", { isOk, responseText });
  111. if ('{' !== responseText.substr(0, 1)) throw "Response Parse Error: Expected json.";
  112. try {
  113. var jsonResponse = JSON.parse(responseText);
  114. if (!jsonResponse.msg) throw "Response Parse Error: Expected json with msg.";
  115. if (!isOk) throw jsonResponse.msg; // TODO: throw or just show alert for user
  116. this._handleFetchResponseJson(jsonResponse)
  117. } catch (err) {
  118. throw "Response Parse Error: " + err;
  119. }
  120. },
  121. _handleFetchResponseJson: function (jsonResponse) {
  122. DBG1 && console.warn("DBG:response _handleFetchResponseJson", { jsonResponse });
  123. this.setState({
  124. isLoading: false,
  125. response_type: jsonResponse.type,
  126. response_msg: jsonResponse.msg,
  127. response_body: jsonResponse.body,
  128. })
  129. },
  130. shouldComponentUpdate: function (nextProps, nextState) {
  131. DBG1 && console.warn("DBG:response shouldComponentUpdate", {
  132. props: this.props, nextProps,
  133. state: this.state, nextState,
  134. });
  135. if (!this.state.isLoading && nextState.isLoading) {
  136. doShowLoading(this.props, this._refContentEl);
  137. }
  138. if (!nextState.isLoading && nextState.response_type) {
  139. doParseResponseBody(nextState.response_type, nextState.response_msg, nextState.response_body, this._refContentEl);
  140. }
  141. return false; // render only once!
  142. },
  143. render: function () {
  144. DBG1 && console.log('DBG:render (TODO: render only once!)', { options: this.state.options });
  145. return h('div', {
  146. ref: this.setContentElRef,
  147. // dangerouslySetInnerHTML: { __html: this.renderContentHtml() }
  148. });
  149. }
  150. });
  151. function doParseResponseBody(type, msg, body, rootNode) {
  152. switch (type) {
  153. case 'error': return doNotify('error', (msg || "Wystąpił błąd"), rootNode);
  154. case 'success': return doShowContent(body, rootNode);
  155. default: return doNotify('warning', msg, rootNode);
  156. }
  157. }
  158. function doShowLoading(props, rootNode) {
  159. DBG1 && console.log('DBG:doShowLoading', { rootNode });
  160. if (!rootNode) return;
  161. rootNode.innerHTML = props.loading || '<p>Loading ...</p>';
  162. // DBG && rootNode.innerHTML += "\n" + '<pre>' + "props: " + JSON.stringify(props, null, 4) + '</pre>';
  163. }
  164. function doShowContent(body, rootNode) {
  165. DBG1 && console.log('DBG:doShowContent', { rootNode });
  166. if (!rootNode) return;
  167. if (body.reactNode) return p5UI__buildDom(body.reactNode, rootNode);
  168. if (body.html) return doShowHtmlContent(body.html, rootNode);
  169. if (typeof body === 'string' || body instanceof String) return doShowHtmlContent(body, rootNode);
  170. else {
  171. // TODO: if DBG
  172. rootNode.innerHTML = '<pre>' + JSON.stringify(body, null, 4) + '</pre>';
  173. }
  174. }
  175. function doShowHtmlContent(htmlContent, node) {
  176. node.innerHTML = htmlContent;
  177. }
  178. function doNotify(type, msg, rootNode) {
  179. // TODO: if (NOTIFY_USER_MODE) // 'inline' | 'notify'
  180. type = ('error' == type) ? 'danger' : type;
  181. rootNode.innerHTML = '<div class="alert alert-' + type + '" style="margin-bottom:0">' + msg + '</div>';
  182. }
  183. global.P5UI__AjaxContent = P5UI__AjaxContent;
  184. })(window, window.p5VendorJs);