From 953a3353cdfd661918b67eab7c5467ccd61236ae Mon Sep 17 00:00:00 2001 From: Sebastiano Giacomini <92300303+Sebastiano-G@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:53:31 +0200 Subject: [PATCH] #10 Min fix: same subtemplate for multiple fields --- app.py | 24 +- forms.py | 10 +- static/js/form.js | 22 +- static/js/intermediateTemplates.js | 429 ++++++++++++++--------------- 4 files changed, 252 insertions(+), 233 deletions(-) diff --git a/app.py b/app.py index 359f1c0..e6745b1 100644 --- a/app.py +++ b/app.py @@ -612,7 +612,7 @@ def POST(self, name): # load the template selected by the user if 'res_name' in recordData: if recordData.res_name != 'None': - f = forms.get_form(recordData.res_name) + f = forms.get_form(recordData.res_name,processed_templates=[]) query_templates = u.get_query_templates(recordData.res_name) extractor = u.has_extractor(recordData.res_name) return render.record(record_form=f, pageID=name, user=user, alert=block_user, @@ -1108,10 +1108,11 @@ def GET(self, name): results_by_class[res_type] = {'class':res_class, 'results':[res_uri]} count = len(appears_in) + map_coordinates = (queries.geonames_geocoding(uri)) if uri.startswith("https://sws.geonames.org/") else None return render.term(user=session['username'], data=data, count=count, is_git_auth=is_git_auth,project=conf.myProject,base=conf.base, - uri=uri,name=name,results=results_by_class) + uri=uri,name=name,results=results_by_class,map=map_coordinates) def POST(self,name): """ controlled vocabulary term web page @@ -1392,7 +1393,24 @@ def GET(self): count_result = [result["count"]["value"] for result in query_results["results"]["bindings"]] count = int(count_result[0]) if len(count_result) > 0 else 0 counter["count"] = count - else: + elif chart["type"] == "map": + chart_id = str(time.time()).replace('.','-') + query_results = queries.hello_blazegraph(chart["query"])["results"]["bindings"] + stats_result = [] + for result in query_results: + geonames = result["geonames"]["value"] + lat, long = queries.geonames_geocoding(geonames) + i = 0 + while i < int(result["count"]["value"]): + stats_result.append({ + "title" : result["title"]["value"], + "latitude": lat, + "longitude": long + }) + i += 1 + chart["stats"] = json.dumps(stats_result) + chart["info"] = chart_id + elif chart["type"] == "chart": chart_id = str(time.time()).replace('.','-') stats_query = chart["query"] x_var, x_name = chart["x-axis"].split(",",1) diff --git a/forms.py b/forms.py index 2b1de31..c820fd4 100644 --- a/forms.py +++ b/forms.py @@ -30,7 +30,7 @@ def parse_config_variables(text:str, conf): text = text.replace(k, v) return text -def get_form(json_form, from_dict=False, supertemplate=False): +def get_form(json_form, from_dict=False, supertemplate=False, processed_templates=[]): """ read config in 'template-(.*).json' and return a webpy form """ import io if from_dict == False: @@ -52,6 +52,7 @@ def get_form(json_form, from_dict=False, supertemplate=False): res_class = [t["type"] for t in tpl_list if t["template"] == json_form] res_class = "; ".join(res_class[0]) if len(res_class) > 0 else "none" + processed_templates.append(json_form) for field in fields: if 'hidden' in field and field['hidden'] == 'False': # do not include hidden fields @@ -89,7 +90,7 @@ def get_form(json_form, from_dict=False, supertemplate=False): # dropdown dropdown_values = [(k,v) for k,v in field['values'].items()] if 'values' in field else None # subtemplate - data_supertemplate = supertemplate if supertemplate else 'None' + data_supertemplate = 'True' if supertemplate else 'None' # Text box if field['type'] == 'Textbox' and field['value'] == 'Literal': @@ -247,9 +248,10 @@ def get_form(json_form, from_dict=False, supertemplate=False): for imported_template in field['import_subtemplate']: template_dict = next(t for t in tpl_list if t["template"] == imported_template) resource_class = template_dict['type'] + resource_class_string = "; ".join(resource_class) resource_name = template_dict['name'] - dropdown_templates.append((resource_class,resource_name)) - params = params + get_form(imported_template, supertemplate=myid) + dropdown_templates.append((resource_class_string,resource_name)) + params = params + get_form(imported_template, supertemplate=True, processed_templates=processed_templates) if imported_template not in processed_templates else params params = params + (form.Dropdown(myid, description = description, args=dropdown_templates, diff --git a/static/js/form.js b/static/js/form.js index 6f6a886..5732a87 100644 --- a/static/js/form.js +++ b/static/js/form.js @@ -293,9 +293,9 @@ function searchCatalogueAdvanced(searchterm) { } // search catalogue's records belonging to a desired class -function searchCatalogueByClass(searchterm) { - +function searchCatalogueByClass(searchterm,fieldId,singleValue) { // get the required class + console.log(fieldId) var resource_class = $('#'+searchterm).attr('data-class'); var resource_classes = resource_class.split(';').map(cls => cls.trim()).filter(cls => cls !== ""); var class_triples = resource_classes.map(cls => `?s a <${cls}> .`).join(" "); @@ -309,7 +309,6 @@ function searchCatalogueByClass(searchterm) { // they must belong to the same required class var yet_to_save_keys = []; var yet_to_save_resources = []; - console.log('.disambiguate[data-class="' + resource_class + '"]:not([data-subform="'+dataSubform+'"])') $('.disambiguate[data-class="' + resource_class + '"]:not([data-subform="'+dataSubform+'"]').each(function() { yet_to_save_keys.push($(this).val()); var key_id = $(this).attr('id'); @@ -385,12 +384,13 @@ function searchCatalogueByClass(searchterm) { \ \ "); - - if ($('[name="'+subrecordBase+'-subrecords"]').length) { - $('[name="'+subrecordBase+'-subrecords"]').val($('[name="'+subrecordBase+'-subrecords"]').val()+","+oldID+";"+oldLabel); + + if ($('[name="'+fieldId+'-subrecords"]').length && !singleValue) { + $('[name="'+fieldId+'-subrecords"]').val($('[name="'+fieldId+'-subrecords"]').val()+","+oldID+";"+oldLabel); } else { - const new_sub = $("") - $('#recordForm, #modifyForm').append(new_sub) + $('[name="'+fieldId+'-subrecords"]').remove(); + const new_sub = $("") + $('#recordForm, #modifyForm').append(new_sub) } }); @@ -421,10 +421,10 @@ function searchCatalogueByClass(searchterm) { \ "); - if ($('[name="'+subrecordBase+'-subrecords"]').length) { - $('[name="'+subrecordBase+'-subrecords"]').val($('[name="'+subrecordBase+'-subrecords"]').val()+","+target+";"+label); + if ($('[name="'+fieldId+'-subrecords"]').length) { + $('[name="'+fieldId+'-subrecords"]').val($('[name="'+fieldId+'-subrecords"]').val()+","+target+";"+label); } else { - const new_sub = $("") + const new_sub = $("") $('#recordForm, #modifyForm').append(new_sub) } }); diff --git a/static/js/intermediateTemplates.js b/static/js/intermediateTemplates.js index 2416861..d865982 100644 --- a/static/js/intermediateTemplates.js +++ b/static/js/intermediateTemplates.js @@ -11,69 +11,62 @@ management of the Intermediate Templates feature. // Start setting Subtemplates' fields $(document).ready(function() { - // display subtemplates input fields - $("[data-subtemplate][data-supertemplate='None']").each(function() { + $("[data-supertemplate]:not([data-supertemplate='None'])").each(function() { // this code applies only to first-level subtemplates // i.e.: subtemplates input fields within subrecords are not made ready in advance - var dataSubtemplate = $(this).attr('data-subtemplate'); - var subtemplateFields = $("[data-supertemplate='"+dataSubtemplate+"']"); - console.log(subtemplateFields) - subtemplateFields.addClass('original-subtemplate'); - subtemplateFields.parent().parent().hide(); - prepareSubtemplateForms($(this)); + $(this).addClass('original-subtemplate'); + $(this).parent().parent().hide(); }); - $("[data-supertemplate]:not([data-supertemplate='None'], [data-subtemplate])").each(function() { + // display subtemplates input fields + $("[data-subtemplate][data-supertemplate='None']").each(function() { // this code applies only to first-level subtemplates // i.e.: subtemplates input fields within subrecords are not made ready in advance - $(this).addClass('original-subtemplate'); - $(this).parent().parent().hide(); + prepareSubtemplateForms($(this)); }); - }) // Make Subtemplates' fields available function prepareSubtemplateForms(element) { - console.log($(element)) + $(element).hide(); - var subtemplateFieldId = $(element).attr('id'); - var oneValue = $(element).hasClass('oneValue'); - var allowDataReuse = $(element).hasClass('allowDataReuse'); - - // get the display name assigned to the 'Subtemplate' field and prepare a button for adding new subrecords - var fieldName = $(element).parent().prev().text(); - const createSubrecordBtn = $('playlist_add Define a new'+fieldName+''); - createSubrecordBtn.on('click', function() { - createSubrecord(subtemplateFieldId,fieldName,createSubrecordBtn,dataReuse=allowDataReuse) + var subtemplateFieldId = $(element).attr("id"); // id of the 'Subtemplate' field + var oneValue = $(element).hasClass("oneValue"); // the number of accepted values for this field + var allowDataReuse = $(element).hasClass("allowDataReuse"); + + // get the Label assigned to the 'Subtemplate' field and prepare a button for adding new subrecords + var label = $(element).parent().prev().text(); + const createSubrecordBtn = $("playlist_add Define a new"+label+""); + createSubrecordBtn.on("click", function() { + createSubrecord(subtemplateFieldId,label,createSubrecordBtn,dataReuse=allowDataReuse) }); if (oneValue) { + console.log(label) // Max. 1 subrecord - // set a new Id to be associated to the new subrecord - let subformId; + // set a new Id to be associated with the new subrecord + let subrecordId; var now = new Date().valueOf(); var timespanId = (now / 1000).toString().replace('.', '-'); // reuse existing Id in Modify/Review stage if($(element).next('span').next('.hiddenInput').length) { - var existingSubform = $(element).next().next().val(); - subformId = existingSubform.split(',')[0]; + var existingSubform = $(element).next().next().val(); + subrecordId = existingSubform.split(',')[0]; } else { - subformId = timespanId; - } + subrecordId = timespanId; + } - // add the createSubrecordBtn to the field section, generate a subrecord form, then remove the button - $(element).after(createSubrecordBtn); - createSubrecord(subtemplateFieldId,fieldName,createSubrecordBtn,allowDataReuse,subformId,"oneValue"); - createSubrecordBtn.remove(); + $(element).after(createSubrecordBtn); // add the creation button to the field section + createSubrecord(subtemplateFieldId,label,createSubrecordBtn,allowDataReuse,subrecordId,oneValue); // generate a subrecord form + createSubrecordBtn.remove(); // remove the button to prevent users from creating new Subrecords // link the subrecord id to the "Subtemplate" type field - var hiddenSubrecordLink = $(''); + var hiddenSubrecordLink = $(''); $('#modifyForm, #recordForm').append(hiddenSubrecordLink); - } else { // Unlimited subrecords @@ -98,149 +91,153 @@ function prepareSubtemplateForms(element) { ////////////////// // Create subrecords -function createSubrecord(supertemplateId, fieldName, el, dataReuse=false, subformId=null,cardinality=null ) { - console.log(supertemplateId, fieldName, el, dataReuse, subformId, cardinality) - var absoluteSuperTemplateId = supertemplateId.split("_")[0]; - +function createSubrecord(subtemplateFieldId, label, el, dataReuse=false, subrecordId=null, singleValue=false ) { + var absoluteSubtemplateFieldId = subtemplateFieldId.split("_")[0]; + console.log("FIELD ID", subtemplateFieldId) // prepare a new subrecord id in case no one has been provided - if (!subformId) { + if (!subrecordId) { var now = new Date().valueOf(); - subformId = (now / 1000).toString().replace('.', '-'); + subrecordId = (now / 1000).toString().replace('.', '-'); } - var formId = $('.corners').eq(0).find('form').eq(0).attr('id'); // either 'recordForm' or 'modifyForm' - + // prepare the new subrecord form + const formId = $('.corners').eq(0).find('form').eq(0).attr('id'); // either 'recordForm' or 'modifyForm' const subrecordSection = $("
"); - const subrecordForm = $("
"); + const subrecordForm = $("
"); - // create a clone for each input belonging to the requested (sub-)template - $("[data-supertemplate='"+absoluteSuperTemplateId+"'][class~='original-subtemplate']").each(function() { - console.log($(this)) - let moveOn = true; - - // DATA REUSE: - // do not clone Elements in case data reuse is allowed and the same property is used in the upper level Form - - if (dataReuse==true) { - var rdfProperty = $(this).attr('data-property'); - var upperLevelCls= $("[data-subtemplate='"+absoluteSuperTemplateId+"']").attr('data-class'); - if ($("[data-class='"+upperLevelCls+"'][data-property='"+rdfProperty+"']").length) { - moveOn = false; - }; - } - - if (moveOn) { - console.log($(this)) - // CREATE A CLONE ELEMENT - const cloneElement = $(this).parent().parent().clone(); - cloneElement.find('textarea, select:not([type="hidden"]), input:not([type="hidden"]):not(label.switch input)').attr('data-subform',subformId); // associate the input field with the subrecord id - cloneElement.find('textarea, select, input').addClass('hidden'); - cloneElement.find('textarea, select, input').removeClass('original-subtemplate'); - // associate proper identifiers to input fields belonging to the subrecord form - var inputId = cloneElement.find('textarea, select:not([type="hidden"]), input:not([type="hidden"]):not(label.switch input)').attr('id'); - cloneElement.find('textarea, select:not([type="hidden"]), input:not([type="hidden"]):not(label.switch input)').attr('id', inputId+"_"+subformId.toString()); - cloneElement.find('textarea, select:not([type="hidden"]), input:not([type="hidden"]):not(label.switch input)').attr('name', inputId+"_"+subformId.toString()); + // create a clone for each input field belonging to the available subtemplates + // i.e.: use data-class to find templates' related fields + var subtemplateOptions = $("#"+absoluteSubtemplateFieldId).find("option:not(first-of-type)"); + console.log(subtemplateOptions) + + subtemplateOptions.each(function() { + var subtemplateOptionsClass = $(this).attr("value"); + console.log(subtemplateOptionsClass) + $("[data-class='"+subtemplateOptionsClass+"'][class~='original-subtemplate']").each(function() { + console.log(subtemplateOptionsClass) - // SET LITERAL INPUT FIELDS - if (cloneElement.find('[lang]').length>0) { - var literalInput = cloneElement.find('[lang]'); - var languageListSection = literalInput.parent().prev(); - languageListSection.find('a').each(function() { - var onclickAttr = $(this).attr('onclick'); - var regex = /'([^"]*)'/g; - var originalIdExtended = onclickAttr.match(regex)[0]; - var originalId = originalIdExtended.substring(1, originalIdExtended.length-1) - $(this).attr('onclick', onclickAttr.replace(originalId, originalId+'_'+subformId)); - }); + // DATA REUSE: + // do not clone Elements in case data reuse is allowed and the same property is used in the upper level Form + let moveOn = true; + if (dataReuse==true) { + var rdfProperty = $(this).attr('data-property'); + var upperLevelCls= $("[data-subtemplate='"+absoluteSubtemplateFieldId+"']").attr('data-class'); + if ($("[data-class='"+upperLevelCls+"'][data-property='"+rdfProperty+"']").length) { + moveOn = false; + }; } - // add a main-lang hidden input in case of primary key - if (cloneElement.find('input.disambiguate').next('[type="hidden"]').length > 0) { - var primaryKeyLangId = cloneElement.find('input.disambiguate').next('[type="hidden"]').attr('id'); - cloneElement.find('input[type="hidden"]').attr('id', primaryKeyLangId+"_"+subformId.toString()); - cloneElement.find('input[type="hidden"]').attr('name', primaryKeyLangId+"_"+subformId.toString()); - } - console.log(cloneElement.find('[data-subtemplate]')); - // SET SUBTEMPLATE FIELDS '+' BUTTON - cloneElement.find('[data-subtemplate]').each(function(){ - prepareSubtemplateForms($(this), hide=true); - - /* var subtemplateClass = $(this).attr('data-subtemplate'); - var fieldName = $(this).parent().prev().text(); - var addSubrecordBtn = $(this).next('i'); - addSubrecordBtn.on('click', function(){ - createSubrecord(subtemplateClass,fieldName,addSubrecordBtn); - }) */ - }); - - // retrieve previously provided values in case they are available (i.e., modify subrecords): - let clonedElementValues = []; - // a) single value fields - if ($('#'+formId+' #'+inputId+"_"+subformId).length >0) { - const toBeModified = $('#'+formId+' #'+inputId+'_'+subformId); - cloneElement.find('input').val(toBeModified.val()); - } - // b) multiple values fields - if ($('#'+formId+' [name^="'+inputId+'_"][name$="_'+subformId+'"]:not([name="'+inputId.split('_')[0]+'_'+subformId+'"])').length >0) { - - var importedValues = $('#'+formId+' [name^="'+inputId.split('_')[0]+'_"][name$="_'+subformId+'"]:not([name="'+inputId.split('_')[0]+'_'+subformId+'"])'); - cloneElement.find('.label div a').remove(); - if ($('#'+inputId).hasClass('searchWikidata') || $('#'+inputId).hasClass('searchVocab') || $('#'+inputId).hasClass('searchGeonamaes')) { - importedValues.each(function(){ - // imported values and URIs - var value = $(this).val(); - var code = value.split(",")[0]; - var label = decodeURIComponent(value.split(",")[1]); - var importedValueSpan = $(""+label+""); - clonedElementValues.push(importedValueSpan); - clonedElementValues.push($(this)); - $(this).remove(); + + if (moveOn) { + // CREATE A CLONE ELEMENT + const cloneElement = $(this).parent().parent().clone(); + cloneElement.find('textarea, select:not([type="hidden"]), input:not([type="hidden"]):not(label.switch input)').attr('data-subform',subrecordId); // associate the input field with the subrecord id + cloneElement.find('textarea, select, input').addClass('hidden'); + cloneElement.find('textarea, select, input').removeClass('original-subtemplate'); + // associate proper identifiers to input fields belonging to the subrecord form + var inputId = cloneElement.find('textarea, select, input:not([type="hidden"]):not(label.switch input)').attr('id'); + cloneElement.find('textarea, select, input:not([type="hidden"]):not(label.switch input)').attr('id', inputId+"_"+subrecordId.toString()); + cloneElement.find('textarea, select, input:not([type="hidden"]):not(label.switch input)').attr('name', inputId+"_"+subrecordId.toString()); + console.log(cloneElement.find('.label').text(), cloneElement) + + // SET LITERAL INPUT FIELDS + if (cloneElement.find('[lang]').length>0) { + var literalInput = cloneElement.find('[lang]'); + var languageListSection = literalInput.parent().prev(); + languageListSection.find('a').each(function() { + var onclickAttr = $(this).attr('onclick'); + var regex = /'([^"]*)'/g; + var originalIdExtended = onclickAttr.match(regex)[0]; + var originalId = originalIdExtended.substring(1, originalIdExtended.length-1) + $(this).attr('onclick', onclickAttr.replace(originalId, originalId+'_'+subrecordId)); }); - } else { - importedValues.each(function(){ - // multiple-lang literal values - clonedElementValues.push($(this)); - cloneElement.find('input').remove(); - if($(this).attr('lang') != undefined) { - let lang = $(this).attr('lang'); - const newLangItem = $(''+lang+''); - cloneElement.find('div').append(newLangItem); - } else { - let mainLang = $(this).val(); - cloneElement.find('div a[title="text language: '+mainLang.toUpperCase()+'"]').addClass('main-lang'); - } + } + // add a main-lang hidden input in case of primary key + if (cloneElement.find('input.disambiguate').next('[type="hidden"]').length > 0) { + var primaryKeyLangId = cloneElement.find('input.disambiguate').next('[type="hidden"]').attr('id'); + cloneElement.find('input[type="hidden"]').attr('id', primaryKeyLangId+"_"+subrecordId.toString()); + cloneElement.find('input[type="hidden"]').attr('name', primaryKeyLangId+"_"+subrecordId.toString()); + cloneElement.find('input.disambiguate').on('click', function() { + searchCatalogueByClass($(this).attr("id"),subtemplateFieldId,singleValue); }); - cloneElement.find('div a').eq(0).addClass('selected-lang'); - clonedElementValues[0].show(); } - } - // c) subrecords fields (inner subrecords) - if ($('[name="'+inputId+'_'+subformId+'-subrecords"]').length>0) { - // retrieve subrecords - var subrecords = $('[name="'+inputId+'_'+subformId+'-subrecords"]').val().split(','); - console.log("Sub", subrecords) - var subtemplateFieldId = $(this).attr('name').replace('-subrecords', ''); - var subrecordCls = $('#'+subtemplateFieldId).attr('subtemplate') - for (let i=0; i 0) { - var mainLang = $('#'+subrecordLabelField.attr('id').split('_')[0] + '_mainLang_' + code).val(); - label = $('#'+subrecordLabelField.attr('id').split('_')[0]+'_'+mainLang+'_'+code).val(); + + // SET SUBTEMPLATE FIELDS '+' BUTTON + cloneElement.find('[data-subtemplate]').each(function(){ + console.log($(this)) + prepareSubtemplateForms($(this)); + }); + + // retrieve previously provided values in case they are available (i.e., modify subrecords): + let clonedElementValues = []; + // a) single value fields + if ($('#'+formId+' #'+inputId+"_"+subrecordId).length >0) { + const toBeModified = $('#'+formId+' #'+inputId+'_'+subrecordId); + cloneElement.find('input').val(toBeModified.val()); + } + // b) multiple values fields + if ($('#'+formId+' [name^="'+inputId+'_"][name$="_'+subrecordId+'"]:not([name="'+inputId.split('_')[0]+'_'+subrecordId+'"])').length >0) { + + var importedValues = $('#'+formId+' [name^="'+inputId.split('_')[0]+'_"][name$="_'+subrecordId+'"]:not([name="'+inputId.split('_')[0]+'_'+subrecordId+'"])'); + cloneElement.find('.label div a').remove(); + if ($('#'+inputId).hasClass('searchWikidata') || $('#'+inputId).hasClass('searchVocab') || $('#'+inputId).hasClass('searchGeonamaes')) { + importedValues.each(function(){ + // imported values and URIs + var value = $(this).val(); + var code = value.split(",")[0]; + var label = decodeURIComponent(value.split(",")[1]); + var importedValueSpan = $(""+label+""); + clonedElementValues.push(importedValueSpan); + clonedElementValues.push($(this)); + $(this).remove(); + }); + } else { + importedValues.each(function(){ + // multiple-lang literal values + clonedElementValues.push($(this)); + cloneElement.find('input').remove(); + if($(this).attr('lang') != undefined) { + let lang = $(this).attr('lang'); + const newLangItem = $(''+lang+''); + cloneElement.find('div').append(newLangItem); + } else { + let mainLang = $(this).val(); + cloneElement.find('div a[title="text language: '+mainLang.toUpperCase()+'"]').addClass('main-lang'); + } + }); + cloneElement.find('div a').eq(0).addClass('selected-lang'); + clonedElementValues[0].show(); + } + } + // c) subrecords fields (inner subrecords) + /* if ($('[name="'+inputId+'_'+subrecordId+'-subrecords"]').length>0) { + // retrieve subrecords + var subrecords = $('[name="'+inputId+'_'+subrecordId+'-subrecords"]').val().split(','); + console.log("Sub", subrecords) + var subtemplateFieldId = $(this).attr('name').replace('-subrecords', ''); + var subrecordCls = $('#'+subtemplateFieldId).attr('subtemplate') + for (let i=0; i 0) { + var mainLang = $('#'+subrecordLabelField.attr('id').split('_')[0] + '_mainLang_' + code).val(); + label = $('#'+subrecordLabelField.attr('id').split('_')[0]+'_'+mainLang+'_'+code).val(); + } } } - } - } - - cloneElement.find('.input_or_select').eq(0).append(clonedElementValues); - subrecordForm.append(cloneElement); - } + } */ + cloneElement.find('.input_or_select').eq(0).append(clonedElementValues); + subrecordForm.append(cloneElement); + console.log(subrecordForm) + } + + }) }) // Add Knowledge Extraction input field if required @@ -248,15 +245,17 @@ function createSubrecord(supertemplateId, fieldName, el, dataReuse=false, subfor // TODO: MODIFY /* var resourceTemplate = $('[data-subtemplate="'+resourceClass+'"').attr('data-subtemplateid'); if (extractorsArray.includes(resourceTemplate)) { - generateExtractionField(subformId,subrecordForm); + generateExtractionField(subrecordId,subrecordForm); } */ - // Save button, Cancel button, Accordion title (only in case multiple subrecords are allowed) - if (cardinality==null) { - // subform accordion - const subrecordTitle = $("

New instance of "+fieldName+"

"); - subrecordSection.append(subrecordTitle); + + // Accordion title + const subrecordTitle = $("

New instance of "+label+"

"); + subrecordSection.append(subrecordTitle); + + // Save button, Cancel button + if (singleValue===false) { // save or cancel subrecord (buttons) const subrecordButtons = $("
"); const saveSubrecordButton = $("