Je eerste Studio-app bouwen
Doel
Een werkende applicatie bouwen vanuit het niets: een tabel om de records in te bewaren, een scherm om ze te tonen, een werkbalk die werkt op het record dat je aanvinkt, en een zijpaneel dat een nieuw record terugschrijft.
De eerste keer kost het ongeveer veertig minuten. Niets hier is speelgoed — elke stap is de stap die je voor een echte app zou zetten, en wat je uiteindelijk hebt, is ongeveer het voorbeeld Support desk.
studio-starter-app in samples/solutions is deze app, getekend. Importeer hem via Oplossingen →
Importeren, open hem en druk overal op. Kom je daarna hier terug, dan wordt elke stap hieronder
korter.
Voordat je begint
- Je bent aangemeld als maker.
- Er zijn een omgeving en een oplossing geselecteerd in de kiezers bovenaan de pagina. Niets wat je hieronder maakt, bestaat daarbuiten.
- Je kent het voorvoegsel van de uitgever van je oplossing — de twee of drie letters voor elke
logische naam. Deze pagina schrijft
par; dat van jou kan anders zijn.
1. Een tabel om de records in te bewaren
Een app die niets toont, is een tekening. Maak eerst de tabel.
Tabellen → Nieuw, en vul in:
| Veld | Waarde |
|---|---|
| Weergavenaam | Case |
| Meervoudsnaam | Cases |
| Logische naam | par_case (het voorvoegsel wordt voor je toegevoegd) |
| Primair veld | Summary |
Sla op en voeg dan op het tabblad Velden nog drie velden toe:
| Weergavenaam | Logische naam | Type |
|---|---|---|
| Priority | par_priority | Keuze — nieuwe keuzeset par_case_priority, opties Low, Normal, High |
| Urgent | par_urgent | Ja/Nee |
| Notes | par_notes | Meerregelige tekst |
Een record dat is aangemaakt zonder Urgent aan te raken, heeft daarvoor helemaal geen waarde — en
geen waarde is geen boolean. Een expressie als record.par_urgent ? "Yes" : "No" mislukt dus met
Only booleans are allowed in this expression, precies op die records. Schrijf
(record.par_urgent ?? false) overal waar je een optioneel veld leest, en de vraag verdwijnt. Dit is
de meest voorkomende verrassing in een eerste app.
2. De app
Studio-apps → Nieuw:
| Veld | Waarde |
|---|---|
| Weergavenaam | Support desk |
| Logische naam | par_support |
| Beschrijving | Eén zin. Die toont de starttegel onder de naam. |
| Pictogram | Druk op de kiezer en kies er een — LifeBuoy past bij een supportapp |
| Vermeld in de shell | aan |
Druk op Opslaan en dan op Ontwerpen. Het canvas opent.
3. Het scherm, en wat een scherm is
Teken één frame dat het canvas vult — ongeveer 1200 × 760 is een prettig tekenformaat — en open, met het frame geselecteerd, het tabblad Scherm in de inspector rechts:
┌─ Inspector ───────────────────────┐
│ Graphic │ Screen │ Code │
├───────────────────────────────────┤
│ This object is … │
│ ┌─────────────────────────────┐ │
│ │ The screen ▾ │ │ ← choose this
│ └─────────────────────────────┘ │
│ │
│ Content that does not fit │
│ ┌─────────────────────────────┐ │
│ │ Show it — it spills out ▾ │ │
│ └─────────────────────────────┘ │
└───────────────────────────────────┘
Hernoem het frame naar Cases in het paneel Lagen. Die naam is de naam van het scherm — het is
wat navigate("Cases") later zal vragen.
Het formaat dat je tekende, is niet het uiteindelijke formaat. Alles erin is vastgezet aan de randen van het frame, en het frame vult elk venster waarin de app wordt geopend.
4. Het eigen geheugen van het scherm
Een scherm onthoudt dingen: op welke pagina van de lijst je bent, wat er in het zoekvak is getypt, welke rijen zijn aangevinkt. Dat geheugen is de schermtoestand, en die wordt één keer op het schermframe gedeclareerd.
Voeg, met het schermframe geselecteerd, op het tabblad Scherm het volgende toe onder Toestand:
| Naam | Begint als |
|---|---|
cases | [] |
caseTotal | 0 |
page | 0 |
pageSize | 20 |
picked | "" |
pickedCount | 0 |
pickedId | "" |
form | { par_name: "", par_urgent: false, par_notes: "" } |
Declareer de vorm van form; laat hem niet {}. Een koppeling die een lid leest dat een object niet
heeft, geeft een fout, en de eerste evaluatie van het scherm draait voordat iets het formulier heeft
ingevuld.
5. De lijst vullen
Zoek, nog steeds op het schermframe en op het tabblad Scherm, Handlers en zet dit bij Geladen:
loadRecords("par_case", "cases", {
top: getState("pageSize"),
skip: getState("page") * getState("pageSize"),
orderBy: "par_name",
countInto: "caseTotal"
});
Eén regel query is alles wat paginering vraagt: top en skip zijn de pagina, en countInto haalt op
hoeveel rijen er in totaal zijn — wat een pagina van twintig niet kan zeggen en wat een knop
Volgende moet weten. Zie Een lijst pagina voor pagina tonen voor
de rest.
Er is nog niets te zien. Er is niets getekend om het in te tonen.
6. Een rij, één keer getekend
Dit is de stap die mensen verrast, dus het is goed om het duidelijk te zeggen: jij tekent één rij, en het platform tekent er één per record.
-
Teken binnen het scherm een frame voor de lijst — over de volle breedte van de inhoud, een paar honderd pixels hoog. Noem het
Rows. Stel op het tabblad Scherm Dit object is → Een lijst in, en Inhoud die niet past → Ernaartoe scrollen, en dan Welke kant het scrolt → Omlaag. Een vak dat is getekend voor zes rijen, bevat er honderd zodra er een echte tabel achter zit. -
Geef het automatische indeling, verticaal, op het tabblad Grafisch. Zonder die indeling komt elke kopie van de rij boven op de eerste te liggen.
-
Teken één frame binnen
Rows, over de volle breedte, 64 hoog. Noem hetCase rowen stel Dit object is → Een lijstitem in. -
Zoek, met
Case rowgeselecteerd, Herhalen voor elk op het tabblad Scherm en typ:state.cases -
Teken binnen de rij een tekstobject voor de samenvatting. Selecteer het en stel bij Koppelingen Tekst in:
row.par_name
row is het record van deze kopie. Sla op en druk op Voorbeeld: één rij per case, uit de tabel,
zonder dat er iets met de hand is herhaald.
┌─ Rows ────────────────────────────────────────────────┐
│ ☐ 1 Sample case 1 Normal │ ← Case row, drawn once
│ ☐ 2 Sample case 2 High Urgent │
│ ☐ 3 Sample case 3 Low │
│ … │
└───────────────────────────────────────────────────────┘
7. Het vinkje, en één idee van wat er gekozen is
Teken aan het begin van de rij een klein frame, 20 × 20, en stel Besturingselement → Selectievakje in. Dan:
-
Koppelingen → Waarde:
state.picked.includes(row.id) -
Handlers → Gewijzigd:
var ids = "";var count = 0;var firstId = "";var i = 0;for (i = 0; i < state.cases.length(); i = i + 1) {var id = state.cases[i].id;var on = state.picked.includes(id);if (id == row.id) { on = !on; }if (on) {ids = ids + id + "|";count = count + 1;if (count == 1) { firstId = id; }}}setState("picked", ids);setState("pickedCount", count);setState("pickedId", firstId);
De waarde is een vergelijking, geen veld. Elk vinkje leest dezelfde ene lijst opnieuw, dus de lijst leegmaken haalt alle vinkjes tegelijk weg — iets wat een vlag per rij nooit zou kunnen, want niets zou weten waar de andere rijen waren.
8. Een werkbalk die wakker wordt
Teken een balk boven de lijst — een frame, over de volle breedte, 56 hoog, Dit object is → Een gebied — en zet er drie knoppen in. Voor elke knop: teken een frame, stel Besturingselement → Knop in, en teken er een tekstobject in voor het opschrift.
Zet op elke knop een glyph. Selecteer de knop en gebruik dan de pictogramkiezer op het tabblad
Grafisch — Plus, Pencil, Trash2. Wat erin komt, is een gewoon getekend pad binnen de knop,
en de uiteindelijke knop legt de glyph, de tussenruimte en het opschrift zelf uit.
| Knop | Koppelingen → Ingeschakeld | Handlers → Geklikt |
|---|---|---|
| Nieuw | (leeg laten) | setState("form", { par_name: "", par_urgent: false, par_notes: "" });openEndDrawer("New case"); |
| Bewerken | state.pickedCount == 1 | openStartDrawer("Edit case"); |
| Verwijderen | state.pickedCount > 0 | showModal("Confirm delete"); |
Koppel Ingeschakeld in plaats van code te schrijven om dingen grijs te maken. Een koppeling wordt opnieuw geëvalueerd elke keer dat de toestand verandert, dus de balk wordt vanzelf wakker en weer stil.
Geef de tekst en rand van de knop Verwijderen de token color.error, niet een rood dat je zelf
hebt gekozen. Een token volgt de kleurmodus van de lezer; een hexwaarde klopt voor wie hem tekende en
is fout voor iedereen met een ander thema.
9. Het zijpaneel, en wat een overlaykaart is
Een overlay is geen scherm. Het is een component die je als kaart aanvinkt, en een handler bepaalt waar hij opent.
- Teken het formulier ergens buiten het scherm — een frame van ongeveer 420 breed, met een titel, de velden en twee knoppen. Het maakt niet uit waar op het canvas het staat; een kaart wordt op de oorsprong gecompileerd, en alleen de breedte blijft behouden.
- Selecteer het frame, druk op Component maken en noem het
New case. - Vink in het paneel van de component Een overlaykaart aan.
┌─ Inspector · Component ───────────┐
│ ☑ An overlay card │
│ Show it with showModal("New │
│ case") … Its drawn width is │
│ the panel's width. │
│ │
│ ☐ It has to be answered │
│ Clicking outside, Escape and │
│ the close button all stop │
│ working while it is open. │
└───────────────────────────────────┘
Teken daarbinnen voor elk veld een frame, stel Besturingselement → Tekstvak in (of Tekstgebied
voor de notities), en koppel Waarde aan het toestandspad dat het bewerkt — state.form.par_name,
state.form.par_notes. Een besturingselement dat aan een gewoon toestandspad is gekoppeld, schrijft
daarnaar terug, dus er valt verder niets aan te sluiten.
Dan Geklikt van de knop Opslaan:
if (getState("form").par_name == "") {
notifyError("A case needs a summary.");
} else {
createRecord("par_case", {
par_name: getState("form").par_name,
par_urgent: getState("form").par_urgent,
par_notes: getState("form").par_notes
});
notify("Case created.");
closeEndDrawer();
setState("page", 0);
loadRecords("par_case", "cases", { top: getState("pageSize"), skip: 0,
orderBy: "par_name", countInto: "caseTotal" });
}
Elke schrijfactie eindigt met het opnieuw lezen van de lijst. Niets wordt vanzelf vernieuwd — het scherm toont wat de toestand bevat, en de toestand bevat wat de laatste query erin heeft gezet.
Een vraag die moet worden beantwoord
Teken de bevestiging voor verwijderen op dezelfde manier, vink Een overlaykaart aan, en vink dan
ook Moet worden beantwoord aan. Ernaast klikken, op Escape drukken en de sluitknop werken dan
allemaal niet meer — teken dus je eigen uitweg: een knop Annuleren die closeModal() uitvoert.
Laat dat tweede vinkje overal anders uit. Een overlay die iemand per ongeluk opende, moet sluiten zoals hij opende; de enige die mag weigeren, is die waarbij ernaast klikken geen antwoord is.
10. Opslaan, en hem echt openen
Druk op Opslaan. Bovenaan verschijnen waarschuwingen als de compiler iets heeft opgemerkt — een knop zonder woorden, een schermframe dat ontbreekt, een koppeling die geen zuivere expressie is. Ze noemen het object, dus je lost ze op door het te selecteren.
Verlaat dan de designer en open de app vanuit de applijst van de shell. Voorbeeld is niet hetzelfde: dat toont dezelfde schermen zonder omgeving erachter, dus wat het je niet kan laten zien, zijn je gegevens. Al het andere — de zijpanelen, de selectievakjes, de werkbalk — gedraagt zich precies hetzelfde.
Wat je daarna kunt doen
- Een tweede scherm. Teken een ander frame naast het eerste, markeer het als Het scherm en geef
het een naam. Zodra er twee zijn, verschijnt er een kiezer Opent op in de titelbalk.
navigate("Board")wisselt ertussen. - Tabbladen. Een tabbladstrook is geen besturingselement: het is één toestandsveld en een paneel per
waarde. Teken de panelen in dezelfde rechthoek, koppel Getoond van elk paneel aan
state.tab == "notes", en geef de tabbladknoppen een koppeling Geselecteerd op dezelfde vergelijking. Gebruik Alleen hierop tekenen op de rij van een laag om een paneel onder de andere te bereiken. - Besturingselementen die je niet zelf hebt getekend. Het voorbeeld
studio-component-librarybevat een vervolgkeuzelijst, een zoekveld en een schakelaar. Vink Publiceert componenten aan op die app en ze verschijnen in je palet — zie Een component op meerdere schermen delen. - Een dashboard. Hetzelfde canvas tekent er een: zie Een dashboard op een scherm.
Problemen oplossen
| Wat je ziet | Wat het is |
|---|---|
| Only booleans are allowed in this expression | Een optioneel veld dat als voorwaarde wordt gelezen. Omhul het: (state.x.par_urgent ?? false). |
| De koppeling van een rij leest als leeg | row bestaat alleen binnen het element met Herhalen voor elk en de onderliggende elementen ervan. |
| Rijen stapelen zich op elkaar | Het vak rond de herhaalde rij heeft automatische indeling nodig. |
| Tekst loopt onder uit een vak | Zet hem in een frame dat is ingesteld op Ernaartoe scrollen → Omlaag, en veranker de woorden aan de bovenkant. |
| Een vervolgkeuzelijst sluit het zijpaneel van waaruit hij werd geopend | Opgelost in 0.31. Werk bij, of verplaats de kiezer uit het zijpaneel. |
| Opslaan is uitgeschakeld | Er is niets veranderd sinds de laatste keer opslaan. |