How to get an html element inside another element?

I'm using a TWebDBTableControl and in the GetCellChildren event I'm creating an input inside each column of each record, and a save and edit button, when I click on the edit button the edit button is visible false and the save button is visible true and I need to show the input in the row of all columns to make the TWebDBTableControl editable and when you click on the save button, the input will disappear and only the values in the table will remain.

How would I do it?

I'm trying with the code below.

procedure TFViewPedidoVendaEdit.gridProdutoGetCellChildren(Sender: TObject;
  ACol, ARow: Integer; AField: TField; AValue: string;
  AElement: TJSHTMLElementRecord);
var
  RecordId: JSValue;
  btnAlterar, btnSalvar: TWebButton;
  edtCampo: TWebDBEdit;
  i: Integer;

  procedure AlterarButtonClick(Sender: TObject);
  begin
    btnAlterar.Visible := False;
    btnSalvar.Visible := True;
  end;

  procedure SalvarButtonClick(Sender: TObject);
  begin
    btnAlterar.Visible := True;
    btnSalvar.Visible := False;

    **for i := 0 to Pred(gridProduto.Columns.Count) do**
**    begin**
**      //how to get the input inside the field?**
**    end;** 

  end;
begin
  if ARow = 0 then
    Exit;

  if gridProduto.Columns[ACol].Title = 'Ações' then
  begin
    RecordId := TCrudUtils.GetIdFromObject(
      cdsProduto.Connection.Model.DefaultSchema.FindEntityType(cdsProduto.EntitySetName),
      cdsProduto.CurrentData
    );
    btnAlterar := TWebButton.Create(nil);
    btnAlterar.Caption := '<i class="fas fa-pen"></i>';
    btnAlterar.OnClick := @AlterarButtonClick;
    btnAlterar.ElementClassName := 'btn btn-outline btn-default btn-sm';
    btnAlterar.Hint := 'Alterar Registro';
    btnAlterar.ShowHint := True;
    btnAlterar.ParentElement := AElement.element;
    btnAlterar.WidthStyle := TSizeStyle.ssAuto;
    btnAlterar.HeightStyle := TSizeStyle.ssAuto;

    btnSalvar := TWebButton.Create(nil);
    btnSalvar.Caption := '<i class="fas fa-save"></i>';
    btnSalvar.OnClick := @SalvarButtonClick;
    btnSalvar.ElementClassName := 'btn btn-outline btn-default btn-sm';
    btnSalvar.Hint := 'Salvar Registro';
    btnSalvar.ShowHint := True;
    btnSalvar.ParentElement := AElement.element;
    btnSalvar.WidthStyle := TSizeStyle.ssAuto;
    btnSalvar.HeightStyle := TSizeStyle.ssAuto;
    btnSalvar.Visible := False;
  end
  else
  begin
    edtCampo := TWebDBEdit.Create(nil);
    edtCampo.DataSource := AField.DataSet.DataSource;
    edtCampo.DataField  := AField.FieldName;
    edtCampo.ElementID  := 'produto_'+AField.FieldName;
    edtCampo.ParentElement := AElement.element;
    edtCampo.Visible := False;

  end;
end;

Give each of these edit controls a unique name and use
form.FindComponent(Name) to get the control you want to hide or show

I am creating the component of this described below:

else
begin
  edtField := TWebDBEdit.Create(nil);
  edtFieldName := 'edt'+AField.FieldName+IntToStr(ARow);
  edtCampo.DataSource := AField.DataSet.DataSource;
  edtCampo.DataField := AField.FieldName;
  edtCampo.ElementID := 'product_'+AField.FieldName+'_'+IntToStr(ARow);
  edtCampo.ParentElement := AElement.element;
  AElement.element.setAttribute('data-field',AField.FieldName);
  AElement.element.innerHTML := '<span>'+AValue+'</span>'+
  '<input id="'+'product_'+AField.FieldName+'_'+IntToStr(ARow)+'" class="form-control form-control-sm" value="'+AValue+'" style="display : none;" disabled="">';
end;

I'm using bootstrap in the edits, so to visualize the field if I only do campo.visible = true or false it doesn't work, I need to change the HTML display tag of the input:

<input id="'+'product_'+AField.FieldName+'_'+IntToStr(ARow)+'" class="form-control form-control-sm" value="'+AValue+'" style="display: none;" disabled="">

An observation, I need to make visible all the edits that are in the current line when I click on the edit button and then make all the edits invisible in the current line when I click on save, so I need to LOOP all the edits that are in the current line , the problem is getting the current row in the TableControl and LOOPing all the columns.

I don't see you use FindComponent(name) in your code?
I have no information how you select a row, so it is unclear how you should get the current row. If you implement OnClickCell for example, this will return via the ARow parameter the row clicked.

I sent in the first post the complete code of the gridProdutoGetCellChildren

I'm not doing a FindComponent.
In the OnClick event of my Edit button I need to get the current line and not in the OnClickCell.

What I just don't know is how to get the current line.

The steps are this:

I create two buttons, an Edit Button and a Save Button.
I add the events:

btnEditar.OnClick := @EditarButtonClick;
btnSave.OnClick  := @SaveButtonClick;

In the @SavarButtonClick event I need to get the current line, loop through all edits that are ParentElement:
edtCampo.ParentElement := AElement.element;

Make all Edits visible in the @EditarButtonClick event;

procedure SaveButtonClick(Sender: TObject);
begin
    btnAlterar.Visible := True;
    btnSave.Visible := False;

    for i := 0 to Pred(gridProduct.Columns.Count) do
    begin
          ???????? Get the current line and each edit.
    end;
end;

The grid does not keep on a cell level basis a reference to child controls.
Hence, my suggestion to use unique instance names and use FindComponent() to get the control by its unique name.

And how do I make the edit visible in the html through the component, do you have any examples?

We are exchanging several messages and getting nowhere, do you have a more practical example, like a code example of how I could do this?

Is simply toggling the Visible boolean property not working?

No

I managed to do it this way, but when I change the value in the input and send it back to the TR it returns the old value and not the new value.

procedure AlterarButtonClick(Sender: TObject);
  begin
    btnAlterar.Visible := False;
    btnSalvar.Visible := True;
    for i := 0 to Pred(gridProduto.Columns.Count) do
    begin
      if gridProduto.Columns[i].Title <> 'Ações' then
      begin
        valor := gridProduto.Cells[i,indexRow];
        gridProduto.CellElements[i,indexRow].innerHTML := '<input id="gridProd'+gridProduto.Columns[i].DataField+IntToStr(indexRow)+'" type="text" class="form-control form-control-sm" value="'+valor+'"/>';
      end;
    end;
  end;

  procedure SalvarButtonClick(Sender: TObject);
  begin
    btnAlterar.Visible := True;
    btnSalvar.Visible := False;
    for i := 0 to Pred(gridProduto.Columns.Count) do
    begin
      if gridProduto.Columns[i].Title <> 'Ações' then
      begin
        elemento := document.getElementById('gridProd'+gridProduto.Columns[i].DataField+IntToStr(indexRow));
        valor := elemento.getAttribute('value');
        elemento.remove;
        gridProduto.CellElements[i,indexRow].innerHTML :=  valor;
      end;
    end;
  end;

Before changing

Changing

Changed and returns to previous value

This type of feature could be native to TWebDBTableControl, where there could be an option to enable the control buttons on the grid and if you want to enable editing on the grid and create all this feature automatically then it is optional and does not affect the legacy component. These enablers increase development productivity and become the attractive component for developers, because if they have difficulties they will migrate to other technologies or components.

We'll consider this but given it can't be predicted what a developer wants to do from OnGetCellChildren, coming up with a generic solution is not trivial.

Hello Bruno,

When I'm trying to find the component with FindComponent it is not found, am I doing something wrong?

Another problem I'm having is when I create the TWebEdit inside the row/column of the table and I change the value in Edit I need to get that value from Edit and when I save it stays in the grid with the new value and when I try to get the Input value is always the old value.

procedure SalvarButtonClick(Sender: TObject);
  begin
    btnAlterar.Visible := True;
    btnSalvar.Visible := False;
    for i := 0 to Pred(gridProduto.Columns.Count) do
    begin
      if gridProduto.Columns[i].Title <> 'Ações' then
      begin
        elemento := document.getElementById('gridProd'+gridProduto.Columns[i].DataField+IntToStr(indexRow));
        value := elemento.getAttribute('value');
        oldValue := elemento.getAttribute('old-value');
        campo := FindComponent('edt'+gridProduto.Columns[i].DataField+IntToStr(indexRow));

        if Assigned(campo) then
        begin
          if value <> oldValue then
            gridProduto.CellElements[i,indexRow].innerHTML := Value
          else
            gridProduto.CellElements[i,indexRow].innerHTML :=  oldValue;

          campo.Free;
        end;
        elemento.remove;
      end;
    end;
  end;

It is hard to test & judge such partial code snippet without having any further full scope.

Here is a code snippet you add to a form with a button that shows how FindComponent() is working:

procedure TForm3.WebButton1Click(Sender: TObject);
var
  edt: TWebEdit;
begin
  edt := TWebEdit.Create(Self);
  edt.Name := 'myedit1';
  edt.Parent := Self;
  edt.Top := 0;

  edt := TWebEdit.Create(Self);
  edt.Name := 'myedit2';
  edt.Parent := Self;
  edt.Top := 40;

  TWebEdit(FindComponent('myedit1')).Text := '1';
  TWebEdit(FindComponent('myedit2')).Text := '2';
end;