mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-19 00:28:47 +02:00
poc form SDK (extends to form)
This commit is contained in:
@@ -3,10 +3,11 @@
|
||||
*
|
||||
* @param oForm
|
||||
* @param objectFormUrl
|
||||
* @param objectSaveUrl
|
||||
* @returns {{handleElement: handleElement}}
|
||||
* @constructor
|
||||
*/
|
||||
const Collection = function(oForm, objectFormUrl){
|
||||
const Collection = function(oForm, objectFormUrl, objectSaveUrl){
|
||||
|
||||
/**
|
||||
* Listen for add item buttons.
|
||||
@@ -101,9 +102,13 @@ const Collection = function(oForm, objectFormUrl){
|
||||
*/
|
||||
function createObject(e){
|
||||
|
||||
let objectId = e.currentTarget.closest('form').dataset.objectId;
|
||||
|
||||
// set modal loading state
|
||||
$('#object_modal .modal-body').html('loading...');
|
||||
|
||||
const cont = e.currentTarget.closest('.link_set_widget_container');
|
||||
|
||||
// open modal
|
||||
const myModalAlternative = new bootstrap.Modal('#object_modal', []);
|
||||
myModalAlternative.show();
|
||||
@@ -115,14 +120,15 @@ const Collection = function(oForm, objectFormUrl){
|
||||
|
||||
// prepare request data
|
||||
const aLockedAttributes = {};
|
||||
aLockedAttributes[e.currentTarget.dataset.extKeyToMe] = 0;
|
||||
aLockedAttributes[e.currentTarget.dataset.extKeyToMe] = objectId;
|
||||
const aData = {
|
||||
locked_attributes: aLockedAttributes
|
||||
locked_attributes: aLockedAttributes,
|
||||
att_code: cont.dataset.attCode
|
||||
}
|
||||
|
||||
// fetch url
|
||||
fetch(url, {
|
||||
method: 'post',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
@@ -154,6 +160,70 @@ const Collection = function(oForm, objectFormUrl){
|
||||
listenRemoveItem(oContainer);
|
||||
}
|
||||
|
||||
|
||||
function listenSaveModalobject(){
|
||||
const oSave = document.querySelector('[data-action="save_modal_object"]');
|
||||
|
||||
oSave.addEventListener('click', function(e){
|
||||
|
||||
const oForm = document.querySelector('form[name="new"]');
|
||||
|
||||
|
||||
for(let instanceName in CKEDITOR.instances) {
|
||||
|
||||
CKEDITOR.instances[instanceName].updateElement();
|
||||
}
|
||||
|
||||
const data = new URLSearchParams();
|
||||
for (const pair of new FormData(oForm)) {
|
||||
|
||||
console.log(pair[0] + ' = ' + pair[1]);
|
||||
|
||||
data.append(pair[0], pair[1]);
|
||||
}
|
||||
data.append('locked_attributes', '');
|
||||
|
||||
// compute object form url
|
||||
const url = objectSaveUrl
|
||||
.replaceAll('object_class', oForm.dataset.objectClass)
|
||||
.replaceAll('form_name', 'new');
|
||||
|
||||
// fetch url
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams(new FormData(oForm))
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
|
||||
let form = $(data.template);
|
||||
|
||||
console.log(form);
|
||||
//
|
||||
// console.log(oForm.dataset.attCode);
|
||||
//
|
||||
// const fragment = oToolkit.createElementFromHtml(data.template);
|
||||
// const inner = fragment.querySelector('form').innerHTML;
|
||||
// const el = oToolkit.createElementFromHtml(inner);
|
||||
// console.log(el);
|
||||
//
|
||||
// const myModalAlternative = new bootstrap.Modal('#object_modal', []);
|
||||
// myModalAlternative.hide();
|
||||
|
||||
console.log($(`[data-att-code="${oForm.dataset.attCode}"] tbody`));
|
||||
|
||||
$(`[data-att-code="${oForm.dataset.attCode}"] tbody`).append($(form.innerHTML));
|
||||
|
||||
})
|
||||
.catch(function (error) {
|
||||
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
listenSaveModalobject();
|
||||
|
||||
return {
|
||||
handleElement
|
||||
}
|
||||
|
||||
@@ -91,6 +91,8 @@ const Form = function(oWidget, oDynamic){
|
||||
});
|
||||
});
|
||||
|
||||
const aAllAttCodes = aDependentAttCodes.concat(aDependenciesAttCodes);
|
||||
|
||||
/////////////////////////////////
|
||||
// II - PREPARE RELOAD REQUEST
|
||||
|
||||
@@ -99,7 +101,7 @@ const Form = function(oWidget, oDynamic){
|
||||
let $bFirst = true;
|
||||
|
||||
// iterate throw dependencies...
|
||||
aDependenciesAttCodes.forEach(function(sAtt) {
|
||||
aAllAttCodes.forEach(function(sAtt) {
|
||||
|
||||
const oDependsOnElement = oElement.querySelector(String.format(aSelectors.dataAttCode, sAtt));
|
||||
if(!$bFirst){
|
||||
@@ -110,7 +112,7 @@ const Form = function(oWidget, oDynamic){
|
||||
});
|
||||
|
||||
sRequestBody += '&att_codes=' + aDependentAttCodes.join(',');
|
||||
sRequestBody += '&dependency_att_codes=' + aDependenciesAttCodes.join(',');
|
||||
sRequestBody += '&dependency_att_codes=' + aAllAttCodes.join(',');
|
||||
|
||||
/////////////////////////////////
|
||||
// III - UPDATE THE FORM
|
||||
|
||||
@@ -142,6 +142,11 @@ class ObjectController extends AbstractController
|
||||
// decode data
|
||||
$aData = json_decode($request->getContent(), true);
|
||||
|
||||
// locked attributes
|
||||
foreach($aData['locked_attributes'] as $sKey => $sValue){
|
||||
$oObject->Set($sKey, $sValue);
|
||||
}
|
||||
|
||||
// create object form
|
||||
$oForm = $oFormFactory->createNamed($name, ObjectType::class, $oObject, [
|
||||
'object_class' => $class,
|
||||
@@ -150,10 +155,40 @@ class ObjectController extends AbstractController
|
||||
'data-reload-url' => $this->generateUrl('object_reload', [
|
||||
'class' => $class,
|
||||
'id' => $id
|
||||
])
|
||||
]),
|
||||
'data-object-class' => $class,
|
||||
'data-att-code' => $aData['att_code']
|
||||
]
|
||||
]);
|
||||
|
||||
// return object form
|
||||
return new JsonResponse([
|
||||
'template' => $this->renderView('DI/form.html.twig', [
|
||||
'id' => $id,
|
||||
'class' => $class,
|
||||
'form' => $oForm->createView(),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{class<\w+>}/{id<\d+>}/{name<\w+>}/save", name="object_save", methods={"POST"})
|
||||
*/
|
||||
public function objectSave(Request $request, string $name, string $class, int $id, ObjectService $oObjectService, FormFactoryInterface $oFormFactory) : Response
|
||||
{
|
||||
// retrieve object
|
||||
try{
|
||||
$oObject = $oObjectService->getObject($class, $id);
|
||||
}
|
||||
catch(Exception $e){
|
||||
throw $this->createNotFoundException("The $class $id does not exist");
|
||||
}
|
||||
|
||||
// create object form
|
||||
$oForm = $oFormFactory->createNamed($name, ObjectType::class, $oObject, [
|
||||
'object_class' => $class,
|
||||
]);
|
||||
|
||||
// handle HTTP request
|
||||
$oForm->handleRequest($request);
|
||||
|
||||
@@ -161,18 +196,32 @@ class ObjectController extends AbstractController
|
||||
if ($oForm->isSubmitted() && $oForm->isValid()) {
|
||||
|
||||
try {
|
||||
// handle link set (apply DbInsert, DbDelete, DbUpdate) could be automatic ?
|
||||
$oObjectService->handleLinkSetDB($oObject);
|
||||
|
||||
// save object
|
||||
$oObject->DBUpdate();
|
||||
if($id === 0){
|
||||
$id = $oObject->DBInsert();
|
||||
}
|
||||
else{
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
throw new HttpException(500, 'Error while trying to save object');
|
||||
}
|
||||
|
||||
// redirect to view object
|
||||
return new JsonResponse();
|
||||
// create object form
|
||||
$oForm = $oFormFactory->createNamed($name, ObjectType::class, $oObject, [
|
||||
'object_class' => $class,
|
||||
'z_list' => 'list'
|
||||
]);
|
||||
|
||||
// return object form
|
||||
return new JsonResponse([
|
||||
'template' => $this->renderView('DI/form.html.twig', [
|
||||
'id' => $id,
|
||||
'class' => $class,
|
||||
'form' => $oForm->createView(),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
// return object form
|
||||
@@ -198,10 +247,13 @@ class ObjectController extends AbstractController
|
||||
throw $this->createNotFoundException("The $class $id does not exist");
|
||||
}
|
||||
|
||||
$aDependencyAttCodes = explode(',', $request->get('dependency_att_codes'));
|
||||
$aAttCodes = explode(',', $request->get('att_codes'));
|
||||
|
||||
// create form with request data (dependent field)
|
||||
$oForm = $this->createForm(PartialObjectType::class, $oObject, [
|
||||
'object_class' => $class,
|
||||
'att_codes' => explode(',', $request->get('dependency_att_codes'))
|
||||
'att_codes' => array_merge($aDependencyAttCodes, $aAttCodes)
|
||||
]);
|
||||
|
||||
// handle form data
|
||||
@@ -213,7 +265,7 @@ class ObjectController extends AbstractController
|
||||
// create a new form for affected field with updated (but not persist) data
|
||||
$oForm = $this->createForm(PartialObjectType::class, $oObject, [
|
||||
'object_class' => $class,
|
||||
'att_codes' => explode(',', $request->get('att_codes'))
|
||||
'att_codes' => $aAttCodes
|
||||
]);
|
||||
|
||||
// return object form
|
||||
|
||||
@@ -79,7 +79,7 @@ class AttributeBuilder
|
||||
'required' => !$oAttributeDefinition->IsNullAllowed(),
|
||||
'disabled' => !$oAttributeDefinition->IsWritable() || $bIsLocked,
|
||||
'attr' => [
|
||||
'data-att-code' => $sCode
|
||||
'data-att-code' => $sCode,
|
||||
],
|
||||
'row_attr' => [
|
||||
'data-block' => 'container'
|
||||
@@ -87,7 +87,8 @@ class AttributeBuilder
|
||||
'label_attr' => [
|
||||
'class' => $bIsLocked ? 'locked' : ''
|
||||
]
|
||||
]
|
||||
],
|
||||
'create_hidden' => $bIsLocked
|
||||
];
|
||||
|
||||
// register dependencies
|
||||
|
||||
@@ -191,9 +191,10 @@ class ObjectFormListener implements EventSubscriberInterface
|
||||
$aOptions['disabled'] = true;
|
||||
}
|
||||
|
||||
// ignore fields hidden
|
||||
// ignore fields hidden ISSUE WITH LINKSET PRESENTATION
|
||||
if ($iFlags & (OPT_ATT_HIDDEN)) {
|
||||
return null;
|
||||
// $aOptions['attr']['class'] = 'd-none';
|
||||
// return null;
|
||||
}
|
||||
|
||||
return $aOptions;
|
||||
|
||||
@@ -63,8 +63,11 @@ class ExternalKeyType extends AbstractType implements IFormTypeOptionModifier
|
||||
}
|
||||
|
||||
// !!! ensure current value is part of result
|
||||
$object = MetaModel::GetObject($aInitialOptions['object_class'], $iVal);
|
||||
$aInitialOptions['choices'][$object->GetName()] = $iVal;
|
||||
if($iVal > 0){
|
||||
$object = MetaModel::GetObject($aInitialOptions['object_class'], $iVal);
|
||||
$aInitialOptions['choices'][$object->GetName()] = $iVal;
|
||||
}
|
||||
|
||||
}
|
||||
catch(Exception $e){
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ class LinkSetType extends AbstractType
|
||||
$view->vars['labels'] = $this->oObjectService->getFormPresentationLabels($options['entry_options']['object_class'], 'list', $options['entry_options']['ext_key_to_me']);
|
||||
$view->vars['object_class'] = $options['entry_options']['object_class'];
|
||||
$view->vars['ext_key_to_me'] = $options['entry_options']['ext_key_to_me'];
|
||||
$view->vars['att_code'] = $options['attr']['data-att-code'];
|
||||
$view->vars['target_class'] = $options['target_class'];
|
||||
$view->vars['is_indirect'] = $options['is_indirect'];
|
||||
$view->vars['is_abstract'] = $options['is_abstract'];
|
||||
|
||||
@@ -27,12 +27,6 @@ class ObjectType extends AbstractType
|
||||
/** @var ObjectPresentationService object presentation service */
|
||||
private ObjectPresentationService $objectPresentationService;
|
||||
|
||||
/** @var \Combodo\iTop\DI\Form\Builder\AttributeBuilder attribute builder */
|
||||
private AttributeBuilder $oAttributeBuilder;
|
||||
|
||||
/** @var \Combodo\iTop\DI\Form\Builder\LayoutBuilder layout builder */
|
||||
private LayoutBuilder $oLayoutBuilder;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -41,12 +35,10 @@ class ObjectType extends AbstractType
|
||||
* @param \Combodo\iTop\DI\Form\Builder\AttributeBuilder $oAttributeBuilder
|
||||
* @param \Combodo\iTop\DI\Form\Builder\LayoutBuilder $oLayoutBuilder
|
||||
*/
|
||||
public function __construct(ObjectFormListener $oObjectFormModifier, ObjectPresentationService $objectPresentationService, AttributeBuilder $oAttributeBuilder, LayoutBuilder $oLayoutBuilder)
|
||||
public function __construct(ObjectFormListener $oObjectFormModifier, ObjectPresentationService $objectPresentationService)
|
||||
{
|
||||
$this->oObjectFormModifier = $oObjectFormModifier;
|
||||
$this->objectPresentationService = $objectPresentationService;
|
||||
$this->oAttributeBuilder = $oAttributeBuilder;
|
||||
$this->oLayoutBuilder = $oLayoutBuilder;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
|
||||
@@ -6,6 +6,7 @@ use Combodo\iTop\DI\Form\Builder\AttributeBuilder;
|
||||
use Combodo\iTop\DI\Form\Builder\LayoutBuilder;
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
@@ -57,6 +58,10 @@ class ObjectPresentationService
|
||||
$level = $this->handleLevel($aPresentation, $class, $lockedAttributes);
|
||||
foreach ($level as $key => $value) {
|
||||
$builder->add($key, $value['type'], $value['options']);
|
||||
|
||||
// if(array_key_exists('create_hidden', $value) && $value['create_hidden'] === true){
|
||||
// $builder->add($key,HiddenType::class);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +112,7 @@ class ObjectPresentationService
|
||||
$aPresentation = MetaModel::GetZListItems($class, $zList);
|
||||
$aPresentation = $this->filterLinkSetpresentation($aPresentation, $class, $sExtKeyToMe);
|
||||
|
||||
$level = $this->handleLevel($aPresentation, $class, null, null);
|
||||
$level = $this->handleLevel($aPresentation, $class, null);
|
||||
$aLabels = [];
|
||||
foreach ($level as $key => $value) {
|
||||
$value['options']['attr']['data-att-code'] = $key;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<body data-bs-theme="light">
|
||||
|
||||
<!-- nav -->
|
||||
<!-- navbar -->
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#"><img class="app_icon" src="{{ asset_image('DI/flask-solid.svg') }}" width="24px"> Forms SDK</a>
|
||||
@@ -80,18 +80,15 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{# - BLOCK BODY - #}
|
||||
<div class="m-3">
|
||||
|
||||
{# - BLOCK BODY - #}
|
||||
{% block body %}{% endblock %}
|
||||
|
||||
</div>
|
||||
|
||||
{# - BLOCK TOASTS - #}
|
||||
<div class="toast-container position-fixed bottom-0 start-50 translate-middle-x p-3">
|
||||
|
||||
{# - BLOCK TOASTS - #}
|
||||
{% block toasts %}{% endblock %}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Full screen modal -->
|
||||
@@ -102,7 +99,7 @@
|
||||
<h1 class="modal-title fs-4" id="exampleModalFullscreenLabel">Create object</h1>
|
||||
<div>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-label="Close">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" aria-label="Confirm">Confirm</button>
|
||||
<button type="button" class="btn btn-primary" data-action="save_modal_object" aria-label="Confirm">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@@ -112,10 +109,15 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div id="test">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
// Toolkit
|
||||
var oToolkit = new Toolkit();
|
||||
const oToolkit = new Toolkit();
|
||||
oToolkit.init();
|
||||
|
||||
// App initialization
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
{% endif %}
|
||||
|
||||
{# container #}
|
||||
<div class="link_set_widget_container">
|
||||
<div class="link_set_widget_container" data-att-code="{{ att_code }}">
|
||||
|
||||
{# table #}
|
||||
<table class="table link_set_widget" >
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
{# inject form #}
|
||||
<div>
|
||||
{{ form(form, {'attr': {'id' : 'object_form'}}) }}
|
||||
{{ form(form, {'attr': {'id' : 'object_form', 'data-object-id' : id}}) }}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -59,7 +59,7 @@
|
||||
});
|
||||
|
||||
{# Collection #}
|
||||
const oCollection = new Collection(oForm, '{{ path('object_form', {'class': 'object_class', 'id' : 0, 'name': 'form_name'}) }}');
|
||||
const oCollection = new Collection(oForm, '{{ path('object_form', {'class': 'object_class', 'id' : 0, 'name': 'form_name'}) }}', '{{ path('object_save', {'class': 'object_class', 'id' : 0, 'name': 'form_name'}) }}');
|
||||
oCollection.handleElement(document);
|
||||
|
||||
{# toast database information #}
|
||||
|
||||
Reference in New Issue
Block a user