jbe/bsw@0: --[[-- jbe/bsw@0: ui.form{ jbe/bsw@0: record = record, -- optional record to be used jbe/bsw@0: read_only = read_only, -- set to true, if form should be read-only (no submit button) jbe/bsw@0: external = external, -- external URL to be used as HTML form action jbe/bsw@0: module = module, -- module name to be used for HTML form action jbe/bsw@0: view = view, -- view name to be used for HTML form action jbe/bsw@0: action = action, -- action name to be used for HTML form action jbe/bsw@0: routing = { jbe/bsw@0: default = { -- default routing for called action jbe/bsw@0: mode = mode, -- "forward" or "redirect" jbe/bsw@0: module = module, -- optional module name, defaults to current module jbe/bsw@0: view = view, -- view name jbe/bsw@0: id = id, -- optional id to be passed to the view jbe/bsw@0: params = params -- optional params to be passed to the view jbe/bsw@0: }, jbe/bsw@0: ok = { ... }, -- routing when "ok" is returned by the called action jbe/bsw@0: error = { ... }, -- routing when "error" is returned by the called action jbe/bsw@0: ... = { ... } -- routing when "..." is returned by the called action jbe@12: }, jbe@12: partial = { -- parameters for partial loading, see below jbe@12: module = module, jbe@12: view = view, jbe@12: id = id, jbe@12: params = params, jbe@12: target = target jbe@12: }, jbe/bsw@0: content = function() jbe/bsw@0: ... -- code creating the contents of the form jbe/bsw@0: end jbe/bsw@0: } jbe/bsw@0: jbe/bsw@0: This functions creates a web form, which encloses the content created by the given 'content' function. When a 'record' is given, ui.field.* helper functions will be able to automatically determine field values by using the given record. If 'read_only' is set to true, then a call of ui.submit{...} will be ignored, and ui.field.* helper functions will behave differently. jbe/bsw@0: jbe@12: When passing a table as "partial" argument, AND if partial loading has been enabled by calling ui.enable_partial_loading(), then ui._partial_load_js is jbe@12: used to create an onsubmit event. The "partial" setting table is passed to ui._partial_load_js as first argument. See ui._partial_load_js(...) for jbe@12: further documentation. jbe@12: jbe/bsw@0: --]]-- jbe/bsw@0: jbe/bsw@11: local function prepare_routing_params(params, routing, default_module) jbe/bsw@11: local routing_default_given = false jbe/bsw@11: if routing then jbe/bsw@11: for status, settings in pairs(routing) do jbe/bsw@11: if status == "default" then jbe/bsw@11: routing_default_given = true jbe/bsw@11: end jbe/bsw@11: local module = settings.module or default_module or request.get_module() jbe/bsw@11: assert(settings.mode, "No mode specified in routing entry.") jbe/bsw@11: assert(settings.view, "No view specified in routing entry.") jbe/bsw@11: params["_webmcp_routing." .. status .. ".mode"] = settings.mode jbe/bsw@11: params["_webmcp_routing." .. status .. ".module"] = module jbe/bsw@11: params["_webmcp_routing." .. status .. ".view"] = settings.view jbe/bsw@11: params["_webmcp_routing." .. status .. ".id"] = settings.id jbe/bsw@11: if settings.params then jbe/bsw@11: for key, value in pairs(settings.params) do jbe/bsw@11: params["_webmcp_routing." .. status .. ".params." .. key] = value jbe/bsw@11: end jbe/bsw@11: end jbe/bsw@11: end jbe/bsw@11: end jbe/bsw@11: if not routing_default_given then jbe/bsw@11: params["_webmcp_routing.default.mode"] = "forward" jbe/bsw@11: params["_webmcp_routing.default.module"] = request.get_module() jbe/bsw@11: params["_webmcp_routing.default.view"] = request.get_view() jbe/bsw@11: end jbe/bsw@11: return params jbe/bsw@11: end jbe/bsw@11: jbe/bsw@0: function ui.form(args) jbe/bsw@0: local args = args or {} jbe/bsw@0: local slot_state = slot.get_state_table() jbe/bsw@0: local old_record = slot_state.form_record jbe/bsw@0: local old_readonly = slot_state.form_readonly jbe/bsw@0: slot_state.form_record = args.record jbe/bsw@0: if args.readonly then jbe/bsw@0: slot_state.form_readonly = true jbe/bsw@0: ui.container{ attr = args.attr, content = args.content } jbe/bsw@0: else jbe/bsw@0: slot_state.form_readonly = false jbe/bsw@0: local params = table.new(args.params) jbe/bsw@11: prepare_routing_params(params, args.routing, args.module) jbe/bsw@0: params._webmcp_csrf_secret = request.get_csrf_secret() jbe/bsw@0: local attr = table.new(args.attr) jbe/bsw@0: attr.action = encode.url{ jbe/bsw@0: external = args.external, jbe/bsw@0: module = args.module or request.get_module(), jbe/bsw@0: view = args.view, jbe/bsw@0: action = args.action, jbe/bsw@0: } jbe/bsw@0: attr.method = args.method and string.upper(args.method) or "POST" jbe/bsw@11: if ui.is_partial_loading_enabled() and args.partial then jbe/bsw@11: attr.onsubmit = slot.use_temporary(function() jbe/bsw@11: local partial_mode = "form_normal" jbe/bsw@11: if args.action then jbe/bsw@11: partial_mode = "form_action" jbe/bsw@11: slot.put( jbe/bsw@11: 'var element; ', jbe/bsw@11: 'var formElements = []; ', jbe/bsw@11: 'for (var i=0; i= 0) { ', jbe/bsw@11: 'element.parentNode.removeChild(element); ', jbe/bsw@11: '} ', jbe/bsw@11: '}' jbe/bsw@11: ) jbe/bsw@11: local routing_params = {} jbe/bsw@11: prepare_routing_params( jbe/bsw@11: routing_params, jbe/bsw@11: args.partial.routing, jbe/bsw@11: args.partial.module jbe/bsw@11: ) jbe/bsw@11: for key, value in pairs(routing_params) do jbe/bsw@11: slot.put( jbe/bsw@11: ' ', jbe/bsw@11: 'element = document.createElement("input"); ', jbe/bsw@11: 'element.setAttribute("type", "hidden"); ', jbe/bsw@11: 'element.setAttribute("name", ', encode.json(key), '); ', jbe/bsw@11: 'element.setAttribute("value", ', encode.json(value), '); ', jbe/bsw@11: 'this.appendChild(element);' jbe/bsw@11: ) jbe/bsw@11: end jbe/bsw@11: slot.put(' ') jbe/bsw@11: end jbe/bsw@11: slot.put(ui._partial_load_js(args.partial, partial_mode)) jbe/bsw@11: end) jbe/bsw@11: end jbe/bsw@0: if slot_state.form_opened then jbe/bsw@0: error("Cannot open a non-readonly form inside a non-readonly form.") jbe/bsw@0: end jbe/bsw@0: slot_state.form_opened = true jbe/bsw@0: ui.tag { jbe/bsw@0: tag = "form", jbe/bsw@0: attr = attr, jbe/bsw@0: content = function() jbe/bsw@0: if args.id then jbe/bsw@0: ui.hidden_field{ name = "_webmcp_id", value = args.id } jbe/bsw@0: end jbe/bsw@0: for key, value in pairs(params) do jbe/bsw@0: ui.hidden_field{ name = key, value = value } jbe/bsw@0: end jbe/bsw@0: if args.content then jbe/bsw@0: args.content() jbe/bsw@0: end jbe/bsw@0: end jbe/bsw@0: } jbe/bsw@0: slot_state.form_opened = false jbe/bsw@0: end jbe/bsw@0: slot_state.form_readonly = old_readonly jbe/bsw@0: slot_state.form_record = old_record jbe/bsw@0: end