De Looper Class
DE LOOPER CLASS
Een looper biedt qua opmaak meer mogelijkheden dan een tabel. Echter de basis functionaliteiten van een table zijn uitgebreider.
Met de LooperEvent class vullen we de mogelijkheden van standaard looper aan.
Opmerking: dit is een eerste opzet van de class. Er zijn nog veel verbeteringen en aanvullingen mogelijk.
Een leuk opensource project(je)
Met de LooperEvent class vullen we de mogelijkheden van standaard looper aan.
Opmerking: dit is een eerste opzet van de class. Er zijn nog veel verbeteringen en aanvullingen mogelijk.
Een leuk opensource project(je)
Hieronder de code van de Class. Het is wat veel om de toepassing van de class hier te beschrijven. Hiervoor een zal ik het project in een zip file bijvoegen. (let op de database die ik gebruik is MariaDB, deze moet je mogelijk even aanpassen)
DECLARATION
cLooperEvent is a Class
PRIVATE
// naam van de looper control
m_cLooper is Control
// naam van bestand of query
m_sTable is string
// naam van het sleutel veld
m_sKey is string
// save en delete button van update formulier
m_cButtonSave is Control
m_cButtonDelete is Control
// originele achtergrondkkleur van geselecteerde rij
m_nBackgroundColor is int
// onthoud huidige sorteer kolom en volgorde (1 = oplopend, 0 = geen, -1 = aflopend)
m_cSortControl is Control
m_sSortControl is string
m_nSortSequence is int
PUBLIC
// regelnummer
m_nRow is int
END
// naam van de looper control
m_cLooper is Control
// naam van bestand of query
m_sTable is string
// naam van het sleutel veld
m_sKey is string
// save en delete button van update formulier
m_cButtonSave is Control
m_cButtonDelete is Control
// originele achtergrondkkleur van geselecteerde rij
m_nBackgroundColor is int
// onthoud huidige sorteer kolom en volgorde (1 = oplopend, 0 = geen, -1 = aflopend)
m_cSortControl is Control
m_sSortControl is string
m_nSortSequence is int
PUBLIC
// regelnummer
m_nRow is int
END
CONSTRUCTOR
PROCEDURE Constructor(pLooper is Control, pTable is string, pKey is string, pButtonSave is Control = Null, pButtonDelete is Control = Null)
m_cLooper <- pLooper
m_sTable = pTable
m_sKey = pKey
m_cButtonSave <- pButtonSave
m_cButtonDelete <- pButtonDelete
DESTRUCTOR
PROCEDURE Destructor()
METHODE
// reset vorig geselecteerde item
PROCEDURE DeselectRow()
m_nRow = 0
METHODE
// detecteer geselecteerde rij uit en visualiseer deze
PROCEDURE SelectRow()
m_nRow = LooperSelect(m_cLooper)
METHODE
// preset invoeren nieuw item
PROCEDURE InsertItem()
DeselectRow()
HReset(m_sTable)
m_cButtonDelete..Visible = False
m_cButtonSave..Visible = True
FileToPage()
RESULT True
METHODE
// geselecteerde rij detecteren ,data ophalen en gegevens op formulier plaatsen
PROCEDURE UpdateItem(pKey)
SelectRow()
IF HReadSeekFirst(m_sTable,m_sKey,pKey) = True THEN
m_cButtonDelete..Visible = False
m_cButtonSave..Visible = True
FileToPage()
RESULT True
ELSE
RESULT False
END
METHODE
// geselecteerde rij detecteren en wacht op bevestiging om te verwijderen
PROCEDURE DeleteItem(pKey)
SelectRow()
IF HReadSeekFirst(m_sTable,m_sKey,pKey) = True THEN
m_cButtonSave..Visible = False
m_cButtonDelete..Visible = True
FileToPage()
RESULT True
ELSE
RESULT False
END
METHODE
// verwijder item na bevestiging
PROCEDURE DeleteConfirmed(pKey)
bResultaat is boolean = False
// record nogmaals inlezen (kan inmiddels zijn verwijderd door andere gebruiker)
IF HReadSeekFirst(m_sTable,m_sKey,pKey) = True THEN
IF HDelete(m_sTable) = True THEN
LooperDelete(m_cLooper,m_nRow)
m_nRow = 0
bResultaat = True
ELSE
Info("Het item kan niet worden verwijderd.")
END
ELSE
// record niet gevonden
Info("Het item is mogelijk al verwijderd door een andere gebruiker of proces.")
END
RESULT bResultaat
METHODE
// gewijzigd of nieuw item opslaan
PROCEDURE SaveItem(pKey)
bResultaat is boolean = False
IF pKey = 0 THEN
// nieuw item toevoegen
PageToFile(MyPage,m_sTable)
bResultaat = HAdd(m_sTable)
ELSE
// wijzigen bestaand item
IF HReadSeekFirst(m_sTable,m_sKey,pKey) = True THEN
PageToFile(MyPage,m_sTable)
bResultaat = HModify(m_sTable)
END
END
IF bResultaat = False THEN Info("Het item kan niet worden opgeslagen.")
RESULT bResultaat
METHODE
// sorteer looper op gekozen kolom
PROCEDURE SortColumn(pControl is Control, sColumn is string) : string
sSortSequence is string
IF pControl..Name = m_sSortControl THEN
// zelfde kolom, dan sorteer volgorde omkeren (toggle -1 / 1)
m_nSortSequence = m_nSortSequence = 1 ? -1 ELSE 1
ELSE
// andere kolom, standaard van laag naar hoog
m_nSortSequence = 1
IF m_sSortControl <> ""
// zet voorgaade sorteer kolom op 'neutraal'
m_cSortControl..Caption = m_cSortControl..InitialValue
END
END
IF m_nSortSequence = -1 THEN
pControl..Caption = pControl..InitialValue+ " ▼"
sSortSequence = sColumn+" desc"
ELSE
pControl..Caption = pControl..InitialValue+ " ▲"
sSortSequence = sColumn+" asc"
END
// reset vorige sorteerkolom en bewaar huidige instelling
m_sSortControl = pControl..Name
m_cSortControl <- pControl
// bewaar table/query (control) kolom (header) en sortering (asc/desc)
CookieWrite("carbase_"+m_cLooper..Name,m_sSortControl+","+sSortSequence)
RESULT sSortSequence
METHODE
// restore laatst gekozen sorteer kolom (controlnaam, veldnaam sortering)
PROCEDURE SortColumnRestore() : string
// restore kolom en sortering
sRestoreSortSequence is string = CookieRead("carbase_"+m_cLooper..Name)
sControlName is string = ExtractString(sRestoreSortSequence,1,",")
IF ControlExist(sControlName) THEN
// geldige control, save de kolomnaam + sortering, restore sorteer symbool, geef sortering terug
m_nSortSequence = (ExtractString(sRestoreSortSequence,2," ") = "asc" ? 1 ELSE -1)
m_sSortControl = sControlName
m_cSortControl <- {sControlName,indControl}
{sControlName,indControl}..Caption = {sControlName,indControl}..InitialValue+(m_nSortSequence = 1 ? " ▲" ELSE " ▼")
RESULT ExtractString(sRestoreSortSequence,2,",")
ELSE
// control bestaat niet (meer)
RESULT ""
END
LINK: Carbase project ZIP
Voor een eerste opzet ziet het er goed uit. Zulke objecten zouden meer gemaakt en gedeeld moeten worden. Zo kun je Library's maken met zulke objecten.
BeantwoordenVerwijderenIkzelf had op 29 January 2016 al zo'n control object gedemonstreerd in Chaam. Daar ging het om een Listcontrol waarin ik een sleep functie had ingebouwd als demo. Je kon daarmee items in de listcontrol naar boven of beneden verplaatsen, door te slepen.
Ik liep daar echter tegen een probleempje aan.
In de constructor kun je elke control doorgeven die er bestaat. Dus als je in deze looper class een edit veld doorgeeft, dan zal de looperclass exceptions gaan creeren.
Dit had ik als volgt opgelost
Maak van de constructor een private constructor:
PROCEDURE PRIVATE Constructor(pLooper is Control, pTable is string, pKey is string, pButtonSave is Control = Null, pButtonDelete is Control = Null)
m_cLooper <- pLooper
m_sTable = pTable
m_sKey = pKey
m_cButtonSave <- pButtonSave
m_cButtonDelete <- pButtonDelete
Maak een Public global factory method aan met precies dezelfde parameters als de constructor en noem die Create. De methode geeft als resultaat een cLooperEvent terug.
Je maakt nu in de Create methode een Local variabele aan van het type cLooperEvent.
Gebruik hiervoor het keywoord Dynamic. (op die manier maak je wel alvast een variabele aan, maar die heeft nog geen verwijzing naar een object). Die variabele heeft dan dus de waarde NULL
Vervolgens controleer je of de control die je hebt meegegeven in de parameters een looper control is. Zo ja dan ken je een nieuw object toe aan de lopper variabele. Zo Nee dan laat je die op NULL staan.
Geef als result van de methode de locale looper variabele terug
Op die manier krijgt aanroepende proces altijd correct cLooperobject terug. Of de waarde NULL, als het aanroepende proces een verkeerde control heeft doorgegeven.
Dat moet dan we afgevangen worden
In het aanroepende proces moet de variabel van het type cLooperEvent ook DYNAMIC zijn.
Hier beneden de uitwerking van de factory method:
(Een factory method maakt een object van zijn eigen classe aan en geeft dat terug in de RESULT)
PROCEDURE PUBLIC GLOBAL Create( pLooper is Control, pTable is string, pKey is string, pButtonSave is Control = Null, pButtonDelete is Control = Null ) : cLooperEvent
lxoLooper is cLooperEvent Dynamic
if pLooper..Type = typLooper then
lxoLooper = new cLooperEvent( pLooper, pTable, pKey, pButtonSave, pButtonDelete)
end
Result lxoLooper
In het aanroepende proces ziet het er dan zo uit:
MyLooperEvent is cLooperEvent Dynamic
MyLooperEvent = cLooperEvent.Create( MyLooperControl, MyTable, MyKey, MyButtonSave, My ButtonDelete)
if MyLooperEvent = Null then
Error("Verkeerd type control doorgegeven aan cLooperEvent.Create() functie")
end
Het werkt beter zo.
Als je dit niet zo doet, dan zal je waarschijnlijk overal waar je een object nodig hebt van cLooperEvent een controle moeten gaan doen of de control die je doorgeeft wel een looper control is. Op deze manier doe je die controle maar één keer en ook nog op de plek waar het thuis hoort. Namelijk in de cLooperEvent classe zelf.
Deze techniek werkt super.
Veel plezier ervan.
Groet Louis Wassenberg
Hallo Louis,
VerwijderenDank je voor de suggestie. Maak de class idd beter.
In 2016 werkte ik nog niet met WebDev. Heb je presentatie dan ook niet gezien in Chaam. Zou leuk zijn als je deze ook op de blog publiceert. Benieuwd hoe je dat doet. Ben erg gecharmeerd van drag-and-drop (vergoot de gebruikersvriendelijkheid van een applicatie namelijk enorm).
Groet,
Henk Ouwejan
Beste Henk, volgens mij werkt download van de zip niet. Kan je mij deze bezorgen a.u.b.
BeantwoordenVerwijderen