0

I'm working on the Jspresso framework (launching the project with Swing). I have made N-N bidirectional relationships between components, but the fields corresponding to these relationships do not appear. For example, I have a relationship between projects and students (a student can have many projects, a project can have many students). When I add a project and open it in its detail view, I can create a new student and add it to the project BUT I can not add an existing student to the project neither search a project according to its students in the search view. Is there a way to display this ?

Here is my view.groovy

 // Implement your views here using the SJS DSL.

 form('Project.pane',
    parent:'decoratedView',
    labelsPosition:'ASIDE',
    columnCount:2,
    fields:     ['name','students','technologies','usesTrainers','technicalTrainers']){
    actionMap{
            actionList('FILE'){
                action(ref:'saveModuleObjectFrontAction')
                action(ref:'reloadModuleObjectFrontAction')
          }
    }
}

table'Project-students.table',
    parent:'decoratedView',
    actionMap:'masterDetailActionMap'

split_vertical'Project.proj.view',
    model:'Project',
    top:'Project.pane',
    bottom:'Project-students.table'



form('Student.pane',
    parent:'decoratedView',
    labelsPosition:'ASIDE',
    columnCount:2){
        actionMap{
                actionList('FILE'){
                action(ref:'saveModuleObjectFrontAction')
                action(ref:'reloadModuleObjectFrontAction')
          }
    }
}

table'Student-technologies.table',
    parent:'decoratedView',
    actionMap:'masterDetailActionMap'

split_vertical'Student.proj.view',
    model:'Student',
    top:'Student.pane',
    bottom:'Student-technologies.table'

form('Trainer.pane',
    parent:'decoratedView',
    labelsPosition:'ASIDE',
    columnCount:5)

form('Technology.pane',
    parent:'decoratedView',
    labelsPosition:'ASIDE',
    columnCount:5)

And here is my model.groovy

// Implement your domain here using the SJS DSL.

Interface('Traceable',
interceptors: 'TraceableLifecycleInterceptor',
uncloned: ['createTimestamp',
           'lastUpdateTimestamp','lastUpdatedBy','createdBy']) {
  string_64 'createdBy',readOnly:true
  date_time 'createTimestamp', timeZoneAware: true, readOnly: true
  string_64 'lastUpdatedBy', readOnly:true
  date_time 'lastUpdateTimestamp', timeZoneAware: true, readOnly: true
}

Entity ('Project', extend:'Traceable',toString:'name',
icon:'project.png',
rendered: ['name','lastUpdateTimestamp','lastUpdatedBy','createTimestamp','createdBy'],
queryable: ['name']){
string_64 'name'
set 'technologies', composition:true, ref:'Technology'
set 'technicalTrainers', composition:true, ref:'Trainer'
set 'usesTrainers', composition:true, ref:'Trainer'
set 'students', composition:true, ref:'Student'

}

Entity ('Technology', extend:'Traceable',toString:'name',
icon:'technology.png',
rendered: ['name','lastUpdateTimestamp','lastUpdatedBy','createTimestamp','createdBy'],
queryable: ['name']){
string_64 'name'
set 'projects', ref:'Project', reverse:'Project-technologies'
set 'studentsAbleToUseIt', ref:'Technology', reverse:'Student-technologies'
set 'trainersAbleToTeachIt', ref:'Technology', reverse:'Trainer-technologies'
}

Interface ('Person', extend:'Traceable'){
string_64 'lastname'
string_64 'firstname'
date_time 'createTimestamp', timeZoneAware: true, readOnly: true
date_time 'lastUpdateTimestamp', timeZoneAware: true, readOnly: true
}

 Entity ('Trainer',
    extend: 'Person', 
    toString:'firstname',
    icon:'trainer.png',
    rendered: ['firstname','lastname','lastUpdateTimestamp','lastUpdatedBy','createTimestamp','createdBy'],
    queryable: ['firstname','lastname']){
set 'projectsAsTechnicalTrainer', ref:'Project', reverse:'Project-technicalTrainers'
set 'projectsAsUsesTrainer', ref:'Project', reverse:'Project-usesTrainers'
set 'technologies', composition:true, ref:'Technology'
date_time 'createTimestamp', timeZoneAware: true, readOnly: true
date_time 'lastUpdateTimestamp', timeZoneAware: true, readOnly: true
}

Entity ('Student',
    extend: 'Person', 
    toString:'firstname',
    icon:'student.png',
    rendered: ['firstname','lastname','lastUpdateTimestamp','lastUpdatedBy','createTimestamp','createdBy'],
    queryable: ['firstname','lastname']){
set 'technologies', composition:true, ref:'Technology'
set 'projects', ref:'Project', reverse:'Project-students'
date_time 'createTimestamp', timeZoneAware: true, readOnly: true
date_time 'lastUpdateTimestamp', timeZoneAware: true, readOnly: true
}

1 Answers1

1

You can definitely achieve both goals quite easily.

1/ A N-N relationship can simply be regarded as two 1-N relationships. So you can use the Project-students relationship on a Project oriented view and the Student-projects on a Student oriented view. You can either use 2 different filter modules (one on Project and one on Student) or complement your Project.proj.view in order to add a second level of detail by adding an extra table that will presents the Student-projects of the selected student in the 1st table detail.

Something like :

split_vertical ('Project.proj.view',
  model:'Project',
  top:'Project.pane') {
  bottom {
    split_horizontal(
      left:  'Project-students.table',
      right: 'Student-projects.table',
      cascadingModels: true
    )
  }
}

2/ Regarding the ability to select and add an existing Project or Student to the respective collections, you can use a custom LOV action as explained in the 1st chapter of the Jspresso-CE reference documentation.

Something like :

table('Project-students.table') {
  actionMap {
    actionList('EDIT'){
      action(parent:'lovAction',
        custom:[
          autoquery:false,
          entityDescriptor_ref:'Student',
          okAction_ref:'addAnyToMasterFrontAction'
        ]
      )
      action(ref:'removeAnyCollectionFromMasterFrontAction')
    }
  }
}

3/ As for filtering Project by Student (or the reverse), Jspresso supports defining collection properties as filter properties. In that case, the filter view will install a LOV for the student to filter the Projects for.

If you want to have it by default on each and every Project filter view, declare it directly on the model.

Something like :

Entity ('Project'
        ...
        queryable : ['name', 'students']
        ...){
  ...
}