Przejdź do głównej treści
INF.04-02-22.06-SGCzerwiec 2022 · 30 kroków · ok. 77 min · 34 kryteria z klucza

Rozwiązanie krok po kroku: Klasa Osoba i formularz kursów (C# + React)

Rozwiązujemy arkusz z klasą Osoba i formularzem zapisu na kursy w wersji C# + React: w konsoli klasa z prywatnymi polami, publicznym licznikiem statycznym, trzema konstruktorami (bezparametrowym, z dwoma parametrami i kopiującym) i metodą powitania, a w programie głównym testy z części III; w przeglądarce jednokomponentowa aplikacja React z Bootstrapem - lista kursów, formularz i wynik zapisu w konsoli przeglądarki. Do tego zrzuty i dokument egzamin.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Najpierw piszemy najprostszą wersję, której wymaga klucz, a w etapie „Dobre praktyki” poprawiamy to, czego klucz nie sprawdza.

Co widać w podglądzie. Wyniki programu konsolowego pochodzą z prawdziwego uruchomienia kodu z danego kroku (.NET 10) z danymi wpisanymi z klawiatury. Podgląd aplikacji webowej wykonuje kod komponentu z kroku w prawdziwym Reakcie z Bootstrapem, a u dołu ma panel konsoli przeglądarki (to, co na egzaminie widać po F12): wpisz imię i numer kursu, kliknij „Zapisz do kursu”. Wszystkie scenariusze sprawdziliśmy też na prawdziwym projekcie Vite.

Arkusz pozwala też na C++, Javę albo Pythona w konsoli i na Angulara w części webowej - ten wariant to jedna z możliwych dróg.

Jak liczą się punkty. Klucz ma 34 kryteria (R.1.1 - R.4.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 26 punktów - można stracić 8 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Przygotowanie

    Krok 1 · po tym etapie masz 0 z 34 kryteriów (pkt)

    Krok 1: Folder zdającego i trzy podfoldery

    ok. 2 min
    Polecenie z arkusza

    Utwórz folder i nazwij go swoim numerem. W folderze utwórz podfoldery: konsola, web, testy.

    Na pulpicie zakładamy folder o nazwie z numerem PESEL, a w nim konsola, web i testy. W tym arkuszu zrzuty ekranu obu aplikacji i dokument egzamin trafiają do folderu testy. Arkusz nie ma archiwum z materiałami.

  2. Etap 2: Aplikacja konsolowa

    Kroki 2-9 · po tym etapie masz 12 z 34 kryteriów (pkt)

    Krok 2: Projekt konsola w C#

    ok. 3 min
    Polecenie z arkusza

    Korzystając z mechanizmów programowania obiektowego zaprojektuj część logiki systemu forum użytkowników. Zastosowano obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python.

    Wybieramy C#. W Visual Studio: Utwórz nowy projekt → Aplikacja konsolowa, nazwa konsola (stąd archiwum konsola.zip); w terminalu to samo daje dotnet new console -n konsola. W Program.cs zostawiamy klasę Program z pustą metodą Main - klasę Osoba dopiszemy nad nią.

    konsola/Program.cs C# nowy plik
    class Program
    {
        static void Main()
        {
        }
    }
  3. Krok 3: Klasa Osoba z prywatnymi polami id i imię

    Kryteria: , , ,

    R.2.1 W programie zdefiniowano klasę Osoba

    R.2.2 Zdefiniowano pola o zakresie private reprezentujące: id dowolnego typu liczbowego całkowitego, imię dowolnego typu tekstowego (w Python zgodnie z konwencją zastosowano jeden lub dwa podkreślniki w nazwie, np. __imie)

    R.1.4 Użyto znaczące nazewnictwo zmiennych / pól. Wyjątkami od reguły są zmienne bufor, tmp, iteratory pętli itp. Kryterium nie jest spełnione tylko wtedy, gdy nazwy zmiennych nic nie znaczą, np. x, a, tab, tablica, foo

    R.1.5 Zastosowano typy pól pasujące do problemu. W przypadku Python zastosowano jawną konwersję do typu int dla wczytywanych liczb

    ok. 3 min
    Polecenie z arkusza

    Zaimplementuj klasę o nazwie osoba z konstruktorami, metodą i obsługą pola statycznego. Klasa Osoba zawiera: Dwa pola: numeryczne id oraz tekstowe imię osoby. Dostęp do obu pól ma jedynie klasa. W przypadku późniejszego rozszerzenia klasy, klasy potomne nie mają dostępu do tych pól.

    Nazwa klasy Osoba jest narzucona przez arkusz, nazwy pól wybieramy sami. „Dostęp ma jedynie klasa, klasy potomne nie” to dokładnie modyfikator private - protected dopuściłby klasy potomne, a public wszystkich (R.2.2). Typy: int na liczbę całkowitą, string na tekst (R.1.5). Pola nazywamy znacząco: id i name (R.1.4).

    Typowe błędy

    protected zamiast private - klasy potomne miałyby dostęp do pól, czego arkusz zabrania.

    konsola/Program.cs C# zmienione: 6 linii
    class Osoba
    {
        private int id;
        private string name;
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  4. Krok 4: Publiczne pole statyczne z liczbą instancji

    Kryteria:

    R.2.3 Zdefiniowano pole statyczne o zakresie public. Każde powołanie obiektu inkrementuje pole (w konstruktorze). W programie głównym przed powołaniem obiektów odwołano się do pola za pomocą nazwy klasy

    ok. 2 min
    Polecenie z arkusza

    Ogólnie dostępne pole statyczne zliczające liczbę instancji klasy, początkowo wypełnione wartością 0.

    Pole static należy do klasy, a nie do pojedynczego obiektu - jest jedno, wspólne dla wszystkich osób, więc nadaje się na licznik. „Ogólnie dostępne” to public, a wartość początkowa 0. Z zewnątrz odczytamy je przez nazwę klasy: Osoba.InstanceCount (R.2.3).

    konsola/Program.cs C# zmienione: 1 linia
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  5. Krok 5: Konstruktor bezparametrowy

    Kryteria: ,

    R.2.4 Zdefiniowano konstruktor bezparametrowy, w którym są przypisywane wartości pól: 0 dla identyfikatora, "" lub null dla imienia (w Python konstruktor z parametrami, których wartości domyślne to: 0 i "" lub null)

    R.2.3 Zdefiniowano pole statyczne o zakresie public. Każde powołanie obiektu inkrementuje pole (w konstruktorze). W programie głównym przed powołaniem obiektów odwołano się do pola za pomocą nazwy klasy

    ok. 2 min
    Polecenie z arkusza

    Trzy konstruktory: bezparametrowy, ustawia wartości 0 i pusty dla pól. Każdy z konstruktorów dodatkowo inkrementuje liczbę instancji klasy.

    Konstruktor ma nazwę klasy i nie ma typu zwracanego. Ustawia id na 0, imię na pusty napis "" i zwiększa licznik instancji o jeden (InstanceCount++) - każde new Osoba() to jedna osoba więcej (R.2.4, R.2.3).

    konsola/Program.cs C# zmienione: 7 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  6. Krok 6: Konstruktor z dwoma parametrami

    Kryteria:

    R.2.5 Zdefiniowano konstruktor z dwoma parametrami typu liczbowego i napisowego, wartości parametrów są przypisane do identyfikatora i imienia nowego obiektu

    ok. 2 min
    Polecenie z arkusza

    Konstruktor z dwoma parametrami, które przekazują wartości id i imienia.

    Drugi konstruktor o tej samej nazwie, ale z innymi parametrami - to przeciążenie konstruktora; C# wybiera wersję po liczbie i typach argumentów. Parametry nazywają się tak jak pola, więc pole wskazujemy przez this: this.id = id przepisuje wartość parametru do pola obiektu (R.2.5). On też zwiększa licznik.

    Typowe błędy

    id = id; bez this przypisuje parametr sam do siebie - pole zostaje z wartością 0.

    konsola/Program.cs C# zmienione: 7 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  7. Krok 7: Konstruktor kopiujący

    Kryteria:

    R.2.6 Zdefiniowano konstruktor kopiujący, którego argumentem jest obiekt klasy Osoba, w konstruktorze następuje przepisanie wartości pól obiektu do nowo utworzonej instancji (w Python metoda kopiująca z parametrem typu Osoba)

    ok. 2 min
    Polecenie z arkusza

    Konstruktor kopiujący.

    Konstruktor kopiujący przyjmuje inny obiekt tej samej klasy i przepisuje jego pola do nowego obiektu (R.2.6). Pola other.id i other.name są prywatne, ale to wciąż kod klasy Osoba - w C# metody klasy mają dostęp do pól prywatnych każdego jej obiektu. Powstaje nowa, niezależna osoba o tych samych danych, więc licznik też rośnie.

    Typowe błędy

    Osoba copy = person; to nie kopia, tylko druga zmienna wskazująca ten sam obiekt - nie powstaje nowa instancja i licznik się nie zmienia.

    konsola/Program.cs C# zmienione: 7 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  8. Krok 8: Metoda Greet: powitanie z imieniem

    Kryteria: ,

    R.2.7 Zdefiniowano metodę z argumentem typu napisowego, która wypisuje lub zwraca napis w postaci: „Cześć <argument>, mam na imię <imie>”, gdzie pole <argument> jest przekazane jako parametr wejściowy metody, a <imie> jest wartością pola imie obiektu klasy Osoba

    R.1.3 Użyto znaczące nazewnictwo funkcji / metod

    ok. 2 min
    Polecenie z arkusza

    Metodę do wypisania imienia obiektu klasy Osoba w postaci: „Cześć <argument>, mam na imię <imie>”, gdzie pole <argument> jest innym imieniem przekazanym jako parametr wejściowy metody.

    Metoda przyjmuje imię rozmówcy (otherName) i wypisuje napis sklejony z niego i z imienia zapisanego w obiekcie (R.2.7). Nie zwraca wartości, więc ma typ void. Nazwa Greet („przywitaj”) mówi, co robi (R.1.3).

    konsola/Program.cs C# zmienione: 5 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            Console.WriteLine("Cześć " + otherName + ", mam na imię " + name);
        }
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  9. Krok 9: Brak danych, gdy imię jest puste

    Kryteria: ,

    R.2.8 Zapisano warunek w metodzie wypisującej imię - gdy pole z imieniem osoby jest puste wypisywany jest komunikat „Brak danych”

    R.1.2 Kod zapisano z wcięciami dla zagłębień bloków

    ok. 2 min
    Polecenie z arkusza

    Jeżeli w obiekcie nie wypełniono wartości pola z imieniem wyświetlany jest komunikat „Brak danych”.

    Obiekt z konstruktora bezparametrowego ma imię "". Sprawdzamy to warunkiem i wtedy wypisujemy „Brak danych” zamiast powitania (R.2.8).

    konsola/Program.cs C# zmienione: 8 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            if (name == "")
            {
                Console.WriteLine("Brak danych");
            }
            else
            {
                Console.WriteLine("Cześć " + otherName + ", mam na imię " + name);
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
        }
    }
  10. Etap 3: Testy w programie głównym

    Kroki 10-14 · po tym etapie masz 23 z 34 kryteriów (pkt)

    Krok 10: Licznik przed utworzeniem obiektów

    Kryteria: ,

    R.4.1 W programie głównym aplikacji konsolowej zapisano testy sprawdzające działanie klasy Osoba (przynajmniej jedna instrukcja zgodna ze składnią operująca na klasie lub obiekcie klasy)

    R.4.2 Na początku działania aplikacji jest wyświetlany stan pola z liczbą instancji, który jest równy 0

    ok. 2 min
    Polecenie z arkusza

    W podfolderze konsola w programie głównym aplikacji konsolowej należy sprawdzić działanie klasy poprzez, kolejno: Wyświetlenie komunikatu „Liczba zarejestrowanych osób to <licznik>”, gdzie <licznik> jest wartością pobraną z pola statycznego klasy.

    Testy z części III piszemy w Main, jeden po drugim. Pierwszy: odczyt pola statycznego przez nazwę klasy - Osoba.InstanceCount; żaden obiekt jeszcze nie istnieje, więc wynik to 0 (R.4.1, R.4.2, R.2.3).

    konsola/Program.cs C# zmienione: 1 linia
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            if (name == "")
            {
                Console.WriteLine("Brak danych");
            }
            else
            {
                Console.WriteLine("Cześć " + otherName + ", mam na imię " + name);
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczba zarejestrowanych osób to 0
  11. Krok 11: Trzy obiekty, każdy innym konstruktorem

    Kryteria: ,

    R.4.4 Powołane zostały trzy obiekty, każdy innym konstruktorem: bezparametrowym, dwuparametrowym, kopiującym (w Python bezparametrowym i metodą kopiującą). Dane dla konstruktora dwuparametrowego zostały pobrane z klawiatury w programie głównym

    R.1.6 Program podejmuje komunikację z użytkownikiem, np. monit o wprowadzenie danych jest znaczący (w przypadku aplikacji web podpisy kontrolek, komunikaty w konsoli)

    ok. 3 min
    Polecenie z arkusza

    Utworzenie obiektu za pomocą konstruktora bezparametrowego. Utworzenie obiektu za pomocą konstruktora z dwoma parametrami. Dane obiektu wprowadzane z klawiatury. Utworzenie obiektu za pomocą konstruktora kopiującego. Obiekt z wypełnionymi polami jest źródłem kopiowania danych.

    Trzy obiekty, trzy konstruktory (R.4.4):

    • new Osoba() - bezparametrowy,
    • new Osoba(id, name) - z danymi wczytanymi z klawiatury; int.Parse zamienia wpisany tekst na liczbę, a komunikaty „Podaj ...” mówią, czego program oczekuje (R.1.6),
    • new Osoba(person) - kopia osoby z wypełnionymi polami, jak wymaga arkusz.

    Program pyta o dane, ale jeszcze nic nie wypisuje - powitania w następnym kroku.

    konsola/Program.cs C# zmienione: 10 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            if (name == "")
            {
                Console.WriteLine("Brak danych");
            }
            else
            {
                Console.WriteLine("Cześć " + otherName + ", mam na imię " + name);
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
    
            Osoba emptyPerson = new Osoba();
    
            Console.Write("Podaj id: ");
            int id = int.Parse(Console.ReadLine());
            Console.Write("Podaj imię: ");
            string name = Console.ReadLine();
            Osoba person = new Osoba(id, name);
    
            Osoba copiedPerson = new Osoba(person);
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczba zarejestrowanych osób to 0
    Podaj id: 7
    Podaj imię: Anna
  12. Krok 12: Powitanie Jana przez każdy obiekt

    Kryteria: ,

    R.4.5 Dla obiektów których pole imię zostało ustawione w konstruktorze został wyświetlony komunikat: "Cześć Jan, mam na imię <imie> "

    R.4.6 Dla obiektu utworzonego konstruktorem bezparametrowym został wyświetlony komunikat "Brak danych"

    ok. 2 min
    Polecenie z arkusza

    Wywołanie metody do wypisania imienia z parametrem wejściowym równym „Jan” dla wszystkich utworzonych obiektów.

    Każdy obiekt wita Jana. Obiekt pusty wypisuje „Brak danych” (R.4.6), a osoba z klawiatury i jej kopia - „Cześć Jan, mam na imię Anna” (R.4.5). Dwa jednakowe powitania potwierdzają, że konstruktor kopiujący przepisał imię.

    konsola/Program.cs C# zmienione: 4 linie
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            if (name == "")
            {
                Console.WriteLine("Brak danych");
            }
            else
            {
                Console.WriteLine("Cześć " + otherName + ", mam na imię " + name);
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
    
            Osoba emptyPerson = new Osoba();
    
            Console.Write("Podaj id: ");
            int id = int.Parse(Console.ReadLine());
            Console.Write("Podaj imię: ");
            string name = Console.ReadLine();
            Osoba person = new Osoba(id, name);
    
            Osoba copiedPerson = new Osoba(person);
    
            emptyPerson.Greet("Jan");
            person.Greet("Jan");
            copiedPerson.Greet("Jan");
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczba zarejestrowanych osób to 0
    Podaj id: 7
    Podaj imię: Anna
    Brak danych
    Cześć Jan, mam na imię Anna
    Cześć Jan, mam na imię Anna
  13. Krok 13: Licznik po utworzeniu obiektów

    Kryteria: ,

    R.4.3 Na końcu działania aplikacji jest wyświetlany stan pola statycznego klasy, który jest równy liczbie powołanych obiektów

    R.1.1 Kod źródłowy zapisano w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych instrukcji blokowej

    ok. 2 min
    Polecenie z arkusza

    Ponowne wyświetlenie komunikatu „Liczba zarejestrowanych osób to <licznik>”, gdzie <licznik> jest wartością pobraną z pola statycznego klasy.

    Ten sam komunikat na końcu: licznik wynosi 3 - po jednym za każdy konstruktor (R.4.3). To najprostszy dowód, że każdy z trzech konstruktorów zwiększa licznik.

    konsola/Program.cs C# zmienione: 2 linie
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            if (name == "")
            {
                Console.WriteLine("Brak danych");
            }
            else
            {
                Console.WriteLine("Cześć " + otherName + ", mam na imię " + name);
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
    
            Osoba emptyPerson = new Osoba();
    
            Console.Write("Podaj id: ");
            int id = int.Parse(Console.ReadLine());
            Console.Write("Podaj imię: ");
            string name = Console.ReadLine();
            Osoba person = new Osoba(id, name);
    
            Osoba copiedPerson = new Osoba(person);
    
            emptyPerson.Greet("Jan");
            person.Greet("Jan");
            copiedPerson.Greet("Jan");
    
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczba zarejestrowanych osób to 0
    Podaj id: 7
    Podaj imię: Anna
    Brak danych
    Cześć Jan, mam na imię Anna
    Cześć Jan, mam na imię Anna
    Liczba zarejestrowanych osób to 3
  14. Krok 14: Zrzut konsola1 i folder konsola

    Kryteria: , ,

    R.1.7 Podjęto próbę skompilowania kodu, co udokumentowano obrazem przedstawiającym wykonywany program lub jego kompilację

    R.2.9 Program kompiluje się i uruchamia w konsoli, co udokumentowano zrzutem ekranu

    R.4.7 Zapisano przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji konsolowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 3 min
    Polecenie z arkusza

    Aplikacja konsolowa – dowolna liczba zrzutów nazwanych konsola1, konsola2 ... Kod aplikacji przygotuj do nagrania na płytę. W podfolderze konsola powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, plik z kodem źródłowym klasy oraz plik uruchomieniowy, jeżeli istnieje.

    Uruchamiamy program z Visual Studio, wpisujemy id i imię i robimy zrzut całego ekranu z wynikiem i środowiskiem - konsola1.png w folderze testy (R.1.7, R.2.9, R.4.7). W folderze konsola: konsola.zip z całym projektem, Program.cs (z klasą Osoba) i konsola.exe.

    Wynik programu bez zmian - taki jak po kroku 13.

  15. Etap 4: Aplikacja web

    Kroki 15-26 · po tym etapie masz 33 z 34 kryteriów (pkt)

    Krok 15: Nowy projekt React w Vite

    ok. 4 min
    Polecenie z arkusza

    Wykonaj aplikację internetową typu front-end obsługującą zapisy na kursy z zastosowaniem dostępnego na stanowisku egzaminacyjnym frameworka Angular lub biblioteki React.

    Wybieramy React. Projekt zakładamy w terminalu narzędziem Vite:

    npm create vite@latest web -- --template react
    cd web
    npm install
    

    Nazwa web od razu pasuje do archiwum web.zip. Vite tworzy src/main.jsx (wstawia komponent App do strony) i src/App.jsx z przykładowym licznikiem, który zastąpimy. npm run dev uruchamia serwer pod http://localhost:5173.

    web/src/main.jsx JSX nowy plik
    import { StrictMode } from 'react'
    import { createRoot } from 'react-dom/client'
    import './index.css'
    import App from './App.jsx'
    
    createRoot(document.getElementById('root')).render(
      <StrictMode>
        <App />
      </StrictMode>,
    )

    Wynik programu bez zmian - taki jak po kroku 13.

  16. Krok 16: Biblioteka Bootstrap w projekcie

    ok. 2 min
    Polecenie z arkusza

    Zastosuj bibliotekę Bootstrap do zdefiniowania stylu formularza. React.js: To use Bootstrap add: import 'bootstrap/dist/css/bootstrap.css';

    npm install bootstrap w folderze web, a potem import z tabeli 1 w main.jsx - w miejsce import './index.css', bo arkusz stylów z szablonu Vite wyśrodkowuje stronę i ustawia ciemne tło, a formularz ma wyglądać jak na obrazie 1b.

    Typowe błędy

    npm install bootstrap uruchomione poza folderem web instaluje pakiet gdzie indziej - import zgłosi błąd „Failed to resolve import”.

    web/src/main.jsx JSX zmienione: 1 linia
    import { StrictMode } from 'react'
    import { createRoot } from 'react-dom/client'
    import 'bootstrap/dist/css/bootstrap.css'
    import App from './App.jsx'
    
    createRoot(document.getElementById('root')).render(
      <StrictMode>
        <App />
      </StrictMode>,
    )

    Wynik programu bez zmian - taki jak po kroku 13.

  17. Krok 17: Komponent App z tablicą kursów

    Kryteria:

    R.3.1 Zapisano w aplikacji jeden komponent, który zawiera zadeklarowaną tablicę kursów, np. kursy = ["Programowanie w C#", "Angular dla początkujących", "Kurs Django"];

    ok. 2 min
    Polecenie z arkusza

    Aplikacja składa się z jednego komponentu. Danymi komponentu jest tablica z nazwami kursów, o elementach: "Programowanie w C#", "Angular dla początkujących", "Kurs Django". Dla uproszczenia tablica może być zdefiniowana jako pole komponentu.

    Zastępujemy całą zawartość App.jsx. Komponent w React to funkcja zwracająca JSX - zapis podobny do HTML wewnątrz JavaScriptu. Tablica courses stoi w komponencie, tak jak dopuszcza arkusz (R.3.1). Zwracamy kontener Bootstrapa (container - marginesy po bokach, mt-3 - odstęp od góry); w JSX klasę CSS podajemy atrybutem className, bo class to słowo zarezerwowane JavaScriptu. Strona jest na razie pusta.

    web/src/App.jsx JSX nowy plik
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
    
      return (
        <div className="container mt-3">
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  18. Krok 18: Nagłówek z liczbą kursów

    Kryteria:

    R.3.5 W nagłówku drugiego stopnia napisano liczbę kursów stosując atrybut długości tabeli ( kursy.length, lub this.kursy.length) oraz wypisano w liście przynajmniej jedną nazwę kursu odwołując się do dowolnego indeksu tabeli z nazwami kursów

    ok. 2 min
    Polecenie z arkusza

    Komponent wyświetla: Nagłówek drugiego stopnia o treści: „Liczba kursów: <liczba>”, gdzie <liczba> oznacza wielkość tablicy z nazwami kursów.

    W nawiasach klamrowych JSX wstawiamy wyrażenie JavaScriptu: courses.length to liczba elementów tablicy (R.3.5). Nie wpisujemy 3 na sztywno - arkusz zaznacza, że tablica może się zmienić, a nagłówek ma iść za nią.

    web/src/App.jsx JSX zmienione: 1 linia
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  19. Krok 19: Lista numerowana wszystkich kursów

    Kryteria:

    R.3.6 W liście <ol> wypisano wszystkie elementy tablicy z nazwami kursów niezależnie od wymiaru tablicy, np.: Angular: <li *ngFor= "let kurs of kursy">{{ kurs }}</li> React: {this.state.kursy.map(kurs => <li key={kurs}>{kurs}</li>)}

    ok. 3 min
    Polecenie z arkusza

    Listę numerowaną generowaną automatycznie dla wszystkich elementów tablicy, niezależnie od jej wymiaru.

    Lista numerowana to <ol> - numery 1, 2, 3 dopisuje przeglądarka. Elementy <li> tworzy metoda courses.map: dla każdego kursu zwraca jeden element listy, więc lista ma tyle pozycji, ile tablica (R.3.6). React wymaga dla elementów tworzonych w pętli atrybutu key o niepowtarzalnej wartości - tu nazwa kursu.

    Typowe błędy

    Trzy elementy <li> wpisane ręcznie nie zmienią się razem z tablicą - R.3.6 wymaga generowania listy z tablicy.

    web/src/App.jsx JSX zmienione: 3 linie
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  20. Krok 20: Formularz: pole Imię i nazwisko z etykietą

    Kryteria: ,

    R.3.2 Zdefiniowano formularz, tak że jego pola poprzedzono etykietą powiązaną z polem, zastosowano klasy bootstrap form-group oraz form-control np. <div class="form-group"> <label for="imieNazw">Imię i nazwisko: </label> <input id="imieNazw" type="text" class="form-control" ...> </div> Uwaga: w React.js atrybut className zamiast class oraz htmlFor zamiast for

    R.3.3 Zastosowano znaczące nazwy kontrolek dla atrybutu for i id. Kryterium należy uznać za spełnione zawsze wtedy, gdy nazwa nie jest przepisana z pomocy (exampleInputEmail1) i gdy nie jest postaci input1, x, itp.

    ok. 3 min
    Polecenie z arkusza

    Formularz składający się z: pola edycyjnego i jego etykiety o treści „Imię i nazwisko:”. Elementy formularza są formatowane zgodnie z obrazem 1a lub 1b za pomocą stylów biblioteki Bootstrap. Należy zastosować znaczące nazwy dla identyfikatorów pól formularza.

    Wzór z tabeli 1: grupa form-group, w niej etykieta i pole z klasą form-control (pełna szerokość, obramowanie, zaokrąglenie). Etykietę wiążemy z polem: atrybut for etykiety = id pola - w React piszemy htmlFor zamiast for (R.3.2). Identyfikator znaczący, fullName, a nie przepisany z pomocy exampleInputEmail1 (R.3.3).

    Klasa mb-3 dodaje odstęp pod grupą - w Bootstrapie 5, który instaluje npm, sama form-group (z Bootstrapa 4 w tabeli 1) już go nie daje.

    web/src/App.jsx JSX zmienione: 6 linii
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName" />
            </div>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  21. Krok 21: Pole numeryczne Numer kursu

    Kryteria: ,

    R.3.2 Zdefiniowano formularz, tak że jego pola poprzedzono etykietą powiązaną z polem, zastosowano klasy bootstrap form-group oraz form-control np. <div class="form-group"> <label for="imieNazw">Imię i nazwisko: </label> <input id="imieNazw" type="text" class="form-control" ...> </div> Uwaga: w React.js atrybut className zamiast class oraz htmlFor zamiast for

    R.3.3 Zastosowano znaczące nazwy kontrolek dla atrybutu for i id. Kryterium należy uznać za spełnione zawsze wtedy, gdy nazwa nie jest przepisana z pomocy (exampleInputEmail1) i gdy nie jest postaci input1, x, itp.

    ok. 2 min
    Polecenie z arkusza

    Edycyjnego pola numerycznego i jego etykiety o treści „Numer kursu:”. Be sure to use an appropriate type attribute on all inputs (e.g., email for email address or number for numerical information).

    Pole numeryczne to <input type="number"> - przeglądarka przyjmuje w nim tylko liczbę i dokłada strzałki zmiany wartości. Ta sama budowa grupy co wyżej, identyfikator courseNumber.

    web/src/App.jsx JSX zmienione: 4 linie
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName" />
            </div>
            <div className="form-group mb-3">
              <label htmlFor="courseNumber">Numer kursu:</label>
              <input type="number" className="form-control" id="courseNumber" />
            </div>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  22. Krok 22: Przycisk Zapisz do kursu

    Kryteria:

    R.3.4 Przypisano dla przycisku klasę btn btn-primary

    ok. 1 min
    Polecenie z arkusza

    Przycisku „Zapisz do kursu”.

    Przycisk z klasami btn btn-primary - niebieski jak na obrazie 1b (R.3.4). Typ submit: kliknięcie zatwierdza formularz, a to zdarzenie obsłużymy za chwilę.

    web/src/App.jsx JSX zmienione: 1 linia
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName" />
            </div>
            <div className="form-group mb-3">
              <label htmlFor="courseNumber">Numer kursu:</label>
              <input type="number" className="form-control" id="courseNumber" />
            </div>
            <button type="submit" className="btn btn-primary">Zapisz do kursu</button>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  23. Krok 23: Wartości pól w stanie komponentu

    Kryteria:

    R.3.7 W kodzie zdefiniowano funkcję realizującą zatwierdzenie formularza zdarzeniem submit (onSubmit, ngSubmit), dostęp do wartości zapisanych w polach następuje poprzez mechanizmy charakterystyczne dla środowiska np. (f, i - dowolna nazwa) Angular: <form #f="ngForm" (ngSubmit) = "onSubmit(f)"> ... <input ... ngModel name="i"> React: i = React.createRef(); ... <form onSubmit={this.hndlSubmit}>...<input ... ref={this.i}/>

    ok. 3 min
    Polecenie z arkusza

    Aplikacja w stanie początkowym wyświetla puste pola formularza.

    Żeby po kliknięciu przycisku odczytać, co wpisano, trzymamy wartości pól w stanie komponentu: useState('') zwraca bieżącą wartość i funkcję, która ją zmienia; pusty napis na starcie to puste pola z arkusza. Pole dostaje value ze stanu, a onChange przy każdym wpisanym znaku zapisuje do stanu event.target.value - to tzw. pole kontrolowane. useState importujemy z react na górze pliku.

    web/src/App.jsx JSX zmienione: 8 linii
    import { useState } from 'react'
    
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
      const [fullName, setFullName] = useState('')
      const [courseNumber, setCourseNumber] = useState('')
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName"
                value={fullName} onChange={event => setFullName(event.target.value)} />
            </div>
            <div className="form-group mb-3">
              <label htmlFor="courseNumber">Numer kursu:</label>
              <input type="number" className="form-control" id="courseNumber"
                value={courseNumber} onChange={event => setCourseNumber(event.target.value)} />
            </div>
            <button type="submit" className="btn btn-primary">Zapisz do kursu</button>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  24. Krok 24: Zatwierdzenie formularza: imię w konsoli przeglądarki

    Kryteria: ,

    R.3.7 W kodzie zdefiniowano funkcję realizującą zatwierdzenie formularza zdarzeniem submit (onSubmit, ngSubmit), dostęp do wartości zapisanych w polach następuje poprzez mechanizmy charakterystyczne dla środowiska np. (f, i - dowolna nazwa) Angular: <form #f="ngForm" (ngSubmit) = "onSubmit(f)"> ... <input ... ngModel name="i"> React: i = React.createRef(); ... <form onSubmit={this.hndlSubmit}>...<input ... ref={this.i}/>

    R.1.6 Program podejmuje komunikację z użytkownikiem, np. monit o wprowadzenie danych jest znaczący (w przypadku aplikacji web podpisy kontrolek, komunikaty w konsoli)

    ok. 3 min
    Polecenie z arkusza

    Po wybraniu przycisku formularza jest generowane zdarzenie zatwierdzenia formularza, które wyświetla w konsoli przeglądarki: Wartość wpisaną w pierwszym polu formularza.

    Funkcję handleSubmit podpinamy pod zdarzenie onSubmit formularza (nie onClick przycisku) - tak brzmi arkusz i działa też zatwierdzenie Enterem (R.3.7). Pierwsza linia, event.preventDefault(), zatrzymuje domyślne zachowanie przeglądarki, czyli przeładowanie strony - bez niej wypis w konsoli zniknąłby razem ze stroną. Potem console.log wypisuje imię ze stanu.

    Wpisz w podglądzie imię i kliknij „Zapisz do kursu” - napis pojawi się w panelu konsoli u dołu (na egzaminie: F12, zakładka Console).

    Typowe błędy

    Bez event.preventDefault() strona przeładowuje się po zatwierdzeniu i konsola zostaje wyczyszczona - na zrzucie nie ma wyniku.

    web/src/App.jsx JSX zmienione: 6 linii
    import { useState } from 'react'
    
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
      const [fullName, setFullName] = useState('')
      const [courseNumber, setCourseNumber] = useState('')
    
      function handleSubmit(event) {
        event.preventDefault()
        console.log(fullName)
      }
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form onSubmit={handleSubmit}>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName"
                value={fullName} onChange={event => setFullName(event.target.value)} />
            </div>
            <div className="form-group mb-3">
              <label htmlFor="courseNumber">Numer kursu:</label>
              <input type="number" className="form-control" id="courseNumber"
                value={courseNumber} onChange={event => setCourseNumber(event.target.value)} />
            </div>
            <button type="submit" className="btn btn-primary">Zapisz do kursu</button>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  25. Krok 25: Nazwa kursu albo Nieprawidłowy numer kursu

    Kryteria:

    R.3.9 Po wybraniu przycisku „Zapisz do kursu” w konsoli przeglądarki zostaje wypisane: - wartość wpisana w pierwsze pole formularza - gdy numer wpisany w formularzu przyjmuje wartości 1, 2, 3 - nazwa kursu pod tym indeksem, w przeciwnym wypadku komunikat "Nieprawidłowy numer kursu"

    ok. 3 min
    Polecenie z arkusza

    Nazwę kursu odpowiadającą numerowi wpisanemu w drugie pole formularza, gdy kurs o takim numerze istnieje. W przeciwnym wypadku wyświetla komunikat "Nieprawidłowy numer kursu".

    Kursy numerujemy od 1 (jak na liście), a indeksy tablicy od 0 - stąd courseNumber - 1; odejmowanie zamienia też tekst z pola na liczbę. Kurs istnieje, gdy indeks mieści się w tablicy: od 0 do courses.length - 1. Wtedy wypisujemy jego nazwę, w przeciwnym razie komunikat z arkusza (R.3.9). Granicę bierzemy z courses.length, więc po dopisaniu kursu do tablicy kod dalej działa.

    Sprawdź w podglądzie numery 1, 3, 4 i 0 (obraz 1b: Jan Twardowski, 1 - „Programowanie w C#”).

    web/src/App.jsx JSX zmienione: 6 linii
    import { useState } from 'react'
    
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
      const [fullName, setFullName] = useState('')
      const [courseNumber, setCourseNumber] = useState('')
    
      function handleSubmit(event) {
        event.preventDefault()
        console.log(fullName)
        const index = courseNumber - 1
        if (index >= 0 && index < courses.length) {
          console.log(courses[index])
        } else {
          console.log('Nieprawidłowy numer kursu')
        }
      }
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form onSubmit={handleSubmit}>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName"
                value={fullName} onChange={event => setFullName(event.target.value)} />
            </div>
            <div className="form-group mb-3">
              <label htmlFor="courseNumber">Numer kursu:</label>
              <input type="number" className="form-control" id="courseNumber"
                value={courseNumber} onChange={event => setCourseNumber(event.target.value)} />
            </div>
            <button type="submit" className="btn btn-primary">Zapisz do kursu</button>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  26. Krok 26: Zrzuty web1 - web4 i folder web

    Kryteria: ,

    R.3.8 Aplikacja jest interpretowana bez błędów i uruchamia się w przeglądarce, co udokumentowano zrzutem ekranu

    R.4.8 Zapisano przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji web, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 4 min
    Polecenie z arkusza

    Aplikacja web – dowolna liczba zrzutów nazwanych web1, web2 ... (np. stan początkowy, po wypełnieniu pól, po zatwierdzeniu konsola przeglądarki, gdy w drugim polu formularza jest prawidłowy numer oraz gdy jest nieprawidłowy numer kursu). Kod aplikacji przygotuj do nagrania na płytę. W podfolderze web powinno znaleźć się archiwum całego projektu o nazwie web.zip oraz pliki z kodem źródłowym, które były modyfikowane.

    npm run dev, aplikacja w przeglądarce z otwartą konsolą (F12) i edytorem w tle. Zrzuty całego ekranu w folderze testy: web1 - stan początkowy, web2 - wypełnione pola, web3 - konsola po zatwierdzeniu z prawidłowym numerem, web4 - z nieprawidłowym (R.3.8, R.4.8).

    W folderze web: web.zip z projektem bez folderu node_modules (setki megabajtów; odtwarza go npm install) oraz zmienione pliki App.jsx i main.jsx.

    Podgląd strony bez zmian - taki jak po kroku 25.

  27. Etap 5: Dokumentacja

    Krok 27 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 27: Dokument egzamin

    Kryteria:

    R.4.9 Dokumentacja zawiera: nazwę systemu operacyjnego, nazwy środowisk, nazwy języków programowania, frameworka Angular lub bilbioteki React

    ok. 2 min
    Polecenie z arkusza

    W edytorze tekstu pakietu biurowego utwórz plik z dokumentacją i nazwij go egzamin. Dokument powinien zawierać informacje: Nazwę systemu operacyjnego, na którym pracował zdający, Nazwy środowisk programistycznych, z których zdający korzystał na egzaminie, Nazwy języków programowania / frameworków / bibliotek użytych podczas tworzenia aplikacji. Zrzuty ekranu i dokument umieść w folderze o nazwie testy.

    W Wordzie albo Writerze dokument egzamin w folderze testy: system, środowiska, języki i - ważne w tym arkuszu - nazwa biblioteki React (R.4.9). Plik obok to treść tekstowa dokumentu.

    egzamin.txt Tekst nowy plik
    System operacyjny: Windows 11
    Środowiska programistyczne: Visual Studio 2022 (aplikacja konsolowa), Visual Studio Code (aplikacja web)
    Języki programowania: C# (aplikacja konsolowa), JavaScript (aplikacja web)
    Biblioteki: React (aplikacja web), Bootstrap (style formularza), Vite (narzędzie do budowania projektu)

    Podgląd strony bez zmian - taki jak po kroku 25.

  28. Etap 6: Dobre praktyki (poza kluczem)

    Kroki 28-29 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 28: Konsola: bezpieczne id i imię z samych spacji

    ok. 3 min
    Polecenie z arkusza

    Program powinien być zapisany czytelnie, z zachowaniem zasad czystego formatowania kodu, należy stosować znaczące nazwy pól i metod.

    Trzy poprawki:

    • int.TryParse w pętli - id wpisane jako „siedem” nie przerywa programu wyjątkiem FormatException, tylko prosi ponownie,
    • string.IsNullOrWhiteSpace(name) - imię złożone z samych spacji też jest „brakiem danych”, a null nie wywraca porównania,
    • powitanie składamy napisem interpolowanym $"Cześć {otherName}, ..." - czytelniej niż plusy.

    Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    konsola/Program.cs C# zmienione: 8 linii
    class Osoba
    {
        private int id;
        private string name;
        public static int InstanceCount = 0;
    
        public Osoba()
        {
            id = 0;
            name = "";
            InstanceCount++;
        }
    
        public Osoba(int id, string name)
        {
            this.id = id;
            this.name = name;
            InstanceCount++;
        }
    
        public Osoba(Osoba other)
        {
            id = other.id;
            name = other.name;
            InstanceCount++;
        }
    
        public void Greet(string otherName)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                Console.WriteLine("Brak danych");
            }
            else
            {
                Console.WriteLine($"Cześć {otherName}, mam na imię {name}");
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
    
            Osoba emptyPerson = new Osoba();
    
            Console.Write("Podaj id: ");
            int id;
            while (!int.TryParse(Console.ReadLine(), out id))
            {
                Console.Write("Id musi być liczbą całkowitą. Podaj id: ");
            }
            // int id = int.Parse(Console.ReadLine());
            Console.Write("Podaj imię: ");
            string name = Console.ReadLine();
            Osoba person = new Osoba(id, name);
    
            Osoba copiedPerson = new Osoba(person);
    
            emptyPerson.Greet("Jan");
            person.Greet("Jan");
            copiedPerson.Greet("Jan");
    
            Console.WriteLine("Liczba zarejestrowanych osób to " + Osoba.InstanceCount);
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczba zarejestrowanych osób to 0
    Podaj id: 7
    siedem
    Id musi być liczbą całkowitą. Podaj id: Podaj imię: Anna
    Brak danych
    Cześć Jan, mam na imię Anna
    Cześć Jan, mam na imię Anna
    Liczba zarejestrowanych osób to 3
  29. Krok 29: Web: walidacja formularza w przeglądarce

    ok. 3 min
    Polecenie z arkusza

    Nazwę kursu odpowiadającą numerowi wpisanemu w drugie pole formularza, gdy kurs o takim numerze istnieje. W przeciwnym wypadku wyświetla komunikat "Nieprawidłowy numer kursu".

    Formularz HTML potrafi sam sprawdzić dane, zanim w ogóle wywoła onSubmit:

    • required - pole nie może zostać puste,
    • min="1" i max={courses.length} - numer spoza listy kursów; granica idzie za tablicą. Ułamki pole type="number" odrzuca już samo, bo jego domyślny krok to 1.

    Przy złej wartości przeglądarka nie wysyła formularza, tylko pokazuje przy polu dymek z opisem błędu - użytkownik dowiaduje się o pomyłce od razu, a nie z konsoli. Sprawdzenie zakresu w handleSubmit zostaje: walidacja przeglądarki to wygoda, nie zabezpieczenie (wyłącza ją np. atrybut noValidate). Dodatkowo Number jawnie zamienia tekst z pola na liczbę, a metoda trim obcina spacje wokół imienia.

    Uwaga: przy tej wersji numer 4 nie da już komunikatu „Nieprawidłowy numer kursu” z arkusza - dlatego na egzaminie zostajemy przy wersji wcześniejszej (klucz sprawdza właśnie ten komunikat).

    web/src/App.jsx JSX zmienione: 4 linie
    import { useState } from 'react'
    
    function App() {
      const courses = ['Programowanie w C#', 'Angular dla początkujących', 'Kurs Django']
      const [fullName, setFullName] = useState('')
      const [courseNumber, setCourseNumber] = useState('')
    
      function handleSubmit(event) {
        event.preventDefault()
        console.log(fullName.trim())
        const index = Number(courseNumber) - 1
        if (index >= 0 && index < courses.length) {
          console.log(courses[index])
        } else {
          console.log('Nieprawidłowy numer kursu')
        }
      }
    
      return (
        <div className="container mt-3">
          <h2>Liczba kursów: {courses.length}</h2>
          <ol>
            {courses.map(course => <li key={course}>{course}</li>)}
          </ol>
          <form onSubmit={handleSubmit}>
            <div className="form-group mb-3">
              <label htmlFor="fullName">Imię i nazwisko:</label>
              <input type="text" className="form-control" id="fullName" required
                value={fullName} onChange={event => setFullName(event.target.value)} />
            </div>
            <div className="form-group mb-3">
              <label htmlFor="courseNumber">Numer kursu:</label>
              <input type="number" className="form-control" id="courseNumber" required min="1" max={courses.length}
                value={courseNumber} onChange={event => setCourseNumber(event.target.value)} />
            </div>
            <button type="submit" className="btn btn-primary">Zapisz do kursu</button>
          </form>
        </div>
      )
    }
    
    export default App
    Podgląd strony po tym kroku
  30. Etap 7: Oddanie pracy

    Krok 30 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 30: Nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Nagraj płytę z rezultatami pracy. W folderze z numerem zdającego powinny się znajdować podfoldery: konsola, testy, web. Po nagraniu płyty sprawdź poprawność nagrania.

    Sprawdzamy zawartość przed nagraniem:

    • konsola: konsola.zip, Program.cs, konsola.exe,
    • web: web.zip (bez node_modules), App.jsx, main.jsx,
    • testy: konsola1.png, web1.png - web4.png, egzamin.

    Nagrywamy, sprawdzamy odczyt płyty, opisujemy ją numerem zdającego.

    Podgląd strony bez zmian - taki jak po kroku 29.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony). Treść arkusza i klucza oceniania pochodzi z CKE.