Master-detail on new record

Hello!

I have a situation, where I open a new form and append a new master, fill some data and append several detail rows, where I also apply some data.

Short code explanation - of what should do :slight_smile: :

Insert a new row in wdsOdvoz
Insert rows into wdsOdvozPodrobno according to the result of wdsPosode - there also insert data from wdsPosode.Posoda to wdsOdvozPodrobno field "Posoda".

After clicking OK it should save the new records (master and detail).

I tried to also check the web/master-detail demo, but it's not present in my installation (latest, I reinstalled it to be sure), below is the content of the demo/web folder.

image

Here's the code:

unit Odvoz;

type
  TfrmOdvoz = class(TWebForm)
    wdsLokacijaPosode: TXDataWebDataSet;
    wdsOdvozPodrobno: TXDataWebDataSet;
    wdsOdvoz: TXDataWebDataSet;
    connServer: TXDataWebConnection;
  end;

implementation

{$R *.dfm}

procedure TfrmOdvoz.FormCreate(Sender: TObject);
begin
  PripraviOdvoz(0);  //0 = new
end;


procedure TfrmOdvoz.Log(Txt: string);
begin
  Console.log(Txt);
end;

procedure TfrmOdvoz.PripraviOdvoz(OdvozId: integer);
begin
  wdsOdvoz.Close;
  wdsOdvoz.QueryString := '$filter=Id eq '+OdvozId.ToString+'&$orderby=Id desc&$expand=Podrobno,Podrobno/Posoda,Lokacija,Lokacija/Stranka';
  wdsOdvoz.QueryString := '$orderby=Id desc&$top=1&$expand=Podrobno,Podrobno/Posoda,Lokacija,Lokacija/Stranka';
  wdsOdvoz.Load;
end;

procedure TfrmOdvoz.wdsOdvozAfterOpen(DataSet: TDataSet);
begin
  // Odvoz pripravljen, vpiši podrobnosti in naloži odvozpodrobno za dodajanje vrstic
  wdsOdvoz.Insert;
end;

procedure TfrmOdvoz.wdsOdvozNewRecord(DataSet: TDataSet);
begin
  wdsOdvoz.FieldByName('CasOdvoza').AsDateTime := Now;
  wdsOdvoz.FieldByName('Vozilo').Value := modMain.Vozilo; // See note 1
  wdsOdvoz.Post;

  wdsOdvozPodrobno.Close;
  wdsOdvozPodrobno.SetJsonData(wdsOdvoz.FieldByName('Podrobno').Value);   
  wdsOdvozPodrobno.Load;
end;

procedure TfrmOdvoz.wdsOdvozPodrobnoAfterOpen(DataSet: TDataSet);
begin
  wdsLokacijaPosode.Close;
  wdsLokacijaPosode.QueryString := 'expand=posoda&$filter=posoda/tip eq '+modMain.FrakcijaId.ToString+' and lokacija/stranka/id eq '+modMain.StrankaId.ToString;
  wdsLokacijaPosode.Load;
end;

procedure TfrmOdvoz.wdsOdvozPostError(DataSet: TDataSet; E: EDatabaseError;
  var Action: TDataAction);
begin
  Console.log(E.Message);
end;

procedure TfrmOdvoz.wdsLokacijaPosodeAfterOpen(DataSet: TDataSet);
begin
  // Fill OdvozPodrobno with data. Add as many rows as there are in wdsLokacijaPosode and fill the field wdsOdvozPodrobno.Posoda with the field wdsLokacijaPosode.Posoda.
Also add a reference to wdsOdvoz (master)

  wdsLokacijaPosode.First;

  Log(wdsLokacijaPosode.RecordCount.ToString);

  while not wdsLokacijaPosode.Eof do begin
    wdsOdvozPodrobno.Insert;
    wdsOdvozPodrobno.FieldByName('Odvoz').Value := TObject(TXDataWebDataSet(wdsOdvoz).CurrentData); // Add reference to master (wdsOdvoz)
    wdsOdvozPodrobno.FieldByName('Posoda').Value := TObject(wdsLokacijaPosode.FieldByName('Posoda').Value);  // See note 2 below
    wdsOdvozPodrobno.FieldByName('Volumen').AsInteger := 0;
    wdsOdvozPodrobno.FieldByName('Teza').AsInteger := 0;
    wdsOdvozPodrobno.FieldByName('Kolicina').AsInteger := 0;   
    wdsLokacijaPosode.Next;
  end;
  wdsOdvozPodrobno.First;
end;

procedure TfrmOdvoz.ApplyNote(Sender: TObject);
begin
  if Sender = btnOpomba1 then
    memoNotes.Text := Settings.Note1;

  if Sender = btnOpomba2 then
    memoNotes.Text := Settings.Note2;

  if Sender = btnOpomba3 then
    memoNotes.Text := Settings.Note3;
end;

procedure TfrmOdvoz.btnSaveClick(Sender: TObject);
begin
  if wdsOdvoz.State in dsEditModes then
    wdsOdvoz.Post;

  if wdsOdvozPodrobno.State in dsEditModes then
    wdsOdvozPodrobno.Post;

  wdsOdvoz.Edit;
  wdsOdvoz.FieldByName('Podrobno').Value := TJSArray.new(TJSJSON.parseObject(TJSJSON.stringify(TJSObject(wdsOdvozPodrobno.CurrentData))));
  wdsOdvoz.Post;
  wdsOdvoz.ApplyUpdates;
end;

procedure TfrmOdvoz.wdsOdvozAfterApplyUpdates(Sender: TDataSet; Info: TResolveResults);
begin
  frmMain.ShowStranke;
end;

end.

The code has several problems:

  1. The field "Posode" doesn't get filled
  2. Clicking on Save button doesn't do anything - nothing is posted to the server

Probably the code is a mess, because I'm not sure what to do :slight_smile: I would really need some help here, because it's my first master-detail with inserting and i'm quite lost.

Kind regards,
m@rko

The master-detail demo is in XData installation. Check it in the folders demos\web\master-detail. Probably (hopefully) it will help you out if you check and try to move from there.

If you still have questions, please let me know.

It's also worth mentioning (and I will write this as a reference for other customers too) that a TDataset in TMS Web Core (be it TXDataWebDataset, TWebClientDataset, etc.), is not conceptually equivalent to a regular Delphi dataset.

You are not connecting directly to a database. A Post doesn't spawn an INSERT/UPDATE request in the database.

The Web Core dataset is an interface to edit the underlying JavaScript object and its properties. That's about it. Then you should use the modified objects to somehow send it to the server, either manually, using a component performing HTTP requests, or, in the case of XData, using ApplyUpdates method, which does that job more or less automatically in the most simple scenarios.

Having that paradigm in mind will help you out understand how it's working and how to properly implement your code using it.

Ok, thank you for the demo location tip :slight_smile:

One more question: Is this correct in WebCore?

wdsTest.Append;
wdsTest.FieldByName('ID').AsInteger := 1;
wdsTest.Post:

(ApplyUpdate is used later)

Is Append/Insert async and must add the code on AfterAppend/AfterInsert or can be done in one place like above?

Second question: in a master-detail what is the correct procedure for saving the data to server - I must AopplyUpdates on both datasets and if yes - master first then detail?

Append, Insert, Edit, Post, Delete dataset operations are all synchronous, your code works fine.

There is no right answer to this. As I mentioned, the datasets only edit objects. It depends on how your server expects data.

I personally think it's easier to make your server receive both master and detail as once, with the detail being a JSON array of detail objects. In this case, a single ApplyUpdates in the master dataset should be enough.