How can i get rid of the error = " Control must be added to the page first." on flet python

123 views Asked by At

Here is my complete code, it does not seem to be any other problem than when adding the table control to the page, because it is returning data to fill said table, only that when calling the function that fills the table I receive said error.

#CONEXION A DB
import flet as ft
from flet import Tab, Tabs
from flet_route import Params, Basket
from Clases.controls import *
#INSTANCIA PARA UTILIZAR LOS CONTROLES
control = Controls()
# Nombre de la tabla
nombre_tabla = "clientes"
# Obtener los nombres de las columnas
nombres_columnas = control.obtenerNombresColumnas(nombre_tabla)
#Style attributes for header class
header_style= {
      "height": 60,
      "bgcolor": "#081d33",
      "border_radius": ft.border_radius.only(top_left=15, top_right=15),
      "padding": ft.padding.only(left=15, right=15),
}
#Method that creates and return textfield
def search_field(function: callable):
      return ft.TextField(
            border_color="transparent",
            width=350,
            height=20,
            text_size=15,
            content_padding=0,
            cursor_color="white",
            cursor_width=1,
            color="white",
            hint_text="Search",
            on_change=function
      )
#Method that adds a container to the search field
def search_bar(control: ft.TextField):
      return ft.Container(
            bgcolor="white10",
            border_radius=6,
            opacity=0,
            animate_opacity=300,
            padding=8,
            content = ft.Row(
                  spacing=10,
                  vertical_alignment="center",
                  controls=[
                        ft.Icon(
                              name=ft.icons.SEARCH_ROUNDED,
                              size=20,
                              opacity=0.85
                        ),
                        control
                  ]
            )

      )
#Define header class
class Header(ft.Container):
    def __init__(self, page):
        super().__init__(**header_style,
                         on_hover=self.toggle_search 
                         )
        #Define attributes
        #self.dt = dt
        self.page = page  # Store the 'page' parameter for later use
        #Create a textfield for search
        self.search_value = search_field(self.filter_data)
        #Create a search box
        self.search = search_bar(self.search_value)
        #Define other class attributes
        self.name = ft.Text("DentistaBromista", color="white", size=18, weight=700)
        self.logout = ft.IconButton(icon=ft.icons.LOGOUT, icon_color="white", tooltip="Cerrar sesion", on_click=self.logOut, width=40)
        #Compile the attributes
        self.content = ft.Row(
            expand=True,
            alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
            controls=[
                self.name,
                self.search, 
                self.logout
            ]
        )
    #Method that log out
    def logOut(self, e: ft.TapEvent):
        #VALIDAR SI SE HACE CLICK
        self.page.go("/")  # Use the stored 'page' for redirecting
    #Method that toggle search bar visibility
    def toggle_search(self, e: ft.HoverEvent):
        #SE MUESTRA SI HACES HOVER Y SI NO SE OCULTA
        self.search.opacity = 1 if e.data == 'true' else 0
        self.search.update()
    #Define a placeholder method for filter data
    def filter_data(self, e):
        print("hola")
#Define form class styling and attributes
form_style = {
    "border_radius": 8,
    "border": ft.border.all(1, "#ebebeb"),
    "bgcolor": "white10",
    "padding": 15,
}
#Method that creates and return textfield
def text_field():
    return ft.TextField(
        border_color="transparent",
        height=30,
        text_size=15,
        content_padding=0,
        cursor_color="black",
        cursor_width=1,
        color="black",
    )
    
#Defin e a container to mantain the textfield
def text_field_container(
    expand: bool | int, name: str, control: ft.TextField
):
    return ft.Container(
        expand=expand,
        height=45,
        bgcolor="#ebebeb",
        border_radius=6,
        padding=8,
        content=ft.Column(
            spacing=1,
            controls=[
                ft.Text(
                    value = name, 
                    size=9,
                    color="black",
                    weight="bold"
                ),
                control
            ]
        )
    )
#Define form class
class formClient(ft.Container):
    def __init__(self, page):
        super().__init__(**form_style)
        #Define attributes
        #self.dt = dt
        self.page = page  # Store the 'page' parameter for later use
        #Define the num of rows for textfields
        self.row1_value = text_field()
        self.row2_value = text_field()
        self.row3_value = text_field()
        
        
        #Define and contain the textfields
        self.row1 = text_field_container(True, "Nombre", self.row1_value)
        self.row2 = text_field_container(True, "Email", self.row2_value)
        self.row3 = text_field_container(True, "Telefono", self.row3_value)
        

        #Button to submit
        self.submit = ft.ElevatedButton(
            text="Añadir",
            style=ft.ButtonStyle(
                shape={"": ft.RoundedRectangleBorder(radius=8)},
            ),
            on_click=self.submit_data
        )
        #Compile the attributes
        self.content = ft.Column(
            expand=True,
            controls=[
                ft.Row(controls=[ft.Text("Llena los campos con los datos del cliente", size=20, color="black")]),
                ft.Row(controls=[ft.Text("1.Informacion personal ", size=15, color="black")]),
                ft.Row(controls=[self.row1, self.row2]),
                ft.Row(controls=[self.row3]),
                ft.Row(controls=[self.submit], alignment="end")
            ]
        )
    #Enviar los datos a la base de datos
    def submit_data(self, e:ft.TapEvent=None):
        self.row_values = (self.row1_value.value, self.row2_value.value, self.row3_value.value)
        print(self.row_values)
        # Validar si los campos no están vacíos
        if all(self.row_values):
            if control.submit_data(e, self.row_values):  # Si submit_data devuelve True
                self.clear_inputs()  # Llamar a clear_inputs
                #Mostrar que se insertó
                self.page.snack_bar = ft.SnackBar(
                    content=ft.Text("¡Se guardo correctamente el cliente!", color="white", weight="bold"),
                    action="Okey!",
                    bgcolor="green",
                    action_color="white",
                    duration=4000)
                self.page.snack_bar.open = True
                self.page.update()
            else:
                #Mostrar que no se insertó
                self.page.snack_bar = ft.SnackBar(
                    content=ft.Text("Hubo un problema al guardar el registro.", color="white", weight="bold"),
                    action="Okey!",
                    bgcolor="red",
                    action_color="white",
                    duration=4000)
                self.page.snack_bar.open = True
                self.page.update()
        else:
            dlg = ft.AlertDialog(title=ft.Text("Por favor llena todos los campos", color="white"))
            self.page.dialog = dlg
            dlg.open = True
            self.page.update()
    #Method for delete user input after submit
    def clear_inputs(self):
        #Borra los valores de los campos
        self.row1_value.value = ""
        self.row2_value.value = ""
        self.row3_value.value = ""
        self.content.update()

#Datatable styles and attributes
data_table_style = {
    "expand": True,
    "border_radius": 8,
    "border": ft.border.all(2, "#ebebeb"),
    "horizontal_lines": ft.border.BorderSide(1, "#ebebeb"),
    "columns": [
        #Traer los datos de la base de datos(los titulos de las columnas)
        ft.DataColumn(ft.Text(index, size=13, color="black", weight="bold"))
        for index in [nombres_columnas[i] for i in [1,2,3] ] #MUESTRA SOLO 4 COLUMNAS NOMBRE, TELEFONO, CORREO Y ACCIONES
    ] + [ft.DataColumn(ft.Text("AGENDAR", size=13, color="black", weight="bold"))] #Columna Acciones
    + [ft.DataColumn(ft.Text("EDITAR", size=13, color="black", weight="bold"))] #Columna Acciones
    + [ft.DataColumn(ft.Text("ELIMINAR", size=13, color="black", weight="bold"))] #Columna Acciones
}
class DataTable(ft.DataTable):
    def __init__(self):
        super().__init__(**data_table_style)
        self.data = control.get_data('clientes')

    def add_data_to_table(self):
        self.rows = []
        for row_dict in self.data:  # Itera sobre cada diccionario en la lista
            data = ft.DataRow()
            data.cells = [
                ft.DataCell(
                    ft.Text(
                        value, size=13, color="black",
                    )
                ) for value in [row_dict['nombre'], row_dict['telefono'], row_dict['email']]
            ]
            data.cells.append(ft.IconButton(ft.icons.EDIT_CALENDAR_ROUNDED))
            data.cells.append(ft.IconButton(ft.icons.EDIT))
            data.cells.append(ft.IconButton(ft.icons.DELETE))

            self.rows.append(data)
        self.update()

class mainForm:
    def __init__(self):
        super().__init__()  
        self.table = DataTable()      
    def main(self, page):
        page.title = "Registro"
        #table = DataTable()     
        header = Header(page)
        form = formClient(page)   

        content = ft.Column(
                expand=True,
                controls=[
                    #Header
                    header,
                    ft.Divider(height=2, color="transparent"),
                    #Form 
                    form,
                    ft.Divider(height=2, color="transparent"),
                    #Table
                    ft.Column(
                        scroll="auto",
                        expand=True,  
                        controls=[ft.Row(controls=[self.table])] #tabla
                    ),
                ],
            )
        
        page.add(content)
        page.update() # Update the page after adding controls
        self.table.add_data_to_table()
        return content  # Return the content, not the page
    #Regresa la vista
    def build(self,  page: ft.Page, params=Params, basket=Basket):
        content = self.main(page)
        return ft.View(
            "/formCliente",
            controls=[content],  # Add the content as a control
            bgcolor="white"
        )

I have tried to add in different ways and in different places the call to the function to fill the table with the data brought from mysql, but nothing seems to work

0

There are 0 answers