🗃️ Citat‑viser

Trin for trin: Brug Data-fanen i App Lab til at gemme, læse og opdatere citater.
Vi bygger videre på det, du kan fra Valutakonverter (ID’er, events, getText/setText).

Kort mål

Opgave 1 – Lav datatabel i App Lab

  1. Åbn dit projekt i App Lab → fanen Data.
  2. Lav en ny tabel Quotes med disse kolonner (id findes altid automatisk – du skal ikke oprette en kolonne der hedder id):
    • text (Text) – selve citatet
    • author (Text)
    • topic (Text)
    • likes (Number) – startværdi 0
    • shownCount (Number) – startværdi 0
    • favorite (Boolean) – startværdi false
  3. (Valgfrit) Indsæt et lille startdatasæt manuelt eller via CSV (se eksempel længere nede).

Opgave 2 – Design UI (IDs)

I Design opret disse elementer. Brug gerne samme ID’er (så passer koden nedenfor direkte):

(Udvid senere) Søg/filtrér:

(Udvid senere) Tilføj citat:

Opgave 3 – Læs data og vis tilfældigt citat

Tilføj denne kode i Kode (Vis tekst). Den henter alle citater, blander dem og viser ét ad gangen uden gentagelser i en runde.

// ===== Citat-Viewer – grundkode =====
var allQuotes = [];
var pool = [];         // indekser for resterende citater i denne runde (blandet)
var current = null;    // den record der vises nu

function loadQuotes(callback) {
  readRecords("Quotes", {}, function(records) {
    allQuotes = records;
    shufflePool();
    if (callback) callback();
  });
}

function shufflePool() {
  pool = [];
  for (var i = 0; i < allQuotes.length; i++) { pool.push(i); }
  // Fisher–Yates-blanding
  for (var j = pool.length - 1; j > 0; j--) {
    var k = randomNumber(0, j);
    var tmp = pool[j];
    pool[j] = pool[k];
    pool[k] = tmp;
  }
}

function showRandom() {
  if (allQuotes.length === 0) {
    setText("lblQuote", "(Ingen data i tabellen endnu)");
    setText("lblAuthor", "");
    return;
  }
  if (pool.length === 0) shufflePool();
  var idx = pool.pop();
  current = allQuotes[idx]; // gem hele recorden (inkl. id)

  // Opdater UI
  setText("lblQuote", current.text || "(mangler tekst)");
  setText("lblAuthor", current.author ? "— " + current.author : "");
  setText("lblLikes", "Likes: " + (current.likes || 0));
  setProperty("btnFav", "text", current.favorite ? "❤️ Favorit" : "🤍 Favorit");

  // Tæl visning og gem tilbage (UPDATE)
  current.shownCount = (current.shownCount || 0) + 1;
  updateRecord("Quotes", current, function(updated) {
    // Opdater også i allQuotes-listen, så data passer lokalt
    allQuotes[idx] = current;
  });
}

// Event: Nyt citat
onEvent("btnNew", "click", function() {
  showRandom();
});

// Indlæs data ved app-start
loadQuotes(function(){
  showRandom();
});
Hvorfor gemmer vi hele recorden i current?

Fordi updateRecord() bruger objektet inkl. det automatisk tildelte id. Vi ændrer feltet (fx shownCount) på current og sender hele recorden ind igen.

Opgave 4 – Like ⭐ og Favorit ❤️ (UPDATE)

Tilføj disse events. De opdaterer både UI og databasen.

// Like-knap: +1 like
onEvent("btnLike", "click", function(){
  if (!current) return;
  current.likes = (current.likes || 0) + 1;
  setText("lblLikes", "Likes: " + current.likes);
  updateRecord("Quotes", current, function(updated){ /* valgfrit: feedback */ });
});

// Favorit-knap: toggle true/false
onEvent("btnFav", "click", function(){
  if (!current) return;
  current.favorite = !current.favorite;
  setProperty("btnFav", "text", current.favorite ? "❤️ Favorit" : "🤍 Favorit");
  updateRecord("Quotes", current, function(updated){ /* valgfrit */ });
});
Tip

Vi arbejder hele tiden på current (det viste citat) – så undgår vi at glemme id i updateRecord().

Opgave 5 – Søg/filtrér på forfatter

En enkel model er at læse alle records én gang og så filtrere i JavaScript (case‑insensitive). Tilføj event til btnSearch:

onEvent("btnSearch", "click", function(){
  var q = (getText("inputSearch") || "").toLowerCase();
  var candidates = allQuotes.filter(function(r){
    return (r.author || "").toLowerCase().indexOf(q) !== -1;
  });
  if (candidates.length === 0) {
    setText("lblQuote", "Ingen citater matcher din søgning.");
    setText("lblAuthor", "");
    return;
  }
  current = candidates[randomNumber(0, candidates.length - 1)];
  setText("lblQuote", current.text);
  setText("lblAuthor", current.author ? "— " + current.author : "");
  setText("lblLikes", "Likes: " + (current.likes || 0));
  setProperty("btnFav", "text", current.favorite ? "❤️ Favorit" : "🤍 Favorit");
});
Note

Vil du kun hente præcise matches fra databasen, kan du bruge readRecords("Quotes", {author: "Præcis Navn"}, callback). Men “indeholder‑søgning” er lettest at forklare ved at filtrere i JS.

Opgave 6 – Tilføj nyt citat (CREATE)

  1. Lav felter/knap: inputNewText, inputNewAuthor, inputNewTopic, btnAdd, samt statuslabel lblMsg.
  2. Tilføj koden her:
onEvent("btnAdd", "click", function(){
  var t = (getText("inputNewText") || "").trim();
  var a = (getText("inputNewAuthor") || "").trim();
  var top = (getText("inputNewTopic") || "").trim();
  if (t === "" || a === "") {
    setText("lblMsg", "Skriv både tekst og forfatter.");
    return;
  }
  var newRec = { text: t, author: a, topic: top, likes: 0, shownCount: 0, favorite: false };
  createRecord("Quotes", newRec, function(created){
    if (created) {
      setText("lblMsg", "Tilføjet ✔");
      // læg i lokalt array, så det kan vises med det samme
      allQuotes.push(created);
      setText("inputNewText", "");
      setText("inputNewAuthor", "");
      setText("inputNewTopic", "");
    } else {
      setText("lblMsg", "Kunne ikke tilføje. Tjek net/forbindelse.");
    }
  });
});
Tip

createRecord() returnerer normalt den oprettede record inkl. det automatisk tildelte id. Vi skubber den ind i allQuotes så UI kan bruge den med det samme.

(Valgfri) Opgave 7 – Redigér og slet (UPDATE/DELETE)

Vis evt. et lille “Admin”-område med to knapper. Husk at bruge current (som har id):

// Redigér: skift fx topic på det viste citat
function setTopic(newTopic){
  if (!current) return;
  current.topic = newTopic;
  updateRecord("Quotes", current, function(updated){ /* feedback */ });
}

// Slet det viste citat
function deleteCurrent(){
  if (!current) return;
  deleteRecord("Quotes", current, function(success){
    if (success) {
      // Fjern fra lokalt array og vis et nyt
      allQuotes = allQuotes.filter(function(r){ return r.id !== current.id; });
      setText("lblMsg", "Slettet ✔");
      current = null;
      showRandom();
    }
  });
}

Eksempel på CSV til hurtig import

Kopier til en .csv-fil og importér i tabellen Quotes (lad systemet selv lave id). Bemærk: sand/falsk skrives som true/false.

text,author,topic,likes,shownCount,favorite
"Stay hungry, stay foolish.",Steve Jobs,inspiration,0,0,false
"Simplicity is the ultimate sophistication.",Leonardo da Vinci,design,0,0,false
"Creativity is intelligence having fun.",Albert Einstein,creativity,0,0,false
"If you can dream it, you can do it.",Walt Disney,motivation,0,0,false
"The only way to learn a new programming language is by writing programs in it.",Dennis Ritchie,coding,0,0,false
"Programs must be written for people to read, and only incidentally for machines to execute.",Harold Abelson,coding,0,0,false
"First, solve the problem. Then, write the code.",John Johnson,coding,0,0,false
"Make it work, make it right, make it fast.",Kent Beck,coding,0,0,false

Fejlforebyggelse & gode vaner

Refleksion

↩ Til forsiden