INF.04 styczeń 2025 — klasa z tablicą i galeria zdjęć w Bootstrapie
Rozbiór arkusza INF.04 ze stycznia 2025: konsolowa klasa operacji na tablicy, front-end galerii z filtrowaniem po kategoriach w React lub Angularze oraz dokumentacja. Plan na 180 minut i gotowy kod.
Oryginalny arkusz CKE (PDF)
Pobierz plik źródłowy i rozwiąż go samodzielnie przed sprawdzeniem rozwiązań.
Dwie aplikacje, cztery rezultaty, trzy godziny
Styczeń 2025 to klasyczny układ INF.04: prosta klasa operująca na tablicy w języku obiektowym plus front-end galerii zdjęć z filtrowaniem po kategoriach. Trudność nie leży w algorytmach — one są na poziomie pierwszej klasy technikum. Leży w tym, że w 180 minut trzeba postawić dwa niezależne projekty, uruchomić oba, udokumentować oba i jeszcze spakować wszystko według bardzo szczegółowej instrukcji o folderach.
Każdy oceniany rezultat ma tu własną sekcję. Zaczyna się od karty „Czego wymaga arkusz" — ta jest widoczna od razu. Rozwiązanie z pełnym kodem jest schowane pod przyciskiem. Odsłaniaj je dopiero wtedy, gdy sam spróbujesz napisać kod: na egzaminie nie ma czego kopiować, wszystko musi wyjść z głowy.
📋 Spis treści
1
Metryka arkusza i klucz oceniania
- Kwalifikacja: INF.04 — projektowanie, programowanie i testowanie aplikacji
- Sesja: styczeń 2025, zadanie 01, wersja arkusza SG
- Czas: 180 minut, plus 10 minut na zapoznanie się z zadaniem (niewliczane)
- Próg zdania: 75% punktów
- Temat: operacje na tablicy w aplikacji konsolowej oraz kategoryzacja zdjęć w galerii front-end
- Konto na stanowisku: Egzamin, bez hasła
- Materiały: archiwum
pliki3.zipna pulpicie, hasłoK@tegorie)
Arkusz wymienia cztery rezultaty. To dosłownie lista, po której idzie egzaminator:
- implementacja, kompilacja, uruchomienie programu
- aplikacja konsolowa
- aplikacja web
- dokumentacja aplikacji
Zwróć uwagę: pierwszy rezultat jest osobny od dwóch kolejnych. Aplikacja, która się nie kompiluje, przegrywa dwa razy — raz w rezultacie 1, drugi raz w rezultacie za samą aplikację.
Folder nazwany numerem zdającego, a w nim trzy podfoldery o narzuconych nazwach:
PESEL/
├── konsolowa/
│ ├── konsola.zip archiwum CALEGO folderu projektu
│ ├── Program.cs (albo main.py / Main.java / main.cpp)
│ └── konsola.exe plik wykonywalny, jesli istnieje
├── webowa/
│ ├── web.zip archiwum CALEGO folderu projektu
│ ├── App.jsx komponent (albo app.component.ts + .html)
│ └── App.css i kazdy inny modyfikowany plik zrodlowy
└── dokumentacja/
├── konsola1.png zrzuty aplikacji konsolowej
├── konsola2.png
├── web1.png zrzuty aplikacji webowej
├── web2.png
├── web3.png
└── egzamin.odt (albo .docx) opis narzedziArkusz mówi wprost: po spakowaniu w podfolderze zostawiasz
tylko archiwum, modyfikowane pliki źródłowe i plik wykonywalny.
Nie zostawiaj tam folderu node_modules
ani bin/obj —
zajmą całą płytę i nagranie się nie skończy przed dzwonkiem.
Czego tu nie ma, a bywa w INF.04: w tej sesji nie ma bazy danych, nie ma back-endu, nie ma testów jednostkowych. Aplikacja webowa jest jawnie opisana jako typu front-end, cały stan siedzi w tablicy w pamięci komponentu. Jeśli zaczniesz kombinować z API albo z zapisem do pliku, tracisz czas na coś, czego nikt nie sprawdza.
2
Plan czasowy na 180 minut
Cztery rezultaty ważą podobnie, więc porzucona dokumentacja kosztuje mniej więcej tyle, co porzucona aplikacja webowa — a robi się ją dwadzieścia razy szybciej. To jest główny wniosek z tego arkusza: najtańsze punkty leżą w dokumentacji i w porządku w folderach, a większość zdających zostawia je na ostatnie pięć minut i nie zdąża.
| Etap | Czas | Rezultat | Uwagi |
|---|---|---|---|
| Foldery, rozpakowanie pliki3.zip, oba projekty założone | 10 min | 1 | projekt webowy zakładaj od razu — instalacja pakietów mieli w tle |
| Klasa tablicy: pola, konstruktor, cztery metody | 45 min | 2 | najprostsza część arkusza, nie przeciągaj jej |
| Uruchomienie konsoli i zrzuty konsola1, konsola2 | 10 min | 1 + 4 | zrzut rób od razu, nie na końcu |
| Web: Bootstrap, assets, tablica z dane.txt | 20 min | 3 | import CSS Bootstrapa sprawdź od razu w przeglądarce |
| Komponent: switche, pętla po zdjęciach, przycisk Pobierz | 45 min | 3 | najpierw wyświetl wszystko, dopiero potem filtruj |
| Uruchomienie web i zrzuty web1, web2, web3 | 15 min | 1 + 4 | stan początkowy, odznaczony switch, wzrost pobrań |
| Nagłówek metody w komentarzu + plik egzamin | 15 min | 4 | najtańsze punkty w całym arkuszu |
| Archiwa zip, czyszczenie folderów, nagranie płyty | 20 min | 1 | rezerwa, nigdy jej nie tnij |
Kolejność, która się broni: konsola najpierw. Jest krótsza, nie zależy od instalacji pakietów i daje ci materiał do nagłówka dokumentacji, który i tak musisz napisać. Gdyby zabrakło czasu, lepiej mieć skończoną konsolę i połowę weba niż odwrotnie — połowa weba, która się nie uruchamia, przegrywa również rezultat 1.
Zasada dwudziestej minuty: jeśli po dwudziestu minutach walki projekt webowy dalej nie startuje w przeglądarce, przestań go naprawiać i zrób zrzut ekranu ze spisem błędów i otwartym środowiskiem. Arkusz wprost przewiduje ten scenariusz w części III — taki zrzut jest punktowany, a puste miejsce nie.
3
Rezultat 1: implementacja, kompilacja, uruchomienie
- Folder nazwany numerem zdającego z podfolderami
konsolowa,webowa,dokumentacja. - Oba projekty mają się kompilować i uruchamiać. To osobny rezultat, niezależny od tego, czy aplikacja robi wszystko, czego wymaga jej opis.
- Pełny kod każdej aplikacji spakowany do archiwum:
konsola.zipiweb.zip. - Po spakowaniu w podfolderze zostają wyłącznie: archiwum, modyfikowane pliki źródłowe i — jeśli istnieje — plik wykonywalny.
- Archiwum
pliki3.zipz pulpitu rozpakowane hasłemK@tegorie), zdjęcia przeniesione do folderuassets. - Na koniec: płyta nagrana i opisana numerem zdającego.
Zrób to jako pierwszą rzecz po przeczytaniu arkusza — zanim napiszesz choć jedną linijkę kodu. Potem porównaj kolejność z procedurą poniżej.
Pokaż rozwiązaniePierwsze 10 minut i ostatnie 20 minut egzaminu
# 1. Struktura folderow (PESEL = Twoj numer zdajacego)
mkdir PESEL
mkdir PESEL/konsolowa PESEL/webowa PESEL/dokumentacja
# 2. Rozpakuj materialy z pulpitu, haslo: K@tegorie)
# W srodku sa zdjecia oraz plik dane.txt
# 3. Projekt konsolowy - wybierz JEDEN wariant
dotnet new console -o PESEL/konsolowa/konsola # C#
# albo po prostu plik main.py # Python
# albo projekt w NetBeans / IntelliJ # Java
# albo projekt w Code::Blocks / Visual Studio # C++
# 4. Projekt webowy - wybierz JEDEN wariant
npx create-react-app egzamin # React.js
# albo
ng new egzamin --standalone --style=css # Angular
# 5. Bootstrap (jest juz na stanowisku egzaminacyjnym)
npm install bootstrap
# 6. Zdjecia z pliki3.zip do folderu assets
# React: egzamin/public/assets/ albo egzamin/src/assets/
# Angular: egzamin/src/assets/ albo egzamin/public/assets/# Spakuj CALY folder projektu, nie same pliki zrodlowe
# konsolowa -> konsola.zip
# webowa -> web.zip
#
# Przed spakowaniem weba usun node_modules:
# to kilkaset megabajtow i kilkadziesiat tysiecy plikow,
# pakowanie tego trwa dluzej niz zostalo Ci czasu.
#
# Po spakowaniu w podfolderze zostawiasz:
# konsolowa/ konsola.zip + Program.cs + konsola.exe
# webowa/ web.zip + App.jsx + App.css
#
# Ostatni krok: nagraj plyte, sprawdz nagranie,
# opisz plyte numerem zdajacego, zostaw w pudelku z arkuszem.- Projekt webowy zakładasz na początku, bo instalacja pakietów potrafi zająć kilka minut. W tym czasie piszesz klasę konsolową — procesor i tak stoi bezczynnie.
- Nazwy podfolderów są narzucone przez arkusz: konsolowa, webowa, dokumentacja. Nie „konsola", nie „web", nie „docs".
- Nazwy archiwów też: konsola.zip i web.zip. Zauważ, że są niesymetryczne względem nazw folderów — to celowa pułapka na automatyczne uzupełnianie.
- Plik wykonywalny dołączasz „jeżeli istnieje". W Pythonie nie istnieje i nikt go nie oczekuje. W C# to plik z folderu
bin/Debug.
Gdzie się traci punkty:
spakowanie samego pliku źródłowego zamiast całego folderu projektu.
Arkusz pisze „pełny kod (cały folder projektu)" — plik
konsola.zip
zawierający jeden plik .cs nie spełnia tego warunku. Drugi klasyk to
nagranie płyty bez sprawdzenia: napęd raportuje sukces, a katalog jest
pusty. Otwórz płytę po nagraniu i przejrzyj wszystkie trzy podfoldery.
4
Rezultat 2: aplikacja konsolowa
- Język: obiektowy, zgodny z zainstalowanym na stanowisku — C++, C#, Java albo Python. Tablica i operacje na niej mają być zamknięte w klasie.
- Pola klasy: tablica liczb całkowitych (tradycyjna tablica, w Pythonie lista) oraz liczba elementów jako liczba całkowita. Oba pola dostępne tylko w tej klasie i niedostępne dla klas potomnych.
- Konstruktor: przyjmuje rozmiar tablicy, ustawia pole liczby elementów na wartość argumentu i wypełnia tablicę pseudolosowymi liczbami całkowitymi z zakresu od 1 do 1000.
- Metoda 1: wyświetla wszystkie elementy w postaci „<index>: <wartość>", nic nie zwraca.
- Metoda 2: wyszukuje pierwsze wystąpienie wartości podanej jako argument, zwraca indeks lub -1, gdy nie znaleziono.
- Metoda 3: wyświetla wszystkie wartości nieparzyste i zwraca ich liczbę.
- Metoda 4: liczy średnią arytmetyczną wartości i ją zwraca.
- Wszystkie metody dostępne poza klasą.
- Program główny: tworzy obiekt z rozmiarem tablicy większym od 20 i sprawdza działanie wszystkich metod. Przy wyszukiwaniu: gdy wartość znaleziono, wyświetla komunikat z indeksem; gdy nie — nie wyświetla nic.
- Znaczące nazewnictwo (angielskie lub polskie), czytelne formatowanie, zrozumiała komunikacja z użytkownikiem.
To zadanie na 45 minut i na papierze wygląda banalnie — napisz je sam, zanim odsłonisz kod. Trzy rzeczy są tu punktowane osobno i łatwo je przeoczyć: modyfikator dostępu pól, zakres losowania i milczenie przy nieznalezionej wartości.
Pokaż rozwiązanieKompletny program w C# — klasa Tablica i Main
Cały plik Program.cs.
Nagłówek dokumentacyjny nad metodą to jednocześnie realizacja części III arkusza.
using System;
namespace AplikacjaKonsolowa
{
class Tablica
{
// Oba pola prywatne: dostepne tylko w tej klasie
// i NIEdostepne dla klas potomnych
private int[] tablica;
private int liczbaElementow;
public Tablica(int rozmiar)
{
liczbaElementow = rozmiar;
tablica = new int[rozmiar];
Random generator = new Random();
for (int i = 0; i < liczbaElementow; i++)
{
// Next(1, 1001) losuje od 1 do 1000 wlacznie
tablica[i] = generator.Next(1, 1001);
}
}
public void Wyswietl()
{
for (int i = 0; i < liczbaElementow; i++)
{
Console.WriteLine(i + ": " + tablica[i]);
}
}
/************************************************
nazwa metody: Szukaj
opis metody: wyszukuje pierwsze wystapienie
podanej wartosci w tablicy
parametry: szukanaWartosc - liczba calkowita,
ktorej szukamy w tablicy
zwracany typ i opis: int - indeks pierwszego wystapienia
szukanej wartosci lub -1, gdy
wartosci nie ma w tablicy
autor: PESEL
************************************************/
public int Szukaj(int szukanaWartosc)
{
for (int i = 0; i < liczbaElementow; i++)
{
if (tablica[i] == szukanaWartosc)
{
return i;
}
}
return -1;
}
public int WyswietlNieparzyste()
{
int licznik = 0;
for (int i = 0; i < liczbaElementow; i++)
{
if (tablica[i] % 2 != 0)
{
Console.WriteLine(tablica[i]);
licznik++;
}
}
return licznik;
}
public double Srednia()
{
double suma = 0;
for (int i = 0; i < liczbaElementow; i++)
{
suma += tablica[i];
}
if (liczbaElementow == 0)
{
return 0;
}
return suma / liczbaElementow;
}
}
class Program
{
static void Main(string[] args)
{
Tablica tab = new Tablica(25);
Console.WriteLine("Elementy tablicy:");
tab.Wyswietl();
Console.WriteLine();
Console.Write("Podaj szukana wartosc: ");
int szukana = int.Parse(Console.ReadLine());
int indeks = tab.Szukaj(szukana);
if (indeks != -1)
{
Console.WriteLine("Znaleziono pod indeksem: " + indeks);
}
// gdy indeks == -1 program celowo NIC nie wyswietla
Console.WriteLine();
Console.WriteLine("Wartosci nieparzyste:");
int ileNieparzystych = tab.WyswietlNieparzyste();
Console.WriteLine("Liczba wartosci nieparzystych: " + ileNieparzystych);
Console.WriteLine();
Console.WriteLine("Srednia arytmetyczna: " + tab.Srednia());
Console.ReadKey();
}
}
}| Zapis w arkuszu | Co to znaczy w kodzie |
|---|---|
| „dostępne tylko w tej klasie oraz niedostępne dla klas potomnych" | private — protected jest widoczne w klasach potomnych, więc jest błędem |
| „pseudolosowymi z zakresu od 1 do 1000" | Next(1, 1001) — górna granica w C# jest wyłączona |
| „w postaci <index>: <wartość>" | dwukropek i spacja między indeksem a wartością, dokładnie jak na obrazie 1 |
| „zwraca indeks lub liczbę -1" | metoda zwraca int, nie bool i nie wypisuje niczego sama |
| „w przeciwnym wypadku nic nie jest wyświetlane" | if bez gałęzi else w programie głównym |
| „wyświetlająca nieparzyste i zwracająca ich liczbę" | jedna metoda robi obie rzeczy: WriteLine w pętli plus return licznik |
| „rozmiar tablicy większy od 20" | dowolna liczba od 21 w górę — w przykładzie 25 |
Gdzie się traci punkty:
wszystkie operacje muszą być ograniczone polem liczby elementów,
a nie długością tablicy. W tym zadaniu obie wartości są równe, ale arkusz
pisze wprost: „wszystkie operacje są ograniczone wartością tego pola".
Pętla po tablica.Length
jest formalnie niezgodna z opisem. Drugi klasyk: metoda średniej zwracająca
int —
dzielenie całkowite ucina część ułamkową i średnia z 25 losowych liczb
wychodzi „równa".
Pokaż rozwiązanieTen sam program w Pythonie — z prawdziwą prywatnością pól
Plik main.py.
Arkusz dopuszcza w Pythonie listę zamiast tablicy — to jedyne odstępstwo,
jakie daje.
import random
class Tablica:
def __init__(self, rozmiar):
# Podwojny podkreslnik = name mangling.
# Pole jest niedostepne poza klasa i w klasach potomnych.
self.__liczba_elementow = rozmiar
self.__tablica = []
for i in range(rozmiar):
self.__tablica.append(random.randint(1, 1000))
def wyswietl(self):
for i in range(self.__liczba_elementow):
print(str(i) + ": " + str(self.__tablica[i]))
# ************************************************
# nazwa metody: szukaj
# opis metody: wyszukuje pierwsze wystapienie
# podanej wartosci w tablicy
# parametry: szukana_wartosc - liczba calkowita,
# ktorej szukamy w tablicy
# zwracany typ i opis: int - indeks pierwszego wystapienia
# lub -1, gdy wartosci nie ma
# autor: PESEL
# ************************************************
def szukaj(self, szukana_wartosc):
for i in range(self.__liczba_elementow):
if self.__tablica[i] == szukana_wartosc:
return i
return -1
def wyswietl_nieparzyste(self):
licznik = 0
for i in range(self.__liczba_elementow):
if self.__tablica[i] % 2 != 0:
print(self.__tablica[i])
licznik += 1
return licznik
def srednia(self):
if self.__liczba_elementow == 0:
return 0
suma = 0
for i in range(self.__liczba_elementow):
suma += self.__tablica[i]
return suma / self.__liczba_elementow
def main():
tab = Tablica(25)
print("Elementy tablicy:")
tab.wyswietl()
print()
szukana = int(input("Podaj szukana wartosc: "))
indeks = tab.szukaj(szukana)
if indeks != -1:
print("Znaleziono pod indeksem: " + str(indeks))
# gdy indeks == -1 program celowo nic nie wyswietla
print()
print("Wartosci nieparzyste:")
ile = tab.wyswietl_nieparzyste()
print("Liczba wartosci nieparzystych: " + str(ile))
print()
print("Srednia arytmetyczna: " + str(tab.srednia()))
main()- W Pythonie
random.randint(1, 1000)ma obie granice włączone — inaczej niżNextw C#. To najczęstszy błąd przy przepisywaniu rozwiązania między językami. - Jeden podkreślnik (
_pole) to tylko konwencja, dziedziczy się normalnie. Arkusz żąda niedostępności w klasach potomnych, więc idziesz w dwa podkreślniki. - W Javie i C++ odpowiednikiem jest
private. W C++ pamiętaj o zwolnieniu pamięci, jeśli alokujesz tablicę przeznew. - Średnia w Pythonie 3 wychodzi zmiennoprzecinkowa automatycznie — operator
/nigdy nie ucina.
Uwaga o czytelności: arkusz
punktuje osobno „znaczące nazewnictwo" i „czyste formatowanie kodu".
Nazwy typu a,
x1,
fun2
kosztują punkt, mimo że program działa idealnie. Wyjątek dopuszczony
wprost przez arkusz: klasa i pole tablicowe mogą się nazywać ogólnie —
tab,
tablica,
table.
5
Rezultat 3: aplikacja WEB z Bootstrapem
- Aplikacja front-end w Angularze albo w React.js, złożona z jednego komponentu.
- Dane: tablica obiektów przepisana z pliku
dane.txt. Każdy obiekt ma polaid,alt,filename,category(1 kwiaty, 2 zwierzęta, 3 samochody),downloads. - Zdjęcia z archiwum trafiają do folderu
assets. - Nagłówek pierwszego stopnia: „Kategorie zdjęć".
- Trzy pola switch (checkbox), domyślnie włączone, etykiety: Kwiaty, Zwierzęta, Samochody.
- Bloki zdjęć wyświetlane warunkowo, jeden obok drugiego. Każdy zawiera zdjęcie, nagłówek czwartego stopnia z liczbą pobrań i przycisk „Pobierz".
- Styl zdjęć: marginesy zewnętrzne 5 px, zaokrąglone rogi.
- Kliknięcie „Pobierz" zwiększa liczbę pobrań o jeden — wartość zapisuje się w tablicy obiektów i natychmiast widać zmianę na ekranie.
- Do wyświetlenia bloków użyte pętle i warunki; aplikacja ma działać poprawnie także dla innej liczby zdjęć.
- Przyciski i switche stylowane zgodnie z tabelą 1 arkusza (elementy Bootstrapa).
Zbuduj to sam — najpierw wyświetl wszystkie zdjęcia bez filtrowania, potem dołóż switche. Rozwiązanie poniżej jest w dwóch wariantach.
Ostatnia strona arkusza to ściąga — CKE podaje w niej gotowe fragmenty Bootstrapa. Nie musisz ich pamiętać, musisz umieć je znaleźć i wkleić. To jest część zadania, nie dodatek:
| Element | Co podaje arkusz | Gdzie tego użyjesz |
|---|---|---|
| Podłączenie w Angularze | @import w styles.css | plik src/styles.css, raz na projekt |
| Podłączenie w React.js | import bootstrap.css | na górze pliku komponentu albo w index.js |
| Switch | form-check form-switch, form-check-input, form-check-label | trzy pola przełączające kategorie |
| Switche w jednym wierszu | form-check-inline | układ poziomy jak na obrazie 2 |
| Przyciski | btn + btn-primary, btn-success, btn-danger, ... | przycisk „Pobierz" w każdym bloku zdjęcia |
Uwaga na klasę etykiety: arkusz
pokazuje switch, w którym id
inputu i for
etykiety są tą samą wartością. Jeśli je rozjedziesz, klikanie
w napis „Kwiaty" przestaje działać, a egzaminator sprawdza działanie
kliknięciem w etykietę, nie w kwadracik.
Pokaż rozwiązanieWariant React.js — cały komponent App.jsx
Plik src/App.jsx.
Tablicę zdjecia
przepisujesz jeden do jednego z dane.txt —
poniżej przykładowe wartości.
import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.css';
// Trzy kategorie zgodnie z opisem pola category w dane.txt
const kategorie = [
{ numer: 1, etykieta: 'Kwiaty' },
{ numer: 2, etykieta: 'Zwierzeta' },
{ numer: 3, etykieta: 'Samochody' }
];
function App() {
// Tablica obiektow przepisana z pliku dane.txt
const [zdjecia, setZdjecia] = useState([
{ id: 1, alt: 'roza', filename: 'kwiat1.jpg', category: 1, downloads: 12 },
{ id: 2, alt: 'tulipan', filename: 'kwiat2.jpg', category: 1, downloads: 5 },
{ id: 3, alt: 'kot', filename: 'zwierze1.jpg', category: 2, downloads: 8 },
{ id: 4, alt: 'pies', filename: 'zwierze2.jpg', category: 2, downloads: 21 },
{ id: 5, alt: 'sedan', filename: 'auto1.jpg', category: 3, downloads: 3 },
{ id: 6, alt: 'kombi', filename: 'auto2.jpg', category: 3, downloads: 17 }
]);
// W stanie poczatkowym wszystkie switche sa wlaczone
const [wlaczoneKategorie, setWlaczoneKategorie] = useState([1, 2, 3]);
function przelaczKategorie(numer) {
setWlaczoneKategorie(function (poprzednie) {
if (poprzednie.includes(numer)) {
return poprzednie.filter(function (k) { return k !== numer; });
}
return poprzednie.concat([numer]);
});
}
function pobierz(idZdjecia) {
setZdjecia(function (poprzednie) {
return poprzednie.map(function (zdjecie) {
if (zdjecie.id === idZdjecia) {
return Object.assign({}, zdjecie, { downloads: zdjecie.downloads + 1 });
}
return zdjecie;
});
});
}
const styleZdjecia = { margin: '5px', borderRadius: '10px' };
return (
<div className="container">
<h1>Kategorie zdjec</h1>
<div className="mb-3">
{kategorie.map(function (kategoria) {
return (
<div className="form-check form-switch form-check-inline"
key={kategoria.numer}>
<input className="form-check-input"
type="checkbox"
id={'switch' + kategoria.numer}
checked={wlaczoneKategorie.includes(kategoria.numer)}
onChange={function () { przelaczKategorie(kategoria.numer); }} />
<label className="form-check-label"
htmlFor={'switch' + kategoria.numer}>
{kategoria.etykieta}
</label>
</div>
);
})}
</div>
<div className="d-flex flex-wrap">
{zdjecia
.filter(function (zdjecie) {
return wlaczoneKategorie.includes(zdjecie.category);
})
.map(function (zdjecie) {
return (
<div className="text-center" key={zdjecie.id}>
<img src={'assets/' + zdjecie.filename}
alt={zdjecie.alt}
width="220"
style={styleZdjecia} />
<h4>Pobrania: {zdjecie.downloads}</h4>
<button type="button"
className="btn btn-success"
onClick={function () { pobierz(zdjecie.id); }}>
Pobierz
</button>
</div>
);
})}
</div>
</div>
);
}
export default App;- Stan w tablicy, nie w licznikach obok. Arkusz pisze: „liczba pobrań jest zapisywana w tablicy z obiektami zdjęć". Trzymanie osobnego licznika w drugim useState formalnie nie spełnia wymagania, choć wygląda tak samo.
- Filtrowanie przez tablicę włączonych kategorii, a nie przez trzy osobne zmienne logiczne. Dzięki temu dodanie czwartej kategorii nie wymaga zmiany kodu — arkusz żąda uniwersalności.
- Pętla i warunek to tutaj
mapplusfilter. Nie wolno wypisać sześciu bloków ręcznie — to natychmiastowa strata punktu za „zastosowano pętle oraz warunki". d-flex flex-wraprealizuje „bloki wyświetlone jeden obok drugiego" i zawija je, gdy zabraknie miejsca w wierszu.- Marginesy 5 px i zaokrąglone rogi są narzucone przez arkusz — możesz je dać stylem wbudowanym albo klasą w
App.css, byle były na zdjęciu, a nie na całym bloku.
Gdzie się traci punkty:
mutacja obiektu w miejscu (zdjecie.downloads++
bez setZdjecia)
zwiększa wartość w pamięci, ale React nie przerysuje widoku — na ekranie
nic się nie dzieje i przepada wymóg „na bieżąco powoduje wyświetlenie tej
modyfikacji". Drugi klasyk: checked
bez onChange —
React zablokuje wtedy przełączanie switcha i wypisze ostrzeżenie w konsoli.
Trzeci: ścieżka do zdjęć. Przy folderze
public/assets
działa assets/plik.jpg,
przy src/assets
trzeba plik zaimportować. Sprawdź w przeglądarce, że obrazy naprawdę się
ładują — puste ramki to strata punktu za wygląd.
Pokaż rozwiązanieWariant Angular — komponent i szablon HTML
Krok zero: dopisz import Bootstrapa do src/styles.css
dokładnie tak, jak podaje tabela 1 arkusza.
/* src/styles.css */
@import "~bootstrap/dist/css/bootstrap.css";
/* jesli tylda nie zadziala w nowszej wersji Angulara: */
/* @import "bootstrap/dist/css/bootstrap.css"; */import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './app.component.html'
})
export class AppComponent {
kategorie = [
{ numer: 1, etykieta: 'Kwiaty', wlaczona: true },
{ numer: 2, etykieta: 'Zwierzeta', wlaczona: true },
{ numer: 3, etykieta: 'Samochody', wlaczona: true }
];
// Tablica obiektow przepisana z pliku dane.txt
zdjecia = [
{ id: 1, alt: 'roza', filename: 'kwiat1.jpg', category: 1, downloads: 12 },
{ id: 2, alt: 'tulipan', filename: 'kwiat2.jpg', category: 1, downloads: 5 },
{ id: 3, alt: 'kot', filename: 'zwierze1.jpg', category: 2, downloads: 8 },
{ id: 4, alt: 'pies', filename: 'zwierze2.jpg', category: 2, downloads: 21 },
{ id: 5, alt: 'sedan', filename: 'auto1.jpg', category: 3, downloads: 3 },
{ id: 6, alt: 'kombi', filename: 'auto2.jpg', category: 3, downloads: 17 }
];
czyWidoczne(numerKategorii: number): boolean {
const kategoria = this.kategorie.find(function (k) {
return k.numer === numerKategorii;
});
return kategoria ? kategoria.wlaczona : false;
}
pobierz(zdjecie: any): void {
zdjecie.downloads = zdjecie.downloads + 1;
}
}<div class="container">
<h1>Kategorie zdjec</h1>
<div class="mb-3">
<div class="form-check form-switch form-check-inline"
*ngFor="let kategoria of kategorie">
<input class="form-check-input"
type="checkbox"
id="switch{{kategoria.numer}}"
[(ngModel)]="kategoria.wlaczona">
<label class="form-check-label"
for="switch{{kategoria.numer}}">
{{kategoria.etykieta}}
</label>
</div>
</div>
<div class="d-flex flex-wrap">
<div class="text-center"
*ngFor="let zdjecie of zdjecia">
<div *ngIf="czyWidoczne(zdjecie.category)">
<img [src]="'assets/' + zdjecie.filename"
[alt]="zdjecie.alt"
width="220"
style="margin: 5px; border-radius: 10px;">
<h4>Pobrania: {{zdjecie.downloads}}</h4>
<button type="button"
class="btn btn-success"
(click)="pobierz(zdjecie)">
Pobierz
</button>
</div>
</div>
</div>
</div>ngModelnie zadziała bez zaimportowanegoFormsModule. Błąd „Can't bind to ngModel" to najczęstsze zatrzymanie na tym arkuszu — w projekcie modułowym import idzie doapp.module.ts, w standalone do tablicyimportskomponentu.*ngFori*ngIfnie mogą stać na tym samym elemencie. Dlatego warunek siedzi w zagnieżdżonymdiv. W Angularze 17+ możesz zamiast tego użyć składni@fori@if.- W Angularze mutacja
zdjecie.downloads = zdjecie.downloads + 1jest w porządku — wykrywanie zmian złapie ją samo. To odwrotność zasady z Reacta i dlatego przepisywanie rozwiązania między frameworkami bez zrozumienia kończy się źle.
Wspólne dla obu wariantów:
nagłówek liczby pobrań ma być czwartego stopnia, a tytuł
strony pierwszego. Arkusz podaje to wprost i egzaminator
sprawdza to w kodzie, nie na oko — <h3>
wygląda podobnie i nie daje punktu. Tekst alternatywny każdego zdjęcia
bierzesz z pola alt
w danych, nie wpisujesz własnego.
6
Rezultat 4: dokumentacja aplikacji
- Nagłówek dowolnej metody aplikacji konsolowej opisany komentarzem według wzoru z listingu 1 — wieloliniowym albo kilkoma jednoliniowymi, nad lub pod nazwą metody.
- We wzorze wypełniasz: nazwę metody, opis, parametry (albo słowo brak dla metody bezparametrowej), zwracany typ z opisem oraz autora, czyli numer zdającego.
- Zrzuty ekranu dokumentujące uruchomienie obu aplikacji. Cały obszar ekranu, z widocznym paskiem zadań. Na zrzucie ma być okno z wynikiem działania programu oraz otwarte środowisko z projektem albo terminal z kompilacją.
- Jeśli aplikacja się nie uruchamia — zrzut ze spisem błędów i otwartym środowiskiem programistycznym.
- Nazwy zrzutów:
konsola1,konsola2, ... orazweb1,web2, ... Dla weba: stan początkowy, różne kombinacje switchy, działanie przycisku „Pobierz". - Plik
egzaminw edytorze tekstu pakietu biurowego z nazwą systemu operacyjnego, nazwami środowisk programistycznych i nazwami języków programowania. - Wszystko ląduje w podfolderze
dokumentacja.
To jest ćwierć oceny za piętnaście minut pracy. Napisz swój nagłówek według wzoru, potem sprawdź, czy nie zgubiłeś żadnej z pięciu pozycji.
Pokaż rozwiązanieSzablon nagłówka metody i gotowa treść pliku egzamin
**********************************************
nazwa metody: <nazwa>
opis metody: <krotki opis, co robi metoda>
parametry: <nazwa i opis parametru1, lub "brak">
<nazwa i opis parametru2>
...
zwracany typ i opis: <nazwa typu i opis co jest zwracane lub "brak">
autor: <numer zdajacego>
**********************************************/***********************************************
nazwa metody: WyswietlNieparzyste
opis metody: przeglada tablice i wypisuje na konsoli
wszystkie wartosci nieparzyste, kazda
w osobnym wierszu
parametry: brak
zwracany typ i opis: int - liczba znalezionych i wyswietlonych
wartosci nieparzystych
autor: PESEL
***********************************************/
public int WyswietlNieparzyste()
{
// ...
}# **********************************************
# nazwa metody: wyswietl_nieparzyste
# opis metody: przeglada tablice i wypisuje na konsoli
# wszystkie wartosci nieparzyste
# parametry: brak
# zwracany typ i opis: int - liczba znalezionych i wyswietlonych
# wartosci nieparzystych
# autor: PESEL
# **********************************************
def wyswietl_nieparzyste(self):
passZapisujesz go w edytorze tekstu pakietu biurowego pod nazwą
egzamin
(rozszerzenie zostawiasz domyślne, .odt albo .docx). Wpisujesz to, czego
naprawdę użyłeś na stanowisku:
DOKUMENTACJA APLIKACJI
Numer zdajacego: PESEL
1. System operacyjny
Windows 10 Education 64-bit
(albo: Windows 11 Pro / Ubuntu 22.04 LTS)
2. Srodowiska programistyczne
- aplikacja konsolowa: Microsoft Visual Studio 2022 Community
- aplikacja webowa: Visual Studio Code 1.85
- przegladarka testowa: Mozilla Firefox 121
3. Jezyki programowania i technologie
- aplikacja konsolowa: C# (.NET 8)
- aplikacja webowa: JavaScript, HTML, CSS
- biblioteka/framework: React.js 18, Bootstrap 5
4. Wykaz zrzutow ekranu
konsola1 - kompilacja i wyswietlenie elementow tablicy
konsola2 - wyszukiwanie, liczby nieparzyste i srednia
web1 - stan poczatkowy, wszystkie switche wlaczone
web2 - odznaczona kategoria Zwierzeta
web3 - wzrost liczby pobran po kliknieciu Pobierz- Ustaw okna tak, żeby na jednym ekranie było widać i wynik działania, i środowisko z projektem. Arkusz żąda obu rzeczy na jednym zrzucie.
- Naciśnij klawisz zrzutu całego ekranu — nie narzędzie wycinania. Kadrowany zrzut nie spełnia warunku „cały obszar ekranu monitora z widocznym paskiem zadań".
- Zapisz od razu do folderu
dokumentacjapod właściwą nazwą. Przenoszenie i przemianowywanie na końcu to strata czasu i okazja do pomyłki. - Web wymaga co najmniej trzech zrzutów, bo arkusz wymienia trzy sytuacje: stan początkowy, różne kombinacje switchy, działanie przycisku „Pobierz". Zasada ogólna z arkusza: tyle zrzutów, ile interakcji podejmuje aplikacja.
Gdzie się traci punkty:
pominięcie linii autor
z numerem zdającego — to jedna z pięciu pozycji wzoru i liczy się osobno.
Drugi klasyk: wpisanie w parametrach myślnika albo zostawienie pustego
miejsca zamiast słowa brak, którego arkusz żąda wprost.
Trzeci: zrzut zrobiony na wąskim oknie terminala, na którym nie widać
środowiska programistycznego. Czwarty, najboleśniejszy: aplikacja
webowa nie ruszyła, więc zdający nie zrobił żadnego zrzutu — a zrzut
z listą błędów kompilacji jest w arkuszu przewidziany i punktowany.
7
Checklista przed oddaniem
- Oba pola klasy są prywatne, nie chronione i nie publiczne.
- Konstruktor przyjmuje rozmiar i losuje wartości od 1 do 1000 — sprawdź granice w swoim języku.
- Obiekt w programie głównym utworzony z rozmiarem większym niż 20.
- Wyświetlanie w formacie
indeks: wartość. - Wyszukiwanie zwraca
-1, a program główny milczy, gdy nic nie znaleziono. - Metoda nieparzystych wyświetla wartości i zwraca ich liczbę; program główny wypisuje tę liczbę z komentarzem.
- Średnia jest liczbą zmiennoprzecinkową.
- Nazwy zmiennych i metod są znaczące, wcięcia równe.
- Bootstrap podłączony i widoczny — switche wyglądają jak przełączniki, a nie jak zwykłe kwadraciki.
- Nagłówek
h1o treści „Kategorie zdjęć", liczby pobrań wh4. - Trzy switche z etykietami Kwiaty, Zwierzęta, Samochody, wszystkie włączone po odświeżeniu strony.
- Odznaczenie switcha ukrywa dokładnie jedną kategorię, ponowne zaznaczenie ją przywraca.
- Zdjęcia mają margines 5 px i zaokrąglone rogi, stoją jeden obok drugiego.
- Kliknięcie „Pobierz" podnosi licznik tego jednego zdjęcia natychmiast, wielokrotne kliknięcia sumują się.
- Wszystkie obrazy ładują się z folderu
assets— żadnej pustej ramki. - Bloki generowane pętlą; dopisanie siódmego zdjęcia do tablicy nie wymaga zmiany szablonu.
- Nagłówek metody w komentarzu ma wszystkie pięć pozycji wzoru, w tym autor: numer zdającego.
- Zrzuty obejmują cały ekran z paskiem zadań, mają nazwy
konsola1,web1i dalej. - Plik
egzaminwymienia system, środowiska i języki. - Folder główny nazwany numerem zdającego, w środku dokładnie trzy podfoldery o narzuconych nazwach.
- W podfolderach są archiwa
konsola.zipiweb.zipz całymi projektami. - Poza archiwami zostały tylko modyfikowane pliki źródłowe i plik wykonywalny.
- Płyta nagrana, otwarta i sprawdzona po nagraniu, opisana numerem zdającego, zapakowana w pudełku wraz z arkuszem.
Ostatnie pięć minut: nie poprawiaj już kodu. Otwórz płytę, przejdź trzy podfoldery, policz pliki i porównaj z listą z tej checklisty. Poprawka, która nie trafi na płytę, nie istnieje dla egzaminatora.
Przeczytanie rozwiązania to nie to samo, co napisanie dwóch aplikacji w 180 minut. Otwórz oryginalny arkusz, ustaw stoper i zrób zadanie od zera — dopiero wynik tej próby pokaże, gdzie naprawdę stoisz.