Est-il possible d’avoir un exemple de code qui ajoute automatiquement des objets liés à d’autre objets par un relation 1-N ou N-N sur un modeler en fonction d’une liste d’objets sélectionnés.
Sans prendre en compte le placement de ces objets.
Au plus bas niveau un modèle est un SVG donc XML manipulable avec tous les utilitaires XML en back si on connait la sémantique du modèle (à mon avis peu maintenable).
Au plus haut niveau front, on doit pouvoir utiliser les APIs du contrôleur du modeleur Simplicite.Diagram.Modeler mais ils n’ont pas forcement été conçus pour être un CLI javascript = public ou silencieux, on a besoin d’un context graphique pour dessiner/mesurer, il se peut qu’il y ait des popups de confirmation ou de choix d’options, etc.
Pour créer un modèle à la volée par code en front, il faut essayer de faire comme suit :
créer un modèle comme n’importe quel objet métier via la couche Ajax, objet Model méthode create
utiliser le controlleur Simplicite.Diagram.Modeler
open = instancier / ouvrir le modèle (dans un div caché ?)
insertNode = ajouter des éléments
fetchRelatedNodes = insérer les objets connexes (links) à un élément
Après quelques ajustements pour rendre le modeleur invisible en V4, voici comment créer un modèle métier et y insérer des objets via JavaScript sans interaction utilisateur :
L’exemple utilise le template DemoModel de la démo :
Créer une action de liste sur DemoProduct de type Front avec le code suivant : javascript:demoNewModel(obj);
Coder dans le SCRIPT front de l’objet :
function demoNewModel(obj) {
var app = $ui.getAjax(),
diagram, modelId,
mt = app.getBusinessObject("ModelTemplate", "tmp_ajax_ModelTemplate"),
mod = app.getBusinessObject("Model", "tmp_ajax_Model"),
ids = obj.selectedIds;
// Needs objects to insert
if (!ids || !ids.length) {
$ui.alert("no selection");
return;
}
// Search the model template
function getTemplate() {
mt.search(function(list) {
if (list && list.length>0)
createModel(list[0]);
else
$ui.alert("no template");
},{
mtp_name: "DemoModel"
});
}
// Create a new empty model
function createModel(tpl) {
mod.create(load, {
row_id: "0",
mod_name: 'Product-' + (new Date()).getTime(),
mod_template_id: tpl.row_id
});
}
function load() {
modelId = mod.item.row_id;
// Load SVG engine
$ui.loadDiagramEngine(true, function() {
// Load empty SVG file in object
mod.get(function() {
// instanciate in silent mode
$ui.diagram.open(modelId, { svg:true, hidden:true }, build);
}, modelId, {
inlineDocs: true
});
});
}
// Insert objects in diagram
function build(d) {
diagram = d;
var list = [];
$(ids).each(function(i,id) {
list.push({
object: "DemoProduct", // node object
id: id, // node row_id
template: "DemoModel_Product", // node template name
x: i*30 + 30, // dummy position
y: i*70 + 30,
container: null // no container
});
});
// async loading with up-to-date data
diagram.insertNodes(list, function(nodes) {
// auto load sibling nodes thru links
diagram.fetchRelatedNodes(nodes, save);
});
}
function save() {
diagram.save(function() {
// Close hidden model
diagram.close(false);
// Re-open in a window
$ui.displayModeler(null, modelId, { svg:true, docked:false });
}, true); // silent
}
getTemplate();
}
Ensuite il suffit de sélectionner des produits et de cliquer sur l’action pour :
construire un nouveau model
lui insérer les objets séléctionnés
puis ajouter les objets relatifs (les commandes + clients) liés aux produits insérés
Enregistrer et fermer le model
réouvrir le model créé dans une fenêtre.
Attention tous les traitements sont asynchrones, donc il est important de bien chainer les callbacks de chaque méthode.
Merci David (et François), j’avais déjà épinglé le post de la semaine dernière =)
J’ai un ticket sur la R5 de notre module GDPR qui porte sur la génération automatique du diagramme de flux des données et acteurs documentés sur les traitements de données personnelles… quelques devoirs de vacances en perspective.
Pour avoir déjà jardiné sur les platebandes du dummy position (coordonnées, calcul d’intersections, smart layout), je sais déjà que c’est là que sera l’os ! … J’ai hâte
En V5, il reste pas mal de boulot sur le positionnement 2D des modèles SVG.
il faut ré-intégrer les layouts classiques en arbre ou en étoile qui avaient été faits en canvas
je travaille actuellement sur un positionnement 2D automatique, un algo NP-complet en javascripts de “remplissage de sac-à-dos”, c’est un enfer… par contre un algo à base de physique des “ressorts” me semble plus efficace pour dessiner des modèles d’objets (entités/relations)
Ce projet est assez inspirant : http://getspringy.com/
je vais essayer de l’intégrer rapidement car ça me chatouille aussi depuis longtemps.
J’ai implémenté le code de démo sur l’environnement RCI et cela génère bien le modèle avec les objets liés, mais le positionnement ne semble pas fonctionner.
resource?row_id=288&d=responsive_ThemeAdmin&_=d644e41c86027b6b224b6aa992a321d927f008eb_20200916145204:2 Uncaught TypeError: diagram.layoutSprings is not a function
at resource?row_id=288&d=responsive_ThemeAdmin&_=d644e41c86027b6b224b6aa992a321d927f008eb_20200916145204:2
at done (diagram-bundle.js?_=d644e41c86027b6b224b6aa992a321d927f008eb_20200916145204:31)
at Simplicite.Ajax.BusinessObject.error (diagram-bundle.js?_=d644e41c86027b6b224b6aa992a321d927f008eb_20200916145204:55)
at Simplicite.Ajax.BusinessObject.<anonymous> (ui-bundle.js?_=d644e41c86027b6b224b6aa992a321d927f008eb:264)
at Simplicite.Ajax._callResponse (ui-bundle.js?_=d644e41c86027b6b224b6aa992a321d927f008eb:59)
at XMLHttpRequest.xhr.onreadystatechange (ui-bundle.js?_=d644e41c86027b6b224b6aa992a321d927f008eb:53)
Et voici mon code
function demoNewModel(obj) {
var ids = obj.selectedIds, list = [];
// Needs objects to insert
if (!ids || !ids.length) {
$ui.alert("no selection");
return;
}
$(ids).each(function(i,id) {
console.log("id" + id);
list.push({
object: "DemoProduct", // node object
id: id, // node row_id
template: "DemoModel_Product", // node template name
x: i*50 + 30, // dummy position
y: i*30 + 30,
container: null // no container
});
});
// Load SVG engine
$ui.loadDiagramEngine(function() {
try {
console.log("loadDiagramEngine");
// Create the model in silent mode
var name = "Product-" + (new Date()).getTime();
console.log(name);
$ui.diagram.create("DemoModel", name, {
hidden: true, // hide the modeler
nodes: list, // nodes to insert
fetch: true // then fetch related orders and customers
},
function(diagram) {
// close the hidden model
diagram.close(false);
// Re-open in a window
$ui.displayModeler(null, diagram.modelId, {
svg: true,
docked: false
});
diagram.layoutSprings({
stiffness: 100,
repulsion: 500,
damping: 0.5,
remoteness: 50,
gravity: 100,
maxDuration: 5000, // 5 sec
callback: function() { diagram.save(); }
});
});
}
catch(e) {
console.log("Erreur");
console.log(e);
}
});
A mon avis l’objet diagram n’est plus valide puisque le modeler est fermé puis réouvert dans votre code. Il faut faire le placement avant de le fermer et le réouvrir, et en pensant que les méthodes sont asynchrones donc à chainer :
$ui.diagram.create("DemoModel", name, {
hidden: true, // hide the modeler
nodes: list, // nodes to insert
fetch: true // then fetch related orders and customers
},
function(diagram) {
// auto-placement
diagram.layoutSprings && diagram.layoutSprings({
stiffness: 100,
repulsion: 500,
damping: 0.5,
remoteness: 50,
gravity: 100,
maxDuration: 5000, // 5sec
callback: function() {
// Save and close
diagram.canClose(false, function() { // confirm=false means auto-save
diagram.close();
// Re-open in a window
$ui.displayModeler(null, diagram.modelId, {
svg: true,
docked: false
});
});
}
});
});
Il faudra tunner les paramètrres pour avoir un résultat correct.
Sinon pour simplifier, il suffit de mettre hidden: false dès le début pour voir le diagramme se créer, et sans avoir à l’enregistrer/réouvrir à la fin.
.
Est-il possible en génération par code via le helper, d’afficher plusieurs objets liés à la suite ? Dans mon cas les applications sont liées entre elles par des flux. Je voudrais afficher un carré avec Application A → carré avec nom du flux → carré avec Application B mais le fetch semble s’arrêter au premier objet lié, le flux.
Sinon sans le helper ?
VOici comment sont configurés mes template links, j’ai entouré celui qui n’est pas fetché
Le fetchRelatedNodes est à un seul niveau, il n’est pas récursif. On pourrait prévoir d’ajouter une profondeur de recherche, ou de préciser quels Links parcourir…
Il faut donc que votre code fasse les recherches nécessaires à votre modèle (ici les flux de A)
et invoque les fetchRelatedNodes sur chaque flux par exemple pour ramener les applications B.
Merci beaucoup pour cet éclairage.
Je vais essayer de le faire par code, mais je crains que ça ne fetche pas le link que j’ai configuré dans le template.
Voici mon modèle généré via le helper (désolée pour la mauvaise qualité des indications)
Et si à partir du canvas, je clique sur le Flux et choisis “fetch related”, ce qui apparaît est le ModelObjectContent configuré sur le ModelObjectTemplate Flux, et non l’Application configurée dans le ModelTemplateLink du node Flux.
J’ai l’impression que c’est le lien FluxData qui est ramené ici.
Vous avez également dû le définir en contenu du node car on le voit dans les contenu de CR1.
Dans vos 2 liens flux/app, il faut que Target field soit row_id. On part du Flux avec 2 sources/FK vers les row_id d’Application. Votre paramétrage de FluxAppEm me semble dans le mauvais sens, et le fetch ne le regarde pas.
En fait le node de départ est l’application (l’action pour générer le modèle est sur l’application)
De l’application le helper fetche les flux avec leur content ce qui est parfait
Mais après je dois trouver un moyen pour que mon code fetche les applications destinataires du flux (link FluAppRec)
Peut-être en récupérant dans mon code à la main les flux liés à mon application (je n’en ai qu’une) et en les pushant dans la liste que je passe au helper. Je vais essayer
Merci !
Oui, faites un obj.search des Flux filtrés sur la source (et/ou la destination) qui vous donnera pour chacun les row_id (flux + application A/B) à insérer dans la liste des noeuds du modèle.
Sinon je continue de penser qu’un de vos Link est paramétré à l’envers et ne ramène rien, avez vous des erreur SQL dans les logs ?
Oui si je pars du Flux vers les Applications il faut que je change le sens.
En revanche je n’arrive plus à faire fonctionner les liens sans “link object”, y a-t-il eu une mise à jour récente ?