Edit: Edit a relational id in datatable

Hi,

I’ve figured out how to display an Id or the Name of an object in relationship with my entity from serverside but if I just load my entity from the api I get this

[object Object]

I was wondering if I was able to do it with webix directly as this doesn’t work:

id: "g_sponsor.name"

Also, I can’t figure how to be able to edit it.

I have a table trial in association with a table sponsor, with sponsor_id as a foreign key in the trial table. I understand this return an object. I can’t figure a way to edit it (select another sponsor from the list).

this is the error I get

Type error: Argument 1 passed to ErpBundle\\Entity\\Trial::setGSponsor() 
must be an instance of ErpBundle\\Entity\\Sponsor, string given 

Ideally, I’d like to be able to loop in this foreign table and select it from a list like this:

editor: "multiselect",
            optionslist: true,
            options: [
                {id: 1, value: "a"},
                {id: 2, value: "b"},
                {id: 3, value: "c"},
                {id: 4, value: "d"}
            ],

What is the best way to do it?

My datatable:

var griddef = {
    view: "datatable",
    id: "dtable",
    headermenu: true,
    columns: [

      
        {
            id: "g_sponsor",
            batch: "main",
            editor: "text",
            header: [null, "Sponsor", {content: "textFilter"}],
            width: 150,
            sort: "string"
        },
        {
            id: "g_bps",
            batch: "main",
            editor: "text",
            header: [null, "BPS", {content: "selectFilter"}],
            width: 50,
            sort: "string"
        },
        {
            id: "g_efip",
            batch: "main",
            editor: "multiselect",
            optionslist: true,
            options: [
                {id: 1, value: "E"},
                {id: 2, value: "F"},
                {id: 3, value: "I"},
                {id: 4, value: "P"}
            ],
            header: [null, "EFIP", {content: "selectFilter"}],
            width: 50,
            sort: "string"
        },
        {
            id: "g_protocol_number",
            batch: "main",
            editor: "text",
            header: [null, "Protocole Code", {content: "textFilter"}],
            width: 150,
            sort: "string"
        },
        {
            id: "g_trial_number",
            batch: "main",
            editor: "text",
            header: [null, "Trial Code", {content: "textFilter"}],
            width: 150,
            sort: "string"
        },         

    ],

    editable: true,
    select: "cell",
    blockselect: true,
    multiselect: true,
    navigation: true,
    autoheight: true,
    autowidth: true,

    save: "rest->{{ path('api_post_t') }}",
    url: "rest->{{ path('erp_trialapi_cgetload') }}"

 webix.ready(function () {
    grida = webix.ui({
        container: 'test',
        rows: [searchbar, excelexport, griddef],

    });
});

Json example:

{
id: 38,
g_project_manager: "Gerard",
g_departement: 12120,
g_country: "France",
g_technician: "Alphonse",
g_cofrac_code: "Code",
g_year: 2017,
g_efip: "2",
g_hfieds: "E",
g_crop_code: "Ma",
g_sponsor: {
        id: 13,
        sponsor_code: "AGB",
        name: "AGRIBIOTEC",
        city: "",
        zip_code: 0,
        address: "",
        country: "",
        phone_number: "0",
        email: "",
        creation_date: "-0001-11-30T00:00:00+0100"

}
},

My PUT action:

/**
 * @Rest\\Put("/api_t/{id}")
 */
public function putAction(Request $request, int $id)
{
    $data = new Trial;
    
    $gSponsor = $request->get('g_sponsor');
    

    $sn = $this->getDoctrine()->getManager();
    $trial = $this->getDoctrine()->getRepository('ErpBundle:Trial')->find($id);

    if (empty($trial)) {
        return new View("user not found", Response::HTTP_NOT_FOUND);
    }

    
    $trial->setGSponsor($gSponsor);
    

    $sn->flush();

    $response=array("id" => $id, "status" => "success");
    return new JsonResponse($response);
}

Thank you very much!

to display value you need to use template setting of column:

{
   id: "_SponsorName", // needed for edit
   template: function(row) {
      return (row.g_sponsor || "") && row.g_sponsor.name
   }
}

to set such a value I would use rules setting of datatable:

rules:{
   _SponsorName: function (value, row) {
     if(!row.g_sponsor) row.g_sponsor = {};
     row.g_sponsor.name = value;
     return true;
   }
}

Thank you very much for taking the time to look into my issue. I’ve just tried what you proposed but it doesn’t work.
The table doesn’t show anymore and I’ve got this error message

Uncaught TypeError: Cannot set property 'rowspan' of null
at o.Mj (webix.js:881)
at o.rj (webix.js:873)
at o.render (webix.js:868)
at o.ej (webix.js:867)
at o (webix.js:14)
at new i (webix.js:11)
at Function.e.A (webix.js:201)
at o.kc (webix.js:230)
at o.kc (webix.js:242)
at o (webix.js:14)

Do you appear to know why?

Again, I’d like to thank you for your assist.

to debug properly you need to use webix_debug version.
https://webix.com/snippet/a9cbb8bc
there is working snippet. to make editor more accurate I replaced template logic with scheme.

Thank you! I’ve tried to adapt my code as in your snippet and add bricks one after another. It appears your solution is working until I add the batch property (and then it gives me the rowspan error message). Any clue why?

can you provide a snippet with error?

Thanks for your help all along! This is kinda working now for displaying the value, I didn’t add the header property which gave me the rowspan error.
It still doesn’t show the value when I add the editor for some reason (but it gives me no error).

For my other problem, I’ve tried a lot of things but I still can’t figure how to edit it. I’ve tried to import the list of sponsors with this method:

{
  id: "_SponsorName",
  batch: "main",
  header: [null, "Sponsor", {content: "textFilter"}],
  width: 100,
  editor: 'combo',
  options:"{{ path('erp_api_cgetloadsponsors') }}",
  },

The data load being

/**
 * @Rest\\Get("/api/sponsorlist/get")
 */
public function cgetLoadSponsorsAction()
{
    $em = $this->getDoctrine()->getManager();
    $repo = $em->getRepository('ErpBundle:Sponsor');
    $clients = $repo->findAll();
    $array = array();
    foreach ($clients as $key =>$client){
        $array[$key]['id'] = $client->getId();
        $array[$key]['value'] = $client->getName();
    }
    $view = view::create($array);
    $view->setFormat('json');
    return $view;
}

It’s working great, I’ve got the list of sponsors in my editor which is the behavior I was expecting.

But when I try to select one of the sponsor I still got the same error:

Type error: Argument 1 passed to ErpBundle\\Entity\\Trial::setGSponsor() 
must be an instance of ErpBundle\\Entity\\Sponsor, string given.

I’ve tried to set the value to an instance ($array[$key][‘value’] = $client) but same problem occurs.

I don’t want to edit the sponsor from this table. Just being able to chose one of the list. I’ve got a column sponsor_id in my db but even when I enter an id it considers it as a string and not as an instance of the sponsor entity.

If someone from webix comes here, is this the kind of support you provide if I update to PRO version? As you can see I’m a beginner and try to learn by doing but still lack a lot of skills, especially when it comes to javascript :slight_smile:

Did you apply scheme and rules settings shown in prev snippet?

I have found a solution which is working!

But I still have a weird display issue.

If I add an editor: text the value is displayed. But if I try to add an editor:select, the value isn’t displayed. I can choose another value, it will then be displayed but when I refresh it will once again disappear.

Maybe I should open a new thread for this issue?

My code right now looks like this:

    webix.ready(function () {
    webix.ui({
        container: 'test',
        rows: [
            {...},
            {
                view: "datatable",
                editable: true,
                id: 'table',
                scheme: {
                    $init: function (row) {
                        row._SponsorName = (row.g_sponsor || "") && row.g_sponsor
.name
                    }
                },
                rules: {
                    _SponsorName: function (value, row) {
                        if (!row.g_sponsor) row.g_sponsor = {};
                        row.g_sponsor = value;
                        return true;
                    }
                },
                columns: [
                    {
                        id: "g_project_manager",
                        header: [{
                            content: "columnGroup",
                            closed: false,
                            batch: "main",
                            groupText: "main information",
                            colspan: 20,
                            width: 200
                        }, "Project Manager", {content: "textFilter"}],
                        editor: "text",
                        width: 150,
                        sort: "string"
                    },
                    {
                        id: "_SponsorName",
                        batch: "main",
                        header: [null, "Sponsor", {content: "textFilter"}],
                        width: 100,
                        editor: 'combo',
                        options:"{{ path('erp_api_cgetloadsponsors') }}",
                    },
                    ....

For information (maybe someone will try to do the same thing :)), this is what I did to solve my issue.

I’ve modified your code a bit because it was updating the sponsor name value in the sponsor table instead of the sponsor_id in the trial table:

rules: {
         _SponsorName: function (value, row) {
          if (!row.g_sponsor) row.g_sponsor = {};
          row.g_sponsor = value;
          return true;
               }
            },

And I’ve updated my api controller:

$sponsor = $request->get('g_sponsor');
$gSponsor = $this->getDoctrine()->getManager()->getRepository('ErpBundle:Sponsor')
->findOneById($sponsor);
$trial->setGSponsor($gSponsor);

Again, Thank you very much for your help. It is truly greatly appreciated.

if you need to change sponsor_id instead of name, then you have to operate with that field:

                scheme: {
                    $init: function (row) {
                        row._SponsorName = (row.g_sponsor || "") && row.g_sponsor
.sponsor_id //not name
                    }
                },
rules: {
         _SponsorName: function (value, row) {
          if (!row.g_sponsor) row.g_sponsor = {};
          row.g_sponsor.sponsor_id = value;
          return true;
               }
            },

It is not working anymore with your new code.
Do you think the code I show you above was working by accident?

As a reminder here is the Json i receive in my view:

{
id: 38,
g_project_manager: "Gerard",
g_departement: 12120,
g_country: "France",
g_technician: "Alphonse",
g_cofrac_code: "Code",
g_year: 2017,
g_efip: "2",
g_hfieds: "E",
g_crop_code: "Ma",
g_sponsor: { //= 13 in the sponsor_id column in the db
    id: 13,
    sponsor_code: "AGB",
    name: "AGRIBIOTEC",
    city: "",
    zip_code: 0,
    address: "",
    country: "",
    phone_number: "0",
    email: ""
}

the sponsor_id is the equivalent of g_sponsor, this is the field I need to update.
And what I want to display is the “name” field associated to this sponsor_id.

The code I showed you above kind of gives me the behavior I’m expecting:

with a text editor, the “name” value is correctly displayed, if I want to edit it, it works if I update the value with an id (not a name).

with a select or a combo editor, I can chose a name in the list, and it will be updated which is great. But the sponsor column is blank (while there is a value in the db and it’s displayed correctly with a text editor).

I’ve also tried something else; I’ve removed the rule and updated the column as below:

{
                        id: "g_sponsor",
                        template: function(row) {
                            return (row.g_sponsor || "") && row.g_sponsor.name
                        },
                        batch: "main",
                        header: [null, "Sponsor", {content: "textFilter"}],
                        width: 100,
                        editor: 'combo',
                        options:"{{ path('erp_api_cgetloadsponsors') }}"

                    },

It is working! It displays the sponsor name, I have the list of sponsors in the combo editor, I can choose one and it will be updated in the db. BUT when I select one of the sponsor in the list, it will first display “undefined”. If I reload the page, it will then display the correct value! I’m close to my goal but I can’t have it to work without someting weird happening everytime :slight_smile:

I think I need an if/else statement in the template property. Are you able to help me with this one last time?

The option list looks like this:

0	
   id	   4
   value	   "dfgdfg"
1	
   id	5
   value	 "FUTURECO BIOSCIENCES, S.A.U."
2	
   id	6
   value	"ACTARIA"

replace sponsor_id with id in scheme and rules

                scheme: {
                    $init: function (row) {
                        row._SponsorName = (row.g_sponsor || "") && row.g_sponsor
.id //not sponsor_id
                    }
                },
rules: {
         _SponsorName: function (value, row) {
          if (!row.g_sponsor) row.g_sponsor = {};
          row.g_sponsor.id = value;
          return true;
               }
            },

I don’t understand why but the following works perfectly:

scheme: {
                $init: function (row) {
                    row._SponsorName = (row.g_sponsor || "") && row.g_sponsor
.id //not sponsor_id
                }
            },
rules: {
     _SponsorName: function (value, row) {
      if (!row.g_sponsor) row.g_sponsor = {};
      row.g_sponsor = value; //I removed id here
      return true;
           }
        },

If I let row.g_sponsor.id = value; the post parameter is “{“id”:“6”}” instead of “6”. It gives me no error but fill in my sponsor table with a null value.

I have 2 questions then:

  • Is this code ok? you didn’t like it earlier when scheme and rules didn’t match.

  • How is this showing me correctly the name of the sponsor as I specify row.g_sponsor.id and not row.g_sponsor.name in the scheme?

in json object seen that g_sponsor is an object, but you want it to be a number. something is messed.

So this is the correct way to do it?

{
       id: "g_sponsor",
       template: function(row) {
            return (row.g_sponsor || "") && row.g_sponsor.name
                },
       batch: "main",
       header: [null, "Sponsor", {content: "textFilter"}],
       width: 100,
       editor: 'combo',
       options:"{{ path('erp_api_cgetloadsponsors') }}"

 },

I still have an issue with the select editor values recognized as undefined at first but I think this is another issue.

Thank you very much for your help, I’m a beginner and truly amazed how people are willing to help each other!

It is actually a number in the db (equal to the id of the sponsor in relationship). It retrieves the object in relationship with this id in the json.
Maybe it has to do with how doctrine manage database?

If you do not retrieve an object and pass g_sponsor as is, then you will not need scheme and rules methods.

in this scenario you do not need template. remove it.