[{"data":1,"prerenderedAt":14746},["ShallowReactive",2],{"navigation_docs":3,"-java-pr2-javafx-fundamentals":3135,"-java-pr2-javafx-fundamentals-surround":14741},[4,1669,1826,2280,2461,2668,2790,2840,2897,2931,3057,3094,3131],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1346,1636,1665],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1352,1356,1360,1364,1368,1372,1376,1380,1384,1388,1392,1396,1400,1404,1408,1412,1416,1420,1424,1428,1432,1436,1440,1444,1448,1452,1456,1460,1464,1468,1472,1476,1480,1484,1488,1492,1496,1500,1504,1508,1512,1516,1520,1524,1528,1532,1536,1540,1544,1548,1552,1556,1560,1564,1568,1572,1576,1580,1584,1588,1592,1596,1600,1604,1608,1612,1616,1620,1624,1628,1632],{"title":1353,"path":1354,"stem":1355},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1357,"path":1358,"stem":1359},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1361,"path":1362,"stem":1363},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1365,"path":1366,"stem":1367},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1369,"path":1370,"stem":1371},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1373,"path":1374,"stem":1375},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1377,"path":1378,"stem":1379},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1381,"path":1382,"stem":1383},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1385,"path":1386,"stem":1387},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1389,"path":1390,"stem":1391},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1393,"path":1394,"stem":1395},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1397,"path":1398,"stem":1399},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1401,"path":1402,"stem":1403},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1405,"path":1406,"stem":1407},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1409,"path":1410,"stem":1411},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1413,"path":1414,"stem":1415},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1417,"path":1418,"stem":1419},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1421,"path":1422,"stem":1423},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1425,"path":1426,"stem":1427},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1429,"path":1430,"stem":1431},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1433,"path":1434,"stem":1435},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1437,"path":1438,"stem":1439},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1441,"path":1442,"stem":1443},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1445,"path":1446,"stem":1447},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1449,"path":1450,"stem":1451},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1453,"path":1454,"stem":1455},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1457,"path":1458,"stem":1459},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1461,"path":1462,"stem":1463},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1465,"path":1466,"stem":1467},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1469,"path":1470,"stem":1471},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1473,"path":1474,"stem":1475},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1477,"path":1478,"stem":1479},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1481,"path":1482,"stem":1483},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1485,"path":1486,"stem":1487},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1489,"path":1490,"stem":1491},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1493,"path":1494,"stem":1495},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1497,"path":1498,"stem":1499},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1501,"path":1502,"stem":1503},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1505,"path":1506,"stem":1507},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1509,"path":1510,"stem":1511},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1513,"path":1514,"stem":1515},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1517,"path":1518,"stem":1519},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1521,"path":1522,"stem":1523},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1525,"path":1526,"stem":1527},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1529,"path":1530,"stem":1531},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1533,"path":1534,"stem":1535},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1537,"path":1538,"stem":1539},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1541,"path":1542,"stem":1543},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1545,"path":1546,"stem":1547},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1549,"path":1550,"stem":1551},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1553,"path":1554,"stem":1555},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1557,"path":1558,"stem":1559},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1561,"path":1562,"stem":1563},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1565,"path":1566,"stem":1567},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1569,"path":1570,"stem":1571},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1573,"path":1574,"stem":1575},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1577,"path":1578,"stem":1579},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1581,"path":1582,"stem":1583},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1585,"path":1586,"stem":1587},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1589,"path":1590,"stem":1591},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1593,"path":1594,"stem":1595},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1597,"path":1598,"stem":1599},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1601,"path":1602,"stem":1603},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1605,"path":1606,"stem":1607},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1609,"path":1610,"stem":1611},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1613,"path":1614,"stem":1615},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1617,"path":1618,"stem":1619},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1621,"path":1622,"stem":1623},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1625,"path":1626,"stem":1627},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1629,"path":1630,"stem":1631},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1633,"path":1634,"stem":1635},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1637,"icon":658,"path":1638,"stem":1639,"children":1640,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1641,1645,1649,1653,1657,1661],{"title":1642,"path":1643,"stem":1644},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1646,"path":1647,"stem":1648},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1650,"path":1651,"stem":1652},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1654,"path":1655,"stem":1656},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1658,"path":1659,"stem":1660},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1662,"path":1663,"stem":1664},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1666,"path":1667,"stem":1668},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1670,"icon":1671,"path":1672,"stem":1673,"children":1674,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1675,1679,1683,1687,1691,1695,1699,1703,1707,1710,1714,1718,1722,1726,1730,1734,1738,1742,1746,1750,1754,1758,1762,1766,1770,1774,1778,1782,1786,1790,1794,1798,1802,1806,1810,1814,1818,1822],{"title":1676,"path":1677,"stem":1678},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1680,"path":1681,"stem":1682},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1684,"path":1685,"stem":1686},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1688,"path":1689,"stem":1690},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1692,"path":1693,"stem":1694},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1696,"path":1697,"stem":1698},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1700,"path":1701,"stem":1702},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1704,"path":1705,"stem":1706},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1708,"stem":1709},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1711,"path":1712,"stem":1713},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1715,"path":1716,"stem":1717},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1719,"path":1720,"stem":1721},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1723,"path":1724,"stem":1725},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1727,"path":1728,"stem":1729},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1731,"path":1732,"stem":1733},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1735,"path":1736,"stem":1737},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1739,"path":1740,"stem":1741},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1743,"path":1744,"stem":1745},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1747,"path":1748,"stem":1749},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1751,"path":1752,"stem":1753},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1755,"path":1756,"stem":1757},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1759,"path":1760,"stem":1761},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1763,"path":1764,"stem":1765},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1767,"path":1768,"stem":1769},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1771,"path":1772,"stem":1773},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1775,"path":1776,"stem":1777},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1779,"path":1780,"stem":1781},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1783,"path":1784,"stem":1785},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1787,"path":1788,"stem":1789},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1791,"path":1792,"stem":1793},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1795,"path":1796,"stem":1797},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1799,"path":1800,"stem":1801},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1803,"path":1804,"stem":1805},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1807,"path":1808,"stem":1809},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1811,"path":1812,"stem":1813},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1815,"path":1816,"stem":1817},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1819,"path":1820,"stem":1821},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1823,"path":1824,"stem":1825},"План навчання: Курс C++ — Продовження (Статті 29–60+)","\u002Fcpp\u002Fcurriculum-plan","02.cpp\u002Fcurriculum-plan",{"title":1827,"icon":1828,"path":1829,"stem":1830,"children":1831,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[1832,1858,1912,1934,2238,2276],{"title":1833,"icon":1834,"path":1835,"stem":1836,"children":1837,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[1838,1842,1846,1850,1854],{"title":1839,"path":1840,"stem":1841},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":1843,"path":1844,"stem":1845},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":1847,"path":1848,"stem":1849},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":1851,"path":1852,"stem":1853},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":1855,"path":1856,"stem":1857},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":1859,"icon":1860,"path":1861,"stem":1862,"children":1863,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908],{"title":1865,"path":1866,"stem":1867},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":1869,"path":1870,"stem":1871},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":1873,"path":1874,"stem":1875},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":1877,"path":1878,"stem":1879},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":1881,"path":1882,"stem":1883},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":1885,"path":1886,"stem":1887},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":1889,"path":1890,"stem":1891},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":1893,"path":1894,"stem":1895},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":1897,"path":1898,"stem":1899},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":1901,"path":1902,"stem":1903},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":1905,"path":1906,"stem":1907},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":1909,"path":1910,"stem":1911},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":1913,"icon":1914,"path":1915,"stem":1916,"children":1917,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[1918,1922,1926,1930],{"title":1919,"path":1920,"stem":1921},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":1923,"path":1924,"stem":1925},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":1927,"path":1928,"stem":1929},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":1931,"path":1932,"stem":1933},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":1935,"icon":1936,"path":1937,"stem":1938,"children":1939},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[1940,1941,1945,1949,1953,1957,2020,2055,2207],{"title":1935,"path":1937,"stem":1938},{"title":1942,"path":1943,"stem":1944},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":1946,"path":1947,"stem":1948},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":1950,"path":1951,"stem":1952},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":1954,"path":1955,"stem":1956},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":1958,"icon":132,"path":1959,"stem":1960,"children":1961},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[1962,1964,1968,1972,1976,1980,1984,1988,1992,1996,2000,2004,2008,2012,2016],{"title":1963,"path":1959,"stem":1960},"TanStack Query: Майстерність Керування Станом Сервера",{"title":1965,"path":1966,"stem":1967},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":1969,"path":1970,"stem":1971},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":1973,"path":1974,"stem":1975},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":1977,"path":1978,"stem":1979},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":1981,"path":1982,"stem":1983},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":1985,"path":1986,"stem":1987},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":1989,"path":1990,"stem":1991},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":1993,"path":1994,"stem":1995},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":1997,"path":1998,"stem":1999},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2001,"path":2002,"stem":2003},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2005,"path":2006,"stem":2007},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2009,"path":2010,"stem":2011},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2013,"path":2014,"stem":2015},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2017,"path":2018,"stem":2019},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2021,"icon":1936,"path":2022,"stem":2023,"children":2024},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2025,2027,2031,2035,2039,2043,2047,2051],{"title":2026,"path":2022,"stem":2023},"React Router: Навігаційна система сучасного вебу",{"title":2028,"path":2029,"stem":2030},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2032,"path":2033,"stem":2034},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2036,"path":2037,"stem":2038},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2040,"path":2041,"stem":2042},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2044,"path":2045,"stem":2046},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2048,"path":2049,"stem":2050},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2052,"path":2053,"stem":2054},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2056,"icon":132,"path":2057,"stem":2058,"children":2059},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2060,2062,2078,2107,2116,2137,2153,2182],{"title":2061,"path":2057,"stem":2058},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2063,"stem":2064,"children":2065,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2066,2070,2074],{"title":2067,"path":2068,"stem":2069},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2071,"path":2072,"stem":2073},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2075,"path":2076,"stem":2077},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2079,"icon":132,"path":2080,"stem":2081,"children":2082,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2083,2087,2091,2095,2099,2103],{"title":2084,"path":2085,"stem":2086},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2088,"path":2089,"stem":2090},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2092,"path":2093,"stem":2094},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2096,"path":2097,"stem":2098},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2100,"path":2101,"stem":2102},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2104,"path":2105,"stem":2106},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2108,"icon":132,"path":2109,"stem":2110,"children":2111,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2112],{"title":2113,"path":2114,"stem":2115},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2117,"icon":132,"path":2118,"stem":2119,"children":2120,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2121,2125,2129,2133],{"title":2122,"path":2123,"stem":2124},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2126,"path":2127,"stem":2128},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2130,"path":2131,"stem":2132},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2134,"path":2135,"stem":2136},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2138,"icon":92,"path":2139,"stem":2140,"children":2141,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2142,2146,2150],{"title":2143,"path":2144,"stem":2145},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2147,"path":2148,"stem":2149},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":1997,"path":2151,"stem":2152},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2154,"icon":132,"path":2155,"stem":2156,"children":2157,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2158,2162,2166,2170,2174,2178],{"title":2159,"path":2160,"stem":2161},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2163,"path":2164,"stem":2165},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2167,"path":2168,"stem":2169},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2171,"path":2172,"stem":2173},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2175,"path":2176,"stem":2177},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2179,"path":2180,"stem":2181},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2183,"icon":132,"path":2184,"stem":2185,"children":2186,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2187,2191,2195,2199,2203],{"title":2188,"path":2189,"stem":2190},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2192,"path":2193,"stem":2194},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2196,"path":2197,"stem":2198},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2200,"path":2201,"stem":2202},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2204,"path":2205,"stem":2206},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2208,"icon":132,"path":2209,"stem":2210,"children":2211},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2212,2214,2218,2222,2226,2230,2234],{"title":2213,"path":2209,"stem":2210},"UI Бібліотеки в React",{"title":2215,"path":2216,"stem":2217},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2219,"path":2220,"stem":2221},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2223,"path":2224,"stem":2225},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2227,"path":2228,"stem":2229},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2231,"path":2232,"stem":2233},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2235,"path":2236,"stem":2237},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2239,"icon":2240,"path":2241,"stem":2242,"children":2243,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2244,2248,2252,2256,2260,2264,2268,2272],{"title":2245,"path":2246,"stem":2247},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2249,"path":2250,"stem":2251},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2253,"path":2254,"stem":2255},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2257,"path":2258,"stem":2259},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2261,"path":2262,"stem":2263},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2265,"path":2266,"stem":2267},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2269,"path":2270,"stem":2271},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2273,"path":2274,"stem":2275},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2277,"path":2278,"stem":2279},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2281,"icon":2282,"path":2283,"stem":2284,"children":2285,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2286,2289,2292,2296,2300,2304,2308],{"title":162,"path":2287,"stem":2288},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2290,"stem":2291},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2293,"path":2294,"stem":2295},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2297,"path":2298,"stem":2299},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2301,"path":2302,"stem":2303},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2305,"path":2306,"stem":2307},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2309,"path":2310,"stem":2311,"children":2312,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2313,2317,2321,2325,2329,2333,2337,2341,2345,2349,2353,2357,2361,2365,2369,2373,2377,2381,2385,2389,2393,2397,2401,2405,2409,2413,2417,2421,2425,2429,2433,2437,2441,2445,2449,2453,2457],{"title":2314,"path":2315,"stem":2316},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2318,"path":2319,"stem":2320},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2322,"path":2323,"stem":2324},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2326,"path":2327,"stem":2328},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2330,"path":2331,"stem":2332},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2334,"path":2335,"stem":2336},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2338,"path":2339,"stem":2340},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2342,"path":2343,"stem":2344},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2346,"path":2347,"stem":2348},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2350,"path":2351,"stem":2352},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2354,"path":2355,"stem":2356},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2358,"path":2359,"stem":2360},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2362,"path":2363,"stem":2364},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2366,"path":2367,"stem":2368},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2370,"path":2371,"stem":2372},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2374,"path":2375,"stem":2376},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2378,"path":2379,"stem":2380},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2382,"path":2383,"stem":2384},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2386,"path":2387,"stem":2388},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2390,"path":2391,"stem":2392},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2394,"path":2395,"stem":2396},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2398,"path":2399,"stem":2400},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2402,"path":2403,"stem":2404},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2406,"path":2407,"stem":2408},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2410,"path":2411,"stem":2412},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2414,"path":2415,"stem":2416},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2418,"path":2419,"stem":2420},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2422,"path":2423,"stem":2424},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2426,"path":2427,"stem":2428},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2430,"path":2431,"stem":2432},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2434,"path":2435,"stem":2436},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2438,"path":2439,"stem":2440},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2442,"path":2443,"stem":2444},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2446,"path":2447,"stem":2448},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2450,"path":2451,"stem":2452},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2454,"path":2455,"stem":2456},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2458,"path":2459,"stem":2460},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2462,"icon":2463,"path":2464,"stem":2465,"children":2466,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2467,2497,2520,2557,2586,2604,2638,2650,2659],{"title":2468,"icon":2469,"path":2470,"stem":2471,"children":2472,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2473,2477,2481,2485,2489,2493],{"title":2474,"path":2475,"stem":2476},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2478,"path":2479,"stem":2480},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2482,"path":2483,"stem":2484},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2486,"path":2487,"stem":2488},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2490,"path":2491,"stem":2492},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2494,"path":2495,"stem":2496},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2498,"icon":2463,"path":2499,"stem":2500,"children":2501,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2502,2506,2512,2516],{"title":2503,"path":2504,"stem":2505},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2507,"path":2508,"stem":2509,"children":2510},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2511],{"title":2507,"path":2508,"stem":2509},{"title":2513,"path":2514,"stem":2515},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2517,"path":2518,"stem":2519},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2521,"icon":2463,"path":2522,"stem":2523,"children":2524,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2525,2529,2533,2537,2541,2545,2549,2553],{"title":2526,"path":2527,"stem":2528},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2530,"path":2531,"stem":2532},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2534,"path":2535,"stem":2536},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2538,"path":2539,"stem":2540},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2542,"path":2543,"stem":2544},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2546,"path":2547,"stem":2548},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2550,"path":2551,"stem":2552},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2554,"path":2555,"stem":2556},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2558,"icon":2463,"path":2559,"stem":2560,"children":2561,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2562,2566,2570,2574,2578,2582],{"title":2563,"path":2564,"stem":2565},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2567,"path":2568,"stem":2569},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2571,"path":2572,"stem":2573},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2575,"path":2576,"stem":2577},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2579,"path":2580,"stem":2581},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2583,"path":2584,"stem":2585},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2587,"icon":2588,"path":2589,"stem":2590,"children":2591,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2592,2596,2600],{"title":2593,"path":2594,"stem":2595},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2597,"path":2598,"stem":2599},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2601,"path":2602,"stem":2603},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2605,"icon":2606,"path":2607,"stem":2608,"children":2609,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2610,2614,2618,2622,2626,2630,2634],{"title":2611,"path":2612,"stem":2613},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2615,"path":2616,"stem":2617},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2619,"path":2620,"stem":2621},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2623,"path":2624,"stem":2625},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2627,"path":2628,"stem":2629},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2631,"path":2632,"stem":2633},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2635,"path":2636,"stem":2637},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2635,"icon":793,"path":2639,"stem":2640,"children":2641,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[2642,2646],{"title":2643,"path":2644,"stem":2645},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":2647,"path":2648,"stem":2649},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":2651,"icon":2652,"path":2653,"stem":2654,"children":2655,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[2656],{"title":2651,"path":2657,"stem":2658},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":2660,"icon":2661,"path":2662,"stem":2663,"children":2664,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[2665],{"title":2660,"path":2666,"stem":2667},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":2669,"icon":2670,"path":2671,"stem":2672,"children":2673,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[2674,2750],{"title":2675,"icon":2676,"path":2677,"stem":2678,"children":2679},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[2680,2682,2686,2690,2694,2698,2702,2706,2710,2714,2718,2722,2726,2730,2734,2738,2742,2746],{"title":2681,"path":2677,"stem":2678},"Docker: від нуля до production",{"title":2683,"path":2684,"stem":2685},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":2687,"path":2688,"stem":2689},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":2691,"path":2692,"stem":2693},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":2695,"path":2696,"stem":2697},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":2699,"path":2700,"stem":2701},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":2703,"path":2704,"stem":2705},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":2707,"path":2708,"stem":2709},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":2711,"path":2712,"stem":2713},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":2715,"path":2716,"stem":2717},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":2719,"path":2720,"stem":2721},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":2723,"path":2724,"stem":2725},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":2727,"path":2728,"stem":2729},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":2731,"path":2732,"stem":2733},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":2735,"path":2736,"stem":2737},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":2739,"path":2740,"stem":2741},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":2743,"path":2744,"stem":2745},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":2747,"path":2748,"stem":2749},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":2751,"icon":2752,"path":2753,"stem":2754,"children":2755},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[2756,2758,2762,2766,2770,2774,2778,2782,2786],{"title":2757,"path":2753,"stem":2754},"Kubernetes: від розробки до production",{"title":2759,"path":2760,"stem":2761},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":2763,"path":2764,"stem":2765},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":2767,"path":2768,"stem":2769},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":2771,"path":2772,"stem":2773},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":2775,"path":2776,"stem":2777},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":2779,"path":2780,"stem":2781},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":2783,"path":2784,"stem":2785},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":2787,"path":2788,"stem":2789},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":2791,"icon":2792,"path":2793,"stem":2794,"children":2795,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[2796,2800,2804,2808,2812,2816,2820,2824,2828,2832,2836],{"title":2797,"path":2798,"stem":2799},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":2801,"path":2802,"stem":2803},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":2805,"path":2806,"stem":2807},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":2809,"path":2810,"stem":2811},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":2813,"path":2814,"stem":2815},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":2817,"path":2818,"stem":2819},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":2821,"path":2822,"stem":2823},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":2825,"path":2826,"stem":2827},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":2829,"path":2830,"stem":2831},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":2833,"path":2834,"stem":2835},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":2837,"path":2838,"stem":2839},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":2841,"icon":943,"path":2842,"stem":2843,"children":2844,"page":59},"DDD","\u002Fddd","10.ddd",[2845,2849,2853,2857,2861,2865,2869,2873,2877,2881,2885,2889,2893],{"title":2846,"path":2847,"stem":2848},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":2850,"path":2851,"stem":2852},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":2854,"path":2855,"stem":2856},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":2858,"path":2859,"stem":2860},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":2862,"path":2863,"stem":2864},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":2866,"path":2867,"stem":2868},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":2870,"path":2871,"stem":2872},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":2874,"path":2875,"stem":2876},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":2878,"path":2879,"stem":2880},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":2882,"path":2883,"stem":2884},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":2886,"path":2887,"stem":2888},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":2890,"path":2891,"stem":2892},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":2894,"path":2895,"stem":2896},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":2898,"icon":2899,"path":2900,"stem":2901,"children":2902,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[2903,2907,2911,2915,2919,2923,2927],{"title":2904,"path":2905,"stem":2906},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":2908,"path":2909,"stem":2910},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":2912,"path":2913,"stem":2914},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":2916,"path":2917,"stem":2918},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":2920,"path":2921,"stem":2922},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":2924,"path":2925,"stem":2926},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":2928,"path":2929,"stem":2930},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":2932,"icon":2933,"path":2934,"stem":2935,"children":2936,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[2937,2941,2945,2949,2953,2957,2961,2965,2969,2973,2977,2981,2985,2989,2993,2997,3001,3005,3009,3013,3017,3021,3025,3029,3033,3037,3041,3045,3049,3053],{"title":2938,"path":2939,"stem":2940},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":2942,"path":2943,"stem":2944},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":2946,"path":2947,"stem":2948},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":2950,"path":2951,"stem":2952},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":2954,"path":2955,"stem":2956},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":2958,"path":2959,"stem":2960},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":2962,"path":2963,"stem":2964},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":2966,"path":2967,"stem":2968},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":2970,"path":2971,"stem":2972},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":2974,"path":2975,"stem":2976},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":2978,"path":2979,"stem":2980},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":2982,"path":2983,"stem":2984},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":2986,"path":2987,"stem":2988},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":2990,"path":2991,"stem":2992},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":2994,"path":2995,"stem":2996},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":2998,"path":2999,"stem":3000},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3002,"path":3003,"stem":3004},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3006,"path":3007,"stem":3008},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3010,"path":3011,"stem":3012},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3014,"path":3015,"stem":3016},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3018,"path":3019,"stem":3020},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3022,"path":3023,"stem":3024},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3026,"path":3027,"stem":3028},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3030,"path":3031,"stem":3032},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3034,"path":3035,"stem":3036},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3038,"path":3039,"stem":3040},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3042,"path":3043,"stem":3044},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3046,"path":3047,"stem":3048},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3050,"path":3051,"stem":3052},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3054,"path":3055,"stem":3056},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3058,"path":3059,"stem":3060,"children":3061,"page":59},"AWS","\u002Faws","13.aws",[3062,3066,3070,3074,3078,3082,3086,3090],{"title":3063,"path":3064,"stem":3065},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3067,"path":3068,"stem":3069},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3071,"path":3072,"stem":3073},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3075,"path":3076,"stem":3077},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3079,"path":3080,"stem":3081},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3083,"path":3084,"stem":3085},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3087,"path":3088,"stem":3089},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3091,"path":3092,"stem":3093},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3095,"path":3096,"stem":3097,"children":3098,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[3099,3103,3107,3111,3115,3119,3123,3127],{"title":3100,"path":3101,"stem":3102},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":3104,"path":3105,"stem":3106},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":3108,"path":3109,"stem":3110},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":3112,"path":3113,"stem":3114},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":3116,"path":3117,"stem":3118},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":3120,"path":3121,"stem":3122},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":3124,"path":3125,"stem":3126},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":3128,"path":3129,"stem":3130},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":3132,"path":3133,"stem":3134},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":3136,"title":2414,"body":3137,"description":14735,"extension":14736,"links":14737,"meta":14738,"navigation":4836,"path":2415,"seo":14739,"stem":2416,"__hash__":14740},"docs\u002F04.java\u002Fpr2\u002F25.javafx-fundamentals.md",{"type":3138,"value":3139,"toc":14674},"minimark",[3140,3144,3149,3167,3185,3188,3205,3210,3217,3227,3237,3243,3249,3259,3312,3316,3319,3329,3339,3352,3361,3367,3374,3377,3381,3388,3392,3402,3405,3439,3458,3490,3514,3623,3627,3640,3659,3684,3702,3710,3727,3730,3738,3898,3902,3919,3939,4061,4075,4246,4256,4343,4362,4379,4491,4497,4524,4634,4636,4640,4643,4647,4658,4984,4987,5082,5086,5089,5094,5157,5161,5211,5242,5292,5296,5306,5744,5748,5759,5778,5795,5807,5832,5844,5856,5878,5896,5921,5938,6038,6042,6049,6061,6151,6153,6157,6163,6167,6170,6197,6208,6212,6229,6234,6281,6284,6450,6453,6752,6756,6781,6784,6816,7115,7120,7139,7150,7154,7163,7465,7470,7475,7504,7507,7585,7589,7598,7741,7746,7755,7759,7881,7883,7887,7894,7897,7901,7906,7971,7976,8017,8020,8117,8121,8126,8166,8174,8178,8187,8285,8290,8330,8336,8434,8438,8444,8553,8558,8746,8750,8760,9013,9017,9023,9152,9157,9350,9354,9359,9641,9661,9667,9743,9755,9759,9765,9837,9841,9846,9909,9911,9915,9922,9926,9940,9945,10019,10024,10065,10068,10102,10106,10109,10114,10144,10149,10169,10174,10188,10191,10293,10297,10304,10309,10554,10559,10594,10597,10607,10609,10613,10627,10647,10661,10665,10671,11138,11143,11199,11203,11208,11568,11576,11596,11607,11611,11617,11841,11846,11852,11863,11867,11948,11962,11964,11968,11971,11975,11995,11999,12537,12541,13458,13462,13465,13842,13846,14021,14026,14044,14046,14050,14053,14057,14062,14065,14278,14290,14295,14298,14315,14318,14323,14329,14333,14338,14346,14355,14360,14363,14378,14383,14389,14402,14406,14411,14416,14439,14442,14447,14450,14463,14468,14471,14497,14502,14513,14515,14519,14522,14528,14544,14561,14580,14590,14596,14605,14618,14621,14624,14670],[3141,3142,2414],"h1",{"id":3143},"javafx-основи-побудови-графічних-інтерфейсів",[3145,3146,3148],"h2",{"id":3147},"вступ-від-консолі-до-вікон","Вступ: Від консолі до вікон",[3150,3151,3152,3153,3157,3158,3161,3162,3166],"p",{},"Уявіть, що ви щойно завершили побудову репозиторіїв для платформи аудіокниг. Ви реалізували ",[3154,3155,3156],"code",{},"AudiobookRepository",", налаштували ",[3154,3159,3160],{},"Unit of Work",", інтегрували Google Guice для впровадження залежностей. Ваш код працює бездоганно: дані зберігаються у базі, транзакції виконуються атомарно, тести проходять зелені. Але є одна проблема — ",[3163,3164,3165],"strong",{},"користувач не бачить жодного рядка вашого коду",".",[3150,3168,3169,3170,3173,3174,3173,3177,3180,3181,3184],{},"Для нього ваш додаток — це не елегантні патерни проектування чи ретельно спроектована архітектура. Це ",[3163,3171,3172],{},"вікно з кнопками",", ",[3163,3175,3176],{},"список аудіокниг",[3163,3178,3179],{},"форма для додавання нового запису",". Це те, що він бачить на екрані, те, з чим він взаємодіє. І саме тут на сцену виходить ",[3163,3182,3183],{},"JavaFX"," — сучасний фреймворк для побудови графічних інтерфейсів користувача (GUI) у Java-додатках.",[3150,3186,3187],{},"Ця стаття — перший крок у світ desktop-розробки. Ми дізнаємося, як перетворити наші репозиторії та сервіси на живий, інтерактивний додаток. Як з'єднати два світи: світ бізнес-логіки (який ми вже побудували) та світ користувацького інтерфейсу (який ми зараз створимо).",[3189,3190,3191,3193,3194,3197,3198],"note",{},[3163,3192,3183],{}," — це платформа для створення багатофункціональних клієнтських додатків на Java. Вона була представлена Sun Microsystems у 2008 році як заміна застарілого Swing. З виходом Java 11 (2018) JavaFX стала окремим проєктом під назвою ",[3163,3195,3196],{},"OpenJFX",", що розвивається незалежно від JDK. ",[3199,3200,3204],"a",{"href":3201,"rel":3202},"https:\u002F\u002Fopenjfx.io\u002F",[3203],"nofollow","Офіційна документація OpenJFX",[3206,3207,3209],"h3",{"id":3208},"чому-javafx-а-не-swing","Чому JavaFX, а не Swing?",[3150,3211,3212,3213,3216],{},"Якщо ви вивчали Java раніше, можливо, чули про ",[3163,3214,3215],{},"Swing"," — попередній стандарт для GUI у Java. Чому ж індустрія перейшла на JavaFX? Відповідь криється у кількох ключових перевагах:",[3150,3218,3219,3222,3223,3226],{},[3163,3220,3221],{},"Сучасна архітектура."," JavaFX побудована на концепції ",[3163,3224,3225],{},"Scene Graph"," — ієрархічного дерева візуальних елементів, подібного до DOM у веб-браузерах. Це робить структуру UI інтуїтивною та легкою для розуміння.",[3150,3228,3229,3232,3233,3236],{},[3163,3230,3231],{},"CSS-стилізація."," На відміну від Swing, де стилізація компонентів вимагала написання Java-коду, JavaFX підтримує ",[3163,3234,3235],{},"каскадні таблиці стилів"," (CSS) — той самий підхід, що використовується у веб-розробці. Ви можете змінити зовнішній вигляд всього додатку, не торкаючись жодного рядка Java-коду.",[3150,3238,3239,3242],{},[3163,3240,3241],{},"FXML — декларативний опис інтерфейсу."," JavaFX дозволяє описувати структуру UI у XML-файлах (FXML), подібно до HTML у веб-додатках. Це розділяє логіку та представлення — фундаментальний принцип, який ми згодом використаємо у патерні MVVM.",[3150,3244,3245,3248],{},[3163,3246,3247],{},"Інтеграція з сучасною Java."," JavaFX повністю підтримує модульну систему Java (Project Jigsaw), лямбда-вирази, Stream API та інші можливості сучасної Java. Swing, натомість, залишився у минулому.",[3150,3250,3251,3254,3255,3258],{},[3163,3252,3253],{},"Апаратне прискорення."," JavaFX використовує графічний конвеєр ",[3163,3256,3257],{},"Prism",", що забезпечує апаратне прискорення через OpenGL, DirectX або Metal (залежно від платформи). Це робить анімації плавними, а інтерфейс — відгукливим.",[3260,3261,3262,3293],"card-group",{},[3263,3264,3267,3273,3287],"card",{"icon":3265,"title":3266},"i-heroicons-archive-box","Swing (1997-2018)",[3150,3268,3269,3272],{},[3163,3270,3271],{},"Підхід:"," Імперативний — кожен компонент створюється та налаштовується через Java-код.",[3150,3274,3275,3278,3279,3282,3283,3286],{},[3163,3276,3277],{},"Стилізація:"," Через ",[3154,3280,3281],{},"UIManager"," та ",[3154,3284,3285],{},"Look and Feel"," — складно та обмежено.",[3150,3288,3289,3292],{},[3163,3290,3291],{},"Статус:"," Legacy — підтримується, але не розвивається.",[3263,3294,3297,3302,3307],{"icon":3295,"title":3296},"i-heroicons-sparkles","JavaFX (2008-сьогодні)",[3150,3298,3299,3301],{},[3163,3300,3271],{}," Декларативний (FXML) + імперативний (Java) — гнучкість вибору.",[3150,3303,3304,3306],{},[3163,3305,3277],{}," CSS — знайомий веб-розробникам синтаксис, потужні можливості.",[3150,3308,3309,3311],{},[3163,3310,3291],{}," Активний розвиток — OpenJFX, регулярні оновлення.",[3206,3313,3315],{"id":3314},"історія-та-еволюція-javafx","Історія та еволюція JavaFX",[3150,3317,3318],{},"Розуміння історії технології допомагає усвідомити, чому вона виглядає саме так, а не інакше. JavaFX пройшла довгий шлях від експериментального проєкту до промислового стандарту.",[3150,3320,3321,3324,3325,3328],{},[3163,3322,3323],{},"2008 рік — JavaFX 1.0."," Sun Microsystems представила JavaFX як конкурента Adobe Flash та Microsoft Silverlight. Перша версія використовувала власну мову програмування ",[3163,3326,3327],{},"JavaFX Script"," — спробу зробити розробку UI більш декларативною. Однак ця мова не прижилася: розробники хотіли писати на Java, а не вивчати новий синтаксис.",[3150,3330,3331,3334,3335,3338],{},[3163,3332,3333],{},"2011 рік — JavaFX 2.0."," Революційний реліз. JavaFX Script була повністю видалена, натомість з'явилося ",[3163,3336,3337],{},"Java API"," для побудови інтерфейсів. Саме у цій версії з'явилися FXML, CSS-стилізація та Scene Graph у сучасному вигляді. JavaFX 2.0 стала частиною JDK 7, що означало офіційне визнання від Oracle.",[3150,3340,3341,3344,3345,3173,3348,3351],{},[3163,3342,3343],{},"2014 рік — JavaFX 8."," Інтеграція з Java 8 принесла підтримку лямбда-виразів та Stream API, що зробило код JavaFX значно лаконічнішим. З'явилися нові компоненти: ",[3154,3346,3347],{},"DatePicker",[3154,3349,3350],{},"TreeTableView",", вбудована підтримка 3D-графіки. Саме ця версія стала \"золотим стандартом\" для багатьох enterprise-додатків.",[3150,3353,3354,3357,3358,3360],{},[3163,3355,3356],{},"2018 рік — JavaFX 11 та народження OpenJFX."," Oracle прийняла рішення виключити JavaFX з JDK, перетворивши її на окремий проєкт під егідою OpenJDK. Це було стратегічне рішення: JavaFX отримала незалежний цикл розробки, не прив'язаний до релізів Java. Проєкт перейменували на ",[3163,3359,3196],{},", і з того часу він розвивається як окремий модуль, що підключається до проєкту через Maven або Gradle.",[3150,3362,3363,3366],{},[3163,3364,3365],{},"2020-2026 роки — Сучасність."," OpenJFX регулярно оновлюється, підтримуючи нові версії Java (до Java 21 включно на момент написання цієї статті). З'явилася підтримка нових платформ (включно з ARM-архітектурою для Apple Silicon), покращена продуктивність рендерингу, нові компоненти та API. JavaFX залишається одним з найпопулярніших фреймворків для desktop-розробки на Java, особливо у корпоративному сегменті.",[3368,3369,3370,3373],"tip",{},[3163,3371,3372],{},"Для нашого курсу"," ми використовуватимемо JavaFX 21 — останню LTS-версію на момент 2026 року. Всі приклади коду сумісні з Java 17+ та OpenJFX 21+.",[3375,3376],"hr",{},[3145,3378,3380],{"id":3379},"архітектура-javafx-scene-graph-та-application-lifecycle","Архітектура JavaFX: Scene Graph та Application Lifecycle",[3150,3382,3383,3384,3387],{},"Перш ніж писати перший рядок коду, необхідно зрозуміти, ",[3163,3385,3386],{},"як влаштована JavaFX зсередини",". Це не просто набір класів для малювання кнопок — це цілісна архітектура з чіткою ієрархією компонентів та життєвим циклом додатку.",[3206,3389,3391],{"id":3390},"scene-graph-ієрархічне-дерево-візуальних-елементів","Scene Graph: Ієрархічне дерево візуальних елементів",[3150,3393,3394,3395,3397,3398,3401],{},"Центральна концепція JavaFX — ",[3163,3396,3225],{}," (граф сцени). Це ієрархічна структура даних, що представляє всі візуальні елементи вашого додатку у вигляді дерева. Кожен елемент у цьому дереві називається ",[3163,3399,3400],{},"вузлом"," (Node).",[3150,3403,3404],{},"Чому саме дерево? Тому що це природний спосіб описати вкладеність UI-компонентів. Вікно містить панель, панель містить кнопки та текстові поля, текстове поле містить текст. Кожен рівень вкладеності — це рівень у дереві.",[3150,3406,3407,3413,3414,3416,3417,3173,3420,3423,3424,3173,3427,3430,3431,3434,3435,3438],{},[3163,3408,3409,3410,3166],{},"Базовий клас — ",[3154,3411,3412],{},"Node"," Це абстрактний клас, від якого успадковуються всі візуальні елементи JavaFX. ",[3154,3415,3412],{}," визначає загальні властивості: позицію (",[3154,3418,3419],{},"x",[3154,3421,3422],{},"y","), розмір (",[3154,3425,3426],{},"width",[3154,3428,3429],{},"height","), видимість (",[3154,3432,3433],{},"visible","), прозорість (",[3154,3436,3437],{},"opacity","), трансформації (обертання, масштабування), обробники подій.",[3150,3440,3441,3447,3448,3450,3451,3453,3454,3457],{},[3163,3442,3443,3444,3166],{},"Контейнери — ",[3154,3445,3446],{},"Parent"," Це підклас ",[3154,3449,3412],{},", що може містити інші вузли. Саме ",[3154,3452,3446],{}," формує структуру дерева: він має список дочірніх вузлів (",[3154,3455,3456],{},"children","), які, у свою чергу, можуть бути контейнерами або листовими вузлами.",[3150,3459,3460,3466,3467,3469,3470,3472,3473,3173,3476,3173,3479,3173,3482,3485,3486,3489],{},[3163,3461,3462,3463,3166],{},"Layout-контейнери — ",[3154,3464,3465],{},"Region"," Підклас ",[3154,3468,3446],{},", що додає можливості автоматичного розташування дочірніх елементів. Саме від ",[3154,3471,3465],{}," успадковуються всі layout-контейнери: ",[3154,3474,3475],{},"VBox",[3154,3477,3478],{},"HBox",[3154,3480,3481],{},"BorderPane",[3154,3483,3484],{},"GridPane",". Вони не просто містять елементи — вони ",[3163,3487,3488],{},"організовують їх у просторі"," за певними правилами.",[3150,3491,3492,3466,3498,3500,3501,3173,3504,3173,3507,3173,3510,3513],{},[3163,3493,3494,3495,3166],{},"Контроли — ",[3154,3496,3497],{},"Control",[3154,3499,3465],{},", що представляє інтерактивні елементи: ",[3154,3502,3503],{},"Button",[3154,3505,3506],{},"TextField",[3154,3508,3509],{},"Label",[3154,3511,3512],{},"TableView",". Контроли мають не лише візуальне представлення, а й поведінку: кнопка реагує на клік, текстове поле приймає введення.",[3515,3516,3517],"mermaid",{},[3518,3519,3523],"pre",{"className":3520,"code":3521,"language":3515,"meta":3522,"style":3522},"language-mermaid shiki shiki-themes light-plus dark-plus dark-plus","graph TD\n    A[Node\u003Cbr\u002F>Базовий клас для всіх візуальних елементів] --> B[Parent\u003Cbr\u002F>Може містити дочірні вузли]\n    B --> C[Region\u003Cbr\u002F>Автоматичне розташування дочірніх елементів]\n    C --> D[Control\u003Cbr\u002F>Інтерактивні компоненти]\n    C --> E[Pane\u003Cbr\u002F>Базовий layout-контейнер]\n    E --> F[VBox, HBox\u003Cbr\u002F>Вертикальне\u002Fгоризонтальне розташування]\n    E --> G[BorderPane\u003Cbr\u002F>5 зон: top, bottom, left, right, center]\n    E --> H[GridPane\u003Cbr\u002F>Табличне розташування]\n    D --> I[Button, Label, TextField\u003Cbr\u002F>Конкретні контроли]\n    A --> J[Shape\u003Cbr\u002F>Геометричні фігури]\n    J --> K[Rectangle, Circle, Line\u003Cbr\u002F>Примітиви]\n    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n    style B fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n    style C fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n    style D fill:#10b981,stroke:#059669,color:#ffffff\n    style E fill:#f59e0b,stroke:#d97706,color:#ffffff\n","",[3154,3524,3525,3533,3539,3545,3551,3557,3563,3569,3575,3581,3587,3593,3599,3605,3611,3617],{"__ignoreMap":3522},[3526,3527,3530],"span",{"class":3528,"line":3529},"line",1,[3526,3531,3532],{},"graph TD\n",[3526,3534,3536],{"class":3528,"line":3535},2,[3526,3537,3538],{},"    A[Node\u003Cbr\u002F>Базовий клас для всіх візуальних елементів] --> B[Parent\u003Cbr\u002F>Може містити дочірні вузли]\n",[3526,3540,3542],{"class":3528,"line":3541},3,[3526,3543,3544],{},"    B --> C[Region\u003Cbr\u002F>Автоматичне розташування дочірніх елементів]\n",[3526,3546,3548],{"class":3528,"line":3547},4,[3526,3549,3550],{},"    C --> D[Control\u003Cbr\u002F>Інтерактивні компоненти]\n",[3526,3552,3554],{"class":3528,"line":3553},5,[3526,3555,3556],{},"    C --> E[Pane\u003Cbr\u002F>Базовий layout-контейнер]\n",[3526,3558,3560],{"class":3528,"line":3559},6,[3526,3561,3562],{},"    E --> F[VBox, HBox\u003Cbr\u002F>Вертикальне\u002Fгоризонтальне розташування]\n",[3526,3564,3566],{"class":3528,"line":3565},7,[3526,3567,3568],{},"    E --> G[BorderPane\u003Cbr\u002F>5 зон: top, bottom, left, right, center]\n",[3526,3570,3572],{"class":3528,"line":3571},8,[3526,3573,3574],{},"    E --> H[GridPane\u003Cbr\u002F>Табличне розташування]\n",[3526,3576,3578],{"class":3528,"line":3577},9,[3526,3579,3580],{},"    D --> I[Button, Label, TextField\u003Cbr\u002F>Конкретні контроли]\n",[3526,3582,3584],{"class":3528,"line":3583},10,[3526,3585,3586],{},"    A --> J[Shape\u003Cbr\u002F>Геометричні фігури]\n",[3526,3588,3590],{"class":3528,"line":3589},11,[3526,3591,3592],{},"    J --> K[Rectangle, Circle, Line\u003Cbr\u002F>Примітиви]\n",[3526,3594,3596],{"class":3528,"line":3595},12,[3526,3597,3598],{},"    style A fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n",[3526,3600,3602],{"class":3528,"line":3601},13,[3526,3603,3604],{},"    style B fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n",[3526,3606,3608],{"class":3528,"line":3607},14,[3526,3609,3610],{},"    style C fill:#3b82f6,stroke:#1d4ed8,color:#ffffff\n",[3526,3612,3614],{"class":3528,"line":3613},15,[3526,3615,3616],{},"    style D fill:#10b981,stroke:#059669,color:#ffffff\n",[3526,3618,3620],{"class":3528,"line":3619},16,[3526,3621,3622],{},"    style E fill:#f59e0b,stroke:#d97706,color:#ffffff\n",[3206,3624,3626],{"id":3625},"stage-scene-та-root-node-тріада-javafx","Stage, Scene та Root Node: Тріада JavaFX",[3150,3628,3629,3630,3173,3633,3282,3636,3639],{},"Щоб відобразити Scene Graph на екрані, JavaFX використовує три ключові компоненти: ",[3163,3631,3632],{},"Stage",[3163,3634,3635],{},"Scene",[3163,3637,3638],{},"Root Node",". Розуміння їхніх ролей критично важливе для побудови додатків.",[3150,3641,3642,3645,3646,3648,3649,3651,3652,3655,3656,3166],{},[3163,3643,3644],{},"Stage (Сцена у театральному сенсі) — це вікно."," У JavaFX ",[3154,3647,3632],{}," представляє вікно операційної системи: те, що користувач бачить на робочому столі, що можна перетягувати, згортати, закривати. Кожен JavaFX-додаток має принаймні один ",[3154,3650,3632],{}," — ",[3163,3653,3654],{},"primary stage",", що створюється автоматично при запуску додатку та передається у метод ",[3154,3657,3658],{},"start()",[3150,3660,3661,3663,3664,3667,3668,3173,3670,3672,3673,3676,3677,3680,3681,3683],{},[3154,3662,3632],{}," має властивості вікна: заголовок (",[3154,3665,3666],{},"title","), розміри (",[3154,3669,3426],{},[3154,3671,3429],{},"), можливість зміни розміру (",[3154,3674,3675],{},"resizable","), модальність (",[3154,3678,3679],{},"modality","). Ви можете створювати додаткові ",[3154,3682,3632],{}," для діалогових вікон або окремих екранів додатку.",[3150,3685,3686,3689,3690,3692,3693,3695,3696,3698,3699,3701],{},[3163,3687,3688],{},"Scene (Сцена у JavaFX-сенсі) — це контейнер для контенту."," ",[3154,3691,3635],{}," — це об'єкт, що містить весь візуальний контент, який відображається у вікні. Один ",[3154,3694,3632],{}," може показувати лише одну ",[3154,3697,3635],{}," у конкретний момент часу, але ви можете змінювати ",[3154,3700,3635],{}," динамічно (наприклад, при навігації між екранами).",[3150,3703,3704,3706,3707,3709],{},[3154,3705,3635],{}," визначає розміри контенту, фоновий колір, таблиці стилів CSS. Саме до ",[3154,3708,3635],{}," підключаються обробники глобальних подій (наприклад, натискання клавіш).",[3150,3711,3712,3715,3716,3718,3719,3721,3722,3173,3724,3726],{},[3163,3713,3714],{},"Root Node — це корінь Scene Graph."," Кожна ",[3154,3717,3635],{}," має один кореневий вузол — ",[3154,3720,3446],{},", від якого починається дерево всіх візуальних елементів. Зазвичай це layout-контейнер (",[3154,3723,3475],{},[3154,3725,3481],{},"), що організовує структуру вашого інтерфейсу.",[3150,3728,3729],{},"Ось як ці три компоненти співвідносяться:",[3518,3731,3736],{"className":3732,"code":3734,"language":3735},[3733],"language-text","Stage (вікно ОС)\n  └── Scene (контейнер контенту)\n        └── Root Node (корінь дерева)\n              ├── Child Node 1 (наприклад, VBox)\n              │     ├── Button\n              │     └── Label\n              └── Child Node 2 (наприклад, TableView)\n","text",[3154,3737,3734],{"__ignoreMap":3522},[3739,3740,3747,3747,3748,3747,3761,3747,3747,3772,3747,3787,3747,3747,3796,3747,3808,3747,3747,3815,3747,3825,3747,3747,3832,3747,3839,3747,3845,3747,3853,3747,3857,3747,3747,3861,3747,3885,3747,3892],"svg",{"viewBox":3741,"className":3742,"xmlns":3746},"0 0 800 400",[3743,3744,3745],"w-full","h-auto","block","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[3749,3750],"rect",{"x":3751,"y":3751,"width":3752,"height":3753,"rx":3754,"className":3755,"strokeWidth":3760},"50","700","320","8",[3756,3757,3758,3759],"fill-slate-100","dark:fill-slate-800","stroke-slate-300","dark:stroke-slate-600","3",[3735,3762,3771],{"x":3763,"y":3764,"textAnchor":3765,"className":3766},"400","35","middle",[3767,3768,3769,3770],"text-sm","font-bold","fill-slate-600","dark:fill-slate-300","Stage (вікно операційної системи)",[3749,3773],{"x":3774,"y":3775,"width":3776,"height":3777,"rx":3778,"className":3779,"strokeWidth":3784,"strokeDashArray":3785},"80","90","640","260","6",[3780,3781,3782,3783],"fill-blue-50","dark:fill-blue-900\u002F30","stroke-blue-400","dark:stroke-blue-500","2",[3786,3786],"5",[3735,3788,3795],{"x":3763,"y":3789,"textAnchor":3765,"className":3790},"115",[3791,3792,3793,3794],"text-xs","font-semibold","fill-blue-600","dark:fill-blue-400","Scene (контейнер контенту)",[3749,3797],{"x":3798,"y":3799,"width":3800,"height":3801,"rx":3802,"className":3803,"strokeWidth":3784},"120","140","560","190","4",[3804,3805,3806,3807],"fill-green-50","dark:fill-green-900\u002F30","stroke-green-500","dark:stroke-green-400",[3735,3809,3814],{"x":3763,"y":3810,"textAnchor":3765,"className":3811},"165",[3791,3792,3812,3813],"fill-green-700","dark:fill-green-300","Root Node (BorderPane)",[3749,3816],{"x":3799,"y":3817,"width":3818,"height":3819,"rx":3760,"className":3820,"strokeWidth":3824},"180","520","40",[3821,3822,3823],"fill-amber-100","dark:fill-amber-900\u002F40","stroke-amber-400","1.5",[3735,3826,3831],{"x":3763,"y":3827,"textAnchor":3765,"className":3828},"205",[3791,3829,3830],"fill-amber-800","dark:fill-amber-200","HBox: Button \"Add\" | Button \"Delete\"",[3749,3833],{"x":3799,"y":3834,"width":3818,"height":3774,"rx":3760,"className":3835,"strokeWidth":3824},"235",[3836,3837,3838],"fill-purple-100","dark:fill-purple-900\u002F40","stroke-purple-400",[3735,3840,3844],{"x":3763,"y":3777,"textAnchor":3765,"className":3841},[3791,3842,3843],"fill-purple-800","dark:fill-purple-200","TableView (список аудіокниг)",[3528,3846],{"x1":3847,"y1":3848,"x2":3776,"y2":3848,"className":3849,"strokeWidth":3852},"160","270",[3850,3851],"stroke-purple-300","dark:stroke-purple-600","1",[3528,3854],{"x1":3847,"y1":3855,"x2":3776,"y2":3855,"className":3856,"strokeWidth":3852},"285",[3850,3851],[3528,3858],{"x1":3847,"y1":3859,"x2":3776,"y2":3859,"className":3860,"strokeWidth":3852},"300",[3850,3851],[3862,3863,3864,3865,3864,3878,3747],"defs",{},"\n    ",[3866,3867,3871,3872,3864],"marker",{"id":3868,"markerWidth":3754,"markerHeight":3754,"refX":3869,"refY":3760,"orient":3870},"arrowblue","7","auto","\n      ",[3873,3874],"path",{"d":3875,"className":3876},"M0,0 L0,6 L8,3 z",[3877],"fill-blue-500",[3866,3879,3871,3881,3864],{"id":3880,"markerWidth":3754,"markerHeight":3754,"refX":3869,"refY":3760,"orient":3870},"arrowgreen",[3873,3882],{"d":3875,"className":3883},[3884],"fill-green-500",[3528,3886],{"x1":3763,"y1":3887,"x2":3763,"y2":3888,"className":3889,"strokeWidth":3784,"markerEnd":3891},"75","88",[3890],"stroke-blue-500","url(#arrowblue)",[3528,3893],{"x1":3763,"y1":3894,"x2":3763,"y2":3895,"className":3896,"strokeWidth":3784,"markerEnd":3897},"125","138",[3806],"url(#arrowgreen)",[3206,3899,3901],{"id":3900},"життєвий-цикл-application-init-start-stop","Життєвий цикл Application: init → start → stop",[3150,3903,3904,3905,3908,3909,3173,3912,3282,3915,3918],{},"Кожен JavaFX-додаток успадковується від класу ",[3154,3906,3907],{},"javafx.application.Application"," та проходить через три фази життєвого циклу: ",[3163,3910,3911],{},"ініціалізація",[3163,3913,3914],{},"запуск",[3163,3916,3917],{},"завершення",". Розуміння цих фаз допомагає правильно організувати код та уникнути типових помилок.",[3150,3920,3921,3928,3929,3932,3933,3689,3935,3938],{},[3163,3922,3923,3924,3927],{},"Фаза 1: ",[3154,3925,3926],{},"init()"," — підготовка до запуску."," Цей метод викликається ",[3163,3930,3931],{},"до"," створення JavaFX Application Thread — у фоновому потоці. Тут ви можете виконувати важкі операції ініціалізації: завантаження конфігурації, підключення до бази даних, створення Guice Injector. Важливо: у ",[3154,3934,3926],{},[3163,3936,3937],{},"не можна"," створювати JavaFX-компоненти (Stage, Scene, Node) — вони вимагають JavaFX Application Thread.",[3518,3940,3944],{"className":3941,"code":3942,"language":3943,"meta":3522,"style":3522},"language-java shiki shiki-themes light-plus dark-plus dark-plus","@Override\npublic void init() throws Exception {\n    \u002F\u002F Виконується у фоновому потоці\n    System.out.println(\"Initializing application...\");\n    \n    \u002F\u002F Тут можна: завантажити конфігурацію, створити Injector, підключитися до БД\n    this.injector = Guice.createInjector(new AudiobookModule());\n    \n    \u002F\u002F Тут НЕ можна: створювати Stage, Scene, Button, тощо\n}\n","java",[3154,3945,3946,3956,3972,3978,4004,4009,4014,4047,4051,4056],{"__ignoreMap":3522},[3526,3947,3948,3952],{"class":3528,"line":3529},[3526,3949,3951],{"class":3950},"sHH4Y","@",[3526,3953,3955],{"class":3954},"sN1BT","Override\n",[3526,3957,3958,3962,3965,3969],{"class":3528,"line":3535},[3526,3959,3961],{"class":3960},"su1O8","public",[3526,3963,3964],{"class":3954}," void",[3526,3966,3968],{"class":3967},"s8Opu"," init",[3526,3970,3971],{"class":3950},"() throws Exception {\n",[3526,3973,3974],{"class":3528,"line":3541},[3526,3975,3977],{"class":3976},"spJ8K","    \u002F\u002F Виконується у фоновому потоці\n",[3526,3979,3980,3984,3986,3989,3991,3994,3997,4001],{"class":3528,"line":3547},[3526,3981,3983],{"class":3982},"siwwj","    System",[3526,3985,3166],{"class":3950},[3526,3987,3988],{"class":3982},"out",[3526,3990,3166],{"class":3950},[3526,3992,3993],{"class":3967},"println",[3526,3995,3996],{"class":3950},"(",[3526,3998,4000],{"class":3999},"sbdoH","\"Initializing application...\"",[3526,4002,4003],{"class":3950},");\n",[3526,4005,4006],{"class":3528,"line":3553},[3526,4007,4008],{"class":3950},"    \n",[3526,4010,4011],{"class":3528,"line":3559},[3526,4012,4013],{"class":3976},"    \u002F\u002F Тут можна: завантажити конфігурацію, створити Injector, підключитися до БД\n",[3526,4015,4016,4019,4021,4024,4027,4030,4032,4035,4037,4041,4044],{"class":3528,"line":3565},[3526,4017,4018],{"class":3960},"    this",[3526,4020,3166],{"class":3950},[3526,4022,4023],{"class":3982},"injector",[3526,4025,4026],{"class":3950}," = ",[3526,4028,4029],{"class":3982},"Guice",[3526,4031,3166],{"class":3950},[3526,4033,4034],{"class":3967},"createInjector",[3526,4036,3996],{"class":3950},[3526,4038,4040],{"class":4039},"s8xlr","new",[3526,4042,4043],{"class":3967}," AudiobookModule",[3526,4045,4046],{"class":3950},"());\n",[3526,4048,4049],{"class":3528,"line":3571},[3526,4050,4008],{"class":3950},[3526,4052,4053],{"class":3528,"line":3577},[3526,4054,4055],{"class":3976},"    \u002F\u002F Тут НЕ можна: створювати Stage, Scene, Button, тощо\n",[3526,4057,4058],{"class":3528,"line":3583},[3526,4059,4060],{"class":3950},"}\n",[3150,4062,4063,4070,4071,4074],{},[3163,4064,4065,4066,4069],{},"Фаза 2: ",[3154,4067,4068],{},"start(Stage primaryStage)"," — побудова UI."," Це головний метод вашого додатку. Він викликається у ",[3163,4072,4073],{},"JavaFX Application Thread"," — спеціальному потоці, що відповідає за всі UI-операції. Тут ви створюєте Scene, завантажуєте FXML, налаштовуєте вікно та показуєте його користувачу.",[3518,4076,4078],{"className":3941,"code":4077,"language":3943,"meta":3522,"style":3522},"@Override\npublic void start(Stage primaryStage) throws Exception {\n    \u002F\u002F Виконується у JavaFX Application Thread\n    primaryStage.setTitle(\"Audiobook Platform\");\n    \n    \u002F\u002F Створення Scene з Root Node\n    VBox root = new VBox();\n    root.getChildren().add(new Label(\"Welcome to Audiobook Platform\"));\n    \n    Scene scene = new Scene(root, 800, 600);\n    primaryStage.setScene(scene);\n    primaryStage.show(); \u002F\u002F Показати вікно\n}\n",[3154,4079,4080,4086,4102,4107,4124,4128,4133,4151,4182,4186,4215,4227,4242],{"__ignoreMap":3522},[3526,4081,4082,4084],{"class":3528,"line":3529},[3526,4083,3951],{"class":3950},[3526,4085,3955],{"class":3954},[3526,4087,4088,4090,4092,4095,4097,4099],{"class":3528,"line":3535},[3526,4089,3961],{"class":3960},[3526,4091,3964],{"class":3954},[3526,4093,4094],{"class":3967}," start",[3526,4096,3996],{"class":3950},[3526,4098,3632],{"class":3954},[3526,4100,4101],{"class":3950}," primaryStage) throws Exception {\n",[3526,4103,4104],{"class":3528,"line":3541},[3526,4105,4106],{"class":3976},"    \u002F\u002F Виконується у JavaFX Application Thread\n",[3526,4108,4109,4112,4114,4117,4119,4122],{"class":3528,"line":3547},[3526,4110,4111],{"class":3982},"    primaryStage",[3526,4113,3166],{"class":3950},[3526,4115,4116],{"class":3967},"setTitle",[3526,4118,3996],{"class":3950},[3526,4120,4121],{"class":3999},"\"Audiobook Platform\"",[3526,4123,4003],{"class":3950},[3526,4125,4126],{"class":3528,"line":3553},[3526,4127,4008],{"class":3950},[3526,4129,4130],{"class":3528,"line":3559},[3526,4131,4132],{"class":3976},"    \u002F\u002F Створення Scene з Root Node\n",[3526,4134,4135,4138,4141,4143,4145,4148],{"class":3528,"line":3565},[3526,4136,4137],{"class":3954},"    VBox",[3526,4139,4140],{"class":3982}," root",[3526,4142,4026],{"class":3950},[3526,4144,4040],{"class":4039},[3526,4146,4147],{"class":3967}," VBox",[3526,4149,4150],{"class":3950},"();\n",[3526,4152,4153,4156,4158,4161,4164,4167,4169,4171,4174,4176,4179],{"class":3528,"line":3571},[3526,4154,4155],{"class":3982},"    root",[3526,4157,3166],{"class":3950},[3526,4159,4160],{"class":3967},"getChildren",[3526,4162,4163],{"class":3950},"().",[3526,4165,4166],{"class":3967},"add",[3526,4168,3996],{"class":3950},[3526,4170,4040],{"class":4039},[3526,4172,4173],{"class":3967}," Label",[3526,4175,3996],{"class":3950},[3526,4177,4178],{"class":3999},"\"Welcome to Audiobook Platform\"",[3526,4180,4181],{"class":3950},"));\n",[3526,4183,4184],{"class":3528,"line":3577},[3526,4185,4008],{"class":3950},[3526,4187,4188,4191,4194,4196,4198,4201,4204,4208,4210,4213],{"class":3528,"line":3583},[3526,4189,4190],{"class":3954},"    Scene",[3526,4192,4193],{"class":3982}," scene",[3526,4195,4026],{"class":3950},[3526,4197,4040],{"class":4039},[3526,4199,4200],{"class":3967}," Scene",[3526,4202,4203],{"class":3950},"(root, ",[3526,4205,4207],{"class":4206},"sJj4R","800",[3526,4209,3173],{"class":3950},[3526,4211,4212],{"class":4206},"600",[3526,4214,4003],{"class":3950},[3526,4216,4217,4219,4221,4224],{"class":3528,"line":3589},[3526,4218,4111],{"class":3982},[3526,4220,3166],{"class":3950},[3526,4222,4223],{"class":3967},"setScene",[3526,4225,4226],{"class":3950},"(scene);\n",[3526,4228,4229,4231,4233,4236,4239],{"class":3528,"line":3595},[3526,4230,4111],{"class":3982},[3526,4232,3166],{"class":3950},[3526,4234,4235],{"class":3967},"show",[3526,4237,4238],{"class":3950},"(); ",[3526,4240,4241],{"class":3976},"\u002F\u002F Показати вікно\n",[3526,4243,4244],{"class":3528,"line":3601},[3526,4245,4060],{"class":3950},[3150,4247,4248,4255],{},[3163,4249,4250,4251,4254],{},"Фаза 3: ",[3154,4252,4253],{},"stop()"," — очищення ресурсів."," Цей метод викликається, коли користувач закриває останнє вікно або додаток завершується програмно. Тут ви маєте закрити всі відкриті ресурси: з'єднання з базою даних, файлові дескриптори, мережеві сокети.",[3518,4257,4259],{"className":3941,"code":4258,"language":3943,"meta":3522,"style":3522},"@Override\npublic void stop() throws Exception {\n    \u002F\u002F Виконується при завершенні додатку\n    System.out.println(\"Shutting down application...\");\n    \n    \u002F\u002F Закриття DataSource, відписка від listeners, тощо\n    injector.getInstance(DataSource.class).close();\n}\n",[3154,4260,4261,4267,4278,4283,4302,4306,4311,4339],{"__ignoreMap":3522},[3526,4262,4263,4265],{"class":3528,"line":3529},[3526,4264,3951],{"class":3950},[3526,4266,3955],{"class":3954},[3526,4268,4269,4271,4273,4276],{"class":3528,"line":3535},[3526,4270,3961],{"class":3960},[3526,4272,3964],{"class":3954},[3526,4274,4275],{"class":3967}," stop",[3526,4277,3971],{"class":3950},[3526,4279,4280],{"class":3528,"line":3541},[3526,4281,4282],{"class":3976},"    \u002F\u002F Виконується при завершенні додатку\n",[3526,4284,4285,4287,4289,4291,4293,4295,4297,4300],{"class":3528,"line":3547},[3526,4286,3983],{"class":3982},[3526,4288,3166],{"class":3950},[3526,4290,3988],{"class":3982},[3526,4292,3166],{"class":3950},[3526,4294,3993],{"class":3967},[3526,4296,3996],{"class":3950},[3526,4298,4299],{"class":3999},"\"Shutting down application...\"",[3526,4301,4003],{"class":3950},[3526,4303,4304],{"class":3528,"line":3553},[3526,4305,4008],{"class":3950},[3526,4307,4308],{"class":3528,"line":3559},[3526,4309,4310],{"class":3976},"    \u002F\u002F Закриття DataSource, відписка від listeners, тощо\n",[3526,4312,4313,4316,4318,4321,4323,4326,4328,4331,4334,4337],{"class":3528,"line":3565},[3526,4314,4315],{"class":3982},"    injector",[3526,4317,3166],{"class":3950},[3526,4319,4320],{"class":3967},"getInstance",[3526,4322,3996],{"class":3950},[3526,4324,4325],{"class":3982},"DataSource",[3526,4327,3166],{"class":3950},[3526,4329,4330],{"class":3982},"class",[3526,4332,4333],{"class":3950},").",[3526,4335,4336],{"class":3967},"close",[3526,4338,4150],{"class":3950},[3526,4340,4341],{"class":3528,"line":3571},[3526,4342,4060],{"class":3950},[4344,4345,4346,4348,4349,4351,4352,4354,4355,4358,4359,3166],"warning",{},[3163,4347,4073],{}," — це єдиний потік, у якому можна безпечно працювати з UI-компонентами. Спроба створити ",[3154,4350,3503],{}," або оновити ",[3154,4353,3509],{}," з іншого потоку призведе до винятку ",[3154,4356,4357],{},"IllegalStateException: Not on FX application thread",". Для виконання UI-операцій з фонових потоків використовуйте ",[3154,4360,4361],{},"Platform.runLater(() -> { \u002F* UI код *\u002F })",[3150,4363,4364,4370,4371,4374,4375,4378],{},[3163,4365,4366,4367,3166],{},"Запуск додатку: ",[3154,4368,4369],{},"Application.launch()"," Щоб запустити JavaFX-додаток, викличте статичний метод ",[3154,4372,4373],{},"launch()"," у методі ",[3154,4376,4377],{},"main()",":",[3518,4380,4382],{"className":3941,"code":4381,"language":3943,"meta":3522,"style":3522},"public class AudiobookApp extends Application {\n    \n    public static void main(String[] args) {\n        launch(args); \u002F\u002F Запуск JavaFX Application\n    }\n    \n    @Override\n    public void start(Stage primaryStage) {\n        \u002F\u002F Побудова UI\n    }\n}\n",[3154,4383,4384,4403,4407,4434,4445,4450,4454,4461,4478,4483,4487],{"__ignoreMap":3522},[3526,4385,4386,4388,4391,4394,4397,4400],{"class":3528,"line":3529},[3526,4387,3961],{"class":3960},[3526,4389,4390],{"class":3960}," class",[3526,4392,4393],{"class":3954}," AudiobookApp",[3526,4395,4396],{"class":3960}," extends",[3526,4398,4399],{"class":3954}," Application",[3526,4401,4402],{"class":3950}," {\n",[3526,4404,4405],{"class":3528,"line":3535},[3526,4406,4008],{"class":3950},[3526,4408,4409,4412,4415,4417,4420,4422,4425,4428,4431],{"class":3528,"line":3541},[3526,4410,4411],{"class":3960},"    public",[3526,4413,4414],{"class":3960}," static",[3526,4416,3964],{"class":3954},[3526,4418,4419],{"class":3967}," main",[3526,4421,3996],{"class":3950},[3526,4423,4424],{"class":3954},"String",[3526,4426,4427],{"class":3950},"[] ",[3526,4429,4430],{"class":3982},"args",[3526,4432,4433],{"class":3950},") {\n",[3526,4435,4436,4439,4442],{"class":3528,"line":3547},[3526,4437,4438],{"class":3967},"        launch",[3526,4440,4441],{"class":3950},"(args); ",[3526,4443,4444],{"class":3976},"\u002F\u002F Запуск JavaFX Application\n",[3526,4446,4447],{"class":3528,"line":3553},[3526,4448,4449],{"class":3950},"    }\n",[3526,4451,4452],{"class":3528,"line":3559},[3526,4453,4008],{"class":3950},[3526,4455,4456,4459],{"class":3528,"line":3565},[3526,4457,4458],{"class":3950},"    @",[3526,4460,3955],{"class":3954},[3526,4462,4463,4465,4467,4469,4471,4473,4476],{"class":3528,"line":3571},[3526,4464,4411],{"class":3960},[3526,4466,3964],{"class":3954},[3526,4468,4094],{"class":3967},[3526,4470,3996],{"class":3950},[3526,4472,3632],{"class":3954},[3526,4474,4475],{"class":3982}," primaryStage",[3526,4477,4433],{"class":3950},[3526,4479,4480],{"class":3528,"line":3577},[3526,4481,4482],{"class":3976},"        \u002F\u002F Побудова UI\n",[3526,4484,4485],{"class":3528,"line":3583},[3526,4486,4449],{"class":3950},[3526,4488,4489],{"class":3528,"line":3589},[3526,4490,4060],{"class":3950},[3150,4492,4493,4494,4496],{},"Метод ",[3154,4495,4373],{}," виконує наступне:",[4498,4499,4500,4504,4510,4516,4519],"ol",{},[4501,4502,4503],"li",{},"Створює екземпляр вашого класу (через рефлексію, викликаючи конструктор без параметрів).",[4501,4505,4506,4507,4509],{},"Викликає ",[3154,4508,3926],{}," у фоновому потоці.",[4501,4511,4512,4513,3166],{},"Створює JavaFX Application Thread та викликає ",[3154,4514,4515],{},"start(primaryStage)",[4501,4517,4518],{},"Чекає, поки всі вікна будуть закриті.",[4501,4520,4506,4521,4523],{},[3154,4522,4253],{}," для очищення ресурсів.",[3515,4525,4526],{},[3518,4527,4529],{"className":3520,"code":4528,"language":3515,"meta":3522,"style":3522},"sequenceDiagram\n    participant Main as main()\n    participant FX as JavaFX Runtime\n    participant App as Application\n    participant UI as UI Thread\n    \n    Main->>FX: launch(args)\n    FX->>App: new AudiobookApp()\n    FX->>App: init() [фоновий потік]\n    Note over App: Ініціалізація: Guice, БД\n    FX->>UI: Створення JavaFX Thread\n    UI->>App: start(primaryStage)\n    Note over App: Побудова UI, показ вікна\n    App->>UI: primaryStage.show()\n    Note over UI: Додаток працює\u003Cbr\u002F>Обробка подій\n    UI->>App: Користувач закрив вікно\n    UI->>App: stop()\n    Note over App: Закриття ресурсів\n    App->>FX: Завершення\n    FX->>Main: System.exit(0)\n",[3154,4530,4531,4536,4541,4546,4551,4556,4560,4565,4570,4575,4580,4585,4590,4595,4600,4605,4610,4616,4622,4628],{"__ignoreMap":3522},[3526,4532,4533],{"class":3528,"line":3529},[3526,4534,4535],{},"sequenceDiagram\n",[3526,4537,4538],{"class":3528,"line":3535},[3526,4539,4540],{},"    participant Main as main()\n",[3526,4542,4543],{"class":3528,"line":3541},[3526,4544,4545],{},"    participant FX as JavaFX Runtime\n",[3526,4547,4548],{"class":3528,"line":3547},[3526,4549,4550],{},"    participant App as Application\n",[3526,4552,4553],{"class":3528,"line":3553},[3526,4554,4555],{},"    participant UI as UI Thread\n",[3526,4557,4558],{"class":3528,"line":3559},[3526,4559,4008],{},[3526,4561,4562],{"class":3528,"line":3565},[3526,4563,4564],{},"    Main->>FX: launch(args)\n",[3526,4566,4567],{"class":3528,"line":3571},[3526,4568,4569],{},"    FX->>App: new AudiobookApp()\n",[3526,4571,4572],{"class":3528,"line":3577},[3526,4573,4574],{},"    FX->>App: init() [фоновий потік]\n",[3526,4576,4577],{"class":3528,"line":3583},[3526,4578,4579],{},"    Note over App: Ініціалізація: Guice, БД\n",[3526,4581,4582],{"class":3528,"line":3589},[3526,4583,4584],{},"    FX->>UI: Створення JavaFX Thread\n",[3526,4586,4587],{"class":3528,"line":3595},[3526,4588,4589],{},"    UI->>App: start(primaryStage)\n",[3526,4591,4592],{"class":3528,"line":3601},[3526,4593,4594],{},"    Note over App: Побудова UI, показ вікна\n",[3526,4596,4597],{"class":3528,"line":3607},[3526,4598,4599],{},"    App->>UI: primaryStage.show()\n",[3526,4601,4602],{"class":3528,"line":3613},[3526,4603,4604],{},"    Note over UI: Додаток працює\u003Cbr\u002F>Обробка подій\n",[3526,4606,4607],{"class":3528,"line":3619},[3526,4608,4609],{},"    UI->>App: Користувач закрив вікно\n",[3526,4611,4613],{"class":3528,"line":4612},17,[3526,4614,4615],{},"    UI->>App: stop()\n",[3526,4617,4619],{"class":3528,"line":4618},18,[3526,4620,4621],{},"    Note over App: Закриття ресурсів\n",[3526,4623,4625],{"class":3528,"line":4624},19,[3526,4626,4627],{},"    App->>FX: Завершення\n",[3526,4629,4631],{"class":3528,"line":4630},20,[3526,4632,4633],{},"    FX->>Main: System.exit(0)\n",[3375,4635],{},[3145,4637,4639],{"id":4638},"перший-javafx-додаток-hello-audiobook-platform","Перший JavaFX-додаток: Hello Audiobook Platform",[3150,4641,4642],{},"Теорія без практики — це лише слова. Настав час написати наш перший JavaFX-додаток. Він буде максимально простим: вікно з написом \"Audiobook Platform\" та кнопкою \"Load Tracks\". Але навіть у цій простоті ховається вся архітектура, яку ми щойно вивчили.",[3206,4644,4646],{"id":4645},"крок-1-налаштування-проєкту","Крок 1: Налаштування проєкту",[3150,4648,4649,4650,4653,4654,4657],{},"Перш за все, переконайтеся, що у вашому ",[3154,4651,4652],{},"pom.xml"," (Maven) або ",[3154,4655,4656],{},"build.gradle"," (Gradle) додані залежності JavaFX:",[3518,4659,4663],{"className":4660,"code":4661,"language":4662,"meta":3522,"style":3522},"language-xml shiki shiki-themes light-plus dark-plus dark-plus","\u003C!-- Maven: pom.xml -->\n\u003Cdependencies>\n    \u003Cdependency>\n        \u003CgroupId>org.openjfx\u003C\u002FgroupId>\n        \u003CartifactId>javafx-controls\u003C\u002FartifactId>\n        \u003Cversion>21\u003C\u002Fversion>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.openjfx\u003C\u002FgroupId>\n        \u003CartifactId>javafx-fxml\u003C\u002FartifactId>\n        \u003Cversion>21\u003C\u002Fversion>\n    \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n\n\u003Cbuild>\n    \u003Cplugins>\n        \u003Cplugin>\n            \u003CgroupId>org.openjfx\u003C\u002FgroupId>\n            \u003CartifactId>javafx-maven-plugin\u003C\u002FartifactId>\n            \u003Cversion>0.0.8\u003C\u002Fversion>\n            \u003Cconfiguration>\n                \u003CmainClass>com.arakviel.AudiobookApp\u003C\u002FmainClass>\n            \u003C\u002Fconfiguration>\n        \u003C\u002Fplugin>\n    \u003C\u002Fplugins>\n\u003C\u002Fbuild>\n","xml",[3154,4664,4665,4670,4683,4693,4714,4732,4750,4759,4767,4783,4800,4816,4824,4832,4838,4847,4856,4865,4882,4899,4916,4926,4946,4956,4966,4975],{"__ignoreMap":3522},[3526,4666,4667],{"class":3528,"line":3529},[3526,4668,4669],{"class":3976},"\u003C!-- Maven: pom.xml -->\n",[3526,4671,4672,4676,4680],{"class":3528,"line":3535},[3526,4673,4675],{"class":4674},"s0P7L","\u003C",[3526,4677,4679],{"class":4678},"sKtos","dependencies",[3526,4681,4682],{"class":4674},">\n",[3526,4684,4685,4688,4691],{"class":3528,"line":3541},[3526,4686,4687],{"class":4674},"    \u003C",[3526,4689,4690],{"class":4678},"dependency",[3526,4692,4682],{"class":4674},[3526,4694,4695,4698,4701,4704,4707,4710,4712],{"class":3528,"line":3547},[3526,4696,4697],{"class":4674},"        \u003C",[3526,4699,4700],{"class":4678},"groupId",[3526,4702,4703],{"class":4674},">",[3526,4705,4706],{"class":3950},"org.openjfx",[3526,4708,4709],{"class":4674},"\u003C\u002F",[3526,4711,4700],{"class":4678},[3526,4713,4682],{"class":4674},[3526,4715,4716,4718,4721,4723,4726,4728,4730],{"class":3528,"line":3553},[3526,4717,4697],{"class":4674},[3526,4719,4720],{"class":4678},"artifactId",[3526,4722,4703],{"class":4674},[3526,4724,4725],{"class":3950},"javafx-controls",[3526,4727,4709],{"class":4674},[3526,4729,4720],{"class":4678},[3526,4731,4682],{"class":4674},[3526,4733,4734,4736,4739,4741,4744,4746,4748],{"class":3528,"line":3559},[3526,4735,4697],{"class":4674},[3526,4737,4738],{"class":4678},"version",[3526,4740,4703],{"class":4674},[3526,4742,4743],{"class":3950},"21",[3526,4745,4709],{"class":4674},[3526,4747,4738],{"class":4678},[3526,4749,4682],{"class":4674},[3526,4751,4752,4755,4757],{"class":3528,"line":3565},[3526,4753,4754],{"class":4674},"    \u003C\u002F",[3526,4756,4690],{"class":4678},[3526,4758,4682],{"class":4674},[3526,4760,4761,4763,4765],{"class":3528,"line":3571},[3526,4762,4687],{"class":4674},[3526,4764,4690],{"class":4678},[3526,4766,4682],{"class":4674},[3526,4768,4769,4771,4773,4775,4777,4779,4781],{"class":3528,"line":3577},[3526,4770,4697],{"class":4674},[3526,4772,4700],{"class":4678},[3526,4774,4703],{"class":4674},[3526,4776,4706],{"class":3950},[3526,4778,4709],{"class":4674},[3526,4780,4700],{"class":4678},[3526,4782,4682],{"class":4674},[3526,4784,4785,4787,4789,4791,4794,4796,4798],{"class":3528,"line":3583},[3526,4786,4697],{"class":4674},[3526,4788,4720],{"class":4678},[3526,4790,4703],{"class":4674},[3526,4792,4793],{"class":3950},"javafx-fxml",[3526,4795,4709],{"class":4674},[3526,4797,4720],{"class":4678},[3526,4799,4682],{"class":4674},[3526,4801,4802,4804,4806,4808,4810,4812,4814],{"class":3528,"line":3589},[3526,4803,4697],{"class":4674},[3526,4805,4738],{"class":4678},[3526,4807,4703],{"class":4674},[3526,4809,4743],{"class":3950},[3526,4811,4709],{"class":4674},[3526,4813,4738],{"class":4678},[3526,4815,4682],{"class":4674},[3526,4817,4818,4820,4822],{"class":3528,"line":3595},[3526,4819,4754],{"class":4674},[3526,4821,4690],{"class":4678},[3526,4823,4682],{"class":4674},[3526,4825,4826,4828,4830],{"class":3528,"line":3601},[3526,4827,4709],{"class":4674},[3526,4829,4679],{"class":4678},[3526,4831,4682],{"class":4674},[3526,4833,4834],{"class":3528,"line":3607},[3526,4835,4837],{"emptyLinePlaceholder":4836},true,"\n",[3526,4839,4840,4842,4845],{"class":3528,"line":3613},[3526,4841,4675],{"class":4674},[3526,4843,4844],{"class":4678},"build",[3526,4846,4682],{"class":4674},[3526,4848,4849,4851,4854],{"class":3528,"line":3619},[3526,4850,4687],{"class":4674},[3526,4852,4853],{"class":4678},"plugins",[3526,4855,4682],{"class":4674},[3526,4857,4858,4860,4863],{"class":3528,"line":4612},[3526,4859,4697],{"class":4674},[3526,4861,4862],{"class":4678},"plugin",[3526,4864,4682],{"class":4674},[3526,4866,4867,4870,4872,4874,4876,4878,4880],{"class":3528,"line":4618},[3526,4868,4869],{"class":4674},"            \u003C",[3526,4871,4700],{"class":4678},[3526,4873,4703],{"class":4674},[3526,4875,4706],{"class":3950},[3526,4877,4709],{"class":4674},[3526,4879,4700],{"class":4678},[3526,4881,4682],{"class":4674},[3526,4883,4884,4886,4888,4890,4893,4895,4897],{"class":3528,"line":4624},[3526,4885,4869],{"class":4674},[3526,4887,4720],{"class":4678},[3526,4889,4703],{"class":4674},[3526,4891,4892],{"class":3950},"javafx-maven-plugin",[3526,4894,4709],{"class":4674},[3526,4896,4720],{"class":4678},[3526,4898,4682],{"class":4674},[3526,4900,4901,4903,4905,4907,4910,4912,4914],{"class":3528,"line":4630},[3526,4902,4869],{"class":4674},[3526,4904,4738],{"class":4678},[3526,4906,4703],{"class":4674},[3526,4908,4909],{"class":3950},"0.0.8",[3526,4911,4709],{"class":4674},[3526,4913,4738],{"class":4678},[3526,4915,4682],{"class":4674},[3526,4917,4919,4921,4924],{"class":3528,"line":4918},21,[3526,4920,4869],{"class":4674},[3526,4922,4923],{"class":4678},"configuration",[3526,4925,4682],{"class":4674},[3526,4927,4929,4932,4935,4937,4940,4942,4944],{"class":3528,"line":4928},22,[3526,4930,4931],{"class":4674},"                \u003C",[3526,4933,4934],{"class":4678},"mainClass",[3526,4936,4703],{"class":4674},[3526,4938,4939],{"class":3950},"com.arakviel.AudiobookApp",[3526,4941,4709],{"class":4674},[3526,4943,4934],{"class":4678},[3526,4945,4682],{"class":4674},[3526,4947,4949,4952,4954],{"class":3528,"line":4948},23,[3526,4950,4951],{"class":4674},"            \u003C\u002F",[3526,4953,4923],{"class":4678},[3526,4955,4682],{"class":4674},[3526,4957,4959,4962,4964],{"class":3528,"line":4958},24,[3526,4960,4961],{"class":4674},"        \u003C\u002F",[3526,4963,4862],{"class":4678},[3526,4965,4682],{"class":4674},[3526,4967,4969,4971,4973],{"class":3528,"line":4968},25,[3526,4970,4754],{"class":4674},[3526,4972,4853],{"class":4678},[3526,4974,4682],{"class":4674},[3526,4976,4978,4980,4982],{"class":3528,"line":4977},26,[3526,4979,4709],{"class":4674},[3526,4981,4844],{"class":4678},[3526,4983,4682],{"class":4674},[3150,4985,4986],{},"Для Gradle:",[3518,4988,4992],{"className":4989,"code":4990,"language":4991,"meta":3522,"style":3522},"language-groovy shiki shiki-themes light-plus dark-plus dark-plus","\u002F\u002F build.gradle\nplugins {\n    id 'application'\n    id 'org.openjfx.javafxplugin' version '0.1.0'\n}\n\njavafx {\n    version = \"21\"\n    modules = ['javafx.controls', 'javafx.fxml']\n}\n\napplication {\n    mainClass = 'com.arakviel.AudiobookApp'\n}\n\ndependencies {\n    implementation 'org.openjfx:javafx-controls:21'\n    implementation 'org.openjfx:javafx-fxml:21'\n}\n","groovy",[3154,4993,4994,4999,5004,5009,5014,5018,5022,5027,5032,5037,5041,5045,5050,5055,5059,5063,5068,5073,5078],{"__ignoreMap":3522},[3526,4995,4996],{"class":3528,"line":3529},[3526,4997,4998],{},"\u002F\u002F build.gradle\n",[3526,5000,5001],{"class":3528,"line":3535},[3526,5002,5003],{},"plugins {\n",[3526,5005,5006],{"class":3528,"line":3541},[3526,5007,5008],{},"    id 'application'\n",[3526,5010,5011],{"class":3528,"line":3547},[3526,5012,5013],{},"    id 'org.openjfx.javafxplugin' version '0.1.0'\n",[3526,5015,5016],{"class":3528,"line":3553},[3526,5017,4060],{},[3526,5019,5020],{"class":3528,"line":3559},[3526,5021,4837],{"emptyLinePlaceholder":4836},[3526,5023,5024],{"class":3528,"line":3565},[3526,5025,5026],{},"javafx {\n",[3526,5028,5029],{"class":3528,"line":3571},[3526,5030,5031],{},"    version = \"21\"\n",[3526,5033,5034],{"class":3528,"line":3577},[3526,5035,5036],{},"    modules = ['javafx.controls', 'javafx.fxml']\n",[3526,5038,5039],{"class":3528,"line":3583},[3526,5040,4060],{},[3526,5042,5043],{"class":3528,"line":3589},[3526,5044,4837],{"emptyLinePlaceholder":4836},[3526,5046,5047],{"class":3528,"line":3595},[3526,5048,5049],{},"application {\n",[3526,5051,5052],{"class":3528,"line":3601},[3526,5053,5054],{},"    mainClass = 'com.arakviel.AudiobookApp'\n",[3526,5056,5057],{"class":3528,"line":3607},[3526,5058,4060],{},[3526,5060,5061],{"class":3528,"line":3613},[3526,5062,4837],{"emptyLinePlaceholder":4836},[3526,5064,5065],{"class":3528,"line":3619},[3526,5066,5067],{},"dependencies {\n",[3526,5069,5070],{"class":3528,"line":4612},[3526,5071,5072],{},"    implementation 'org.openjfx:javafx-controls:21'\n",[3526,5074,5075],{"class":3528,"line":4618},[3526,5076,5077],{},"    implementation 'org.openjfx:javafx-fxml:21'\n",[3526,5079,5080],{"class":3528,"line":4624},[3526,5081,4060],{},[3206,5083,5085],{"id":5084},"запуск-javafx-додатку","Запуск JavaFX-додатку",[3150,5087,5088],{},"Після налаштування залежностей ви можете запустити додаток різними способами залежно від вашої системи збірки.",[3150,5090,5091],{},[3163,5092,5093],{},"Для Maven:",[3518,5095,5099],{"className":5096,"code":5097,"language":5098,"meta":3522,"style":3522},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# Запуск через Maven plugin\nmvn clean javafx:run\n\n# Або збірка та запуск JAR-файлу\nmvn clean package\njava --module-path \u002Fpath\u002Fto\u002Fjavafx-sdk\u002Flib --add-modules javafx.controls,javafx.fxml -jar target\u002Faudiobook-app.jar\n","bash",[3154,5100,5101,5106,5117,5121,5126,5135],{"__ignoreMap":3522},[3526,5102,5103],{"class":3528,"line":3529},[3526,5104,5105],{"class":3976},"# Запуск через Maven plugin\n",[3526,5107,5108,5111,5114],{"class":3528,"line":3535},[3526,5109,5110],{"class":3967},"mvn",[3526,5112,5113],{"class":3999}," clean",[3526,5115,5116],{"class":3999}," javafx:run\n",[3526,5118,5119],{"class":3528,"line":3541},[3526,5120,4837],{"emptyLinePlaceholder":4836},[3526,5122,5123],{"class":3528,"line":3547},[3526,5124,5125],{"class":3976},"# Або збірка та запуск JAR-файлу\n",[3526,5127,5128,5130,5132],{"class":3528,"line":3553},[3526,5129,5110],{"class":3967},[3526,5131,5113],{"class":3999},[3526,5133,5134],{"class":3999}," package\n",[3526,5136,5137,5139,5142,5145,5148,5151,5154],{"class":3528,"line":3559},[3526,5138,3943],{"class":3967},[3526,5140,5141],{"class":3960}," --module-path",[3526,5143,5144],{"class":3999}," \u002Fpath\u002Fto\u002Fjavafx-sdk\u002Flib",[3526,5146,5147],{"class":3960}," --add-modules",[3526,5149,5150],{"class":3999}," javafx.controls,javafx.fxml",[3526,5152,5153],{"class":3960}," -jar",[3526,5155,5156],{"class":3999}," target\u002Faudiobook-app.jar\n",[3150,5158,5159],{},[3163,5160,4986],{},[3518,5162,5164],{"className":5096,"code":5163,"language":5098,"meta":3522,"style":3522},"# Запуск через Gradle plugin\n.\u002Fgradlew run\n\n# Або збірка та запуск JAR-файлу\n.\u002Fgradlew build\njava --module-path \u002Fpath\u002Fto\u002Fjavafx-sdk\u002Flib --add-modules javafx.controls,javafx.fxml -jar build\u002Flibs\u002Faudiobook-app.jar\n",[3154,5165,5166,5171,5179,5183,5187,5194],{"__ignoreMap":3522},[3526,5167,5168],{"class":3528,"line":3529},[3526,5169,5170],{"class":3976},"# Запуск через Gradle plugin\n",[3526,5172,5173,5176],{"class":3528,"line":3535},[3526,5174,5175],{"class":3967},".\u002Fgradlew",[3526,5177,5178],{"class":3999}," run\n",[3526,5180,5181],{"class":3528,"line":3541},[3526,5182,4837],{"emptyLinePlaceholder":4836},[3526,5184,5185],{"class":3528,"line":3547},[3526,5186,5125],{"class":3976},[3526,5188,5189,5191],{"class":3528,"line":3553},[3526,5190,5175],{"class":3967},[3526,5192,5193],{"class":3999}," build\n",[3526,5195,5196,5198,5200,5202,5204,5206,5208],{"class":3528,"line":3559},[3526,5197,3943],{"class":3967},[3526,5199,5141],{"class":3960},[3526,5201,5144],{"class":3999},[3526,5203,5147],{"class":3960},[3526,5205,5150],{"class":3999},[3526,5207,5153],{"class":3960},[3526,5209,5210],{"class":3999}," build\u002Flibs\u002Faudiobook-app.jar\n",[4344,5212,5213,5226,5232],{},[3150,5214,5215,5218,5219,5221,5222,5225],{},[3163,5216,5217],{},"Важливо:"," Без плагіна ",[3154,5220,4892],{}," (для Maven) або ",[3154,5223,5224],{},"org.openjfx.javafxplugin"," (для Gradle) додаток не запуститься, оскільки JavaFX-модулі не будуть автоматично додані до module path. Ви отримаєте помилку:",[3518,5227,5230],{"className":5228,"code":5229,"language":3735},[3733],"Error: JavaFX runtime components are missing, and are required to run this application\n",[3154,5231,5229],{"__ignoreMap":3522},[3150,5233,5234,5235,3282,5238,5241],{},"Плагіни автоматично налаштовують ",[3154,5236,5237],{},"--module-path",[3154,5239,5240],{},"--add-modules"," при запуску.",[3189,5243,5244,5254,5289],{},[3150,5245,5246,5249,5250,5253],{},[3163,5247,5248],{},"Модульна система Java"," вимагає явного оголошення модулів JavaFX. Якщо ви використовуєте ",[3154,5251,5252],{},"module-info.java",", додайте:",[3518,5255,5257],{"className":3941,"code":5256,"language":3943,"meta":3522,"style":3522},"module dev.kostyl.audiobook {\n    requires javafx.controls;\n    requires javafx.fxml;\n}\n",[3154,5258,5259,5270,5278,5285],{"__ignoreMap":3522},[3526,5260,5261,5264,5267],{"class":3528,"line":3529},[3526,5262,5263],{"class":3960},"module",[3526,5265,5266],{"class":3954}," dev",[3526,5268,5269],{"class":3950},".kostyl.audiobook {\n",[3526,5271,5272,5275],{"class":3528,"line":3535},[3526,5273,5274],{"class":3960},"    requires",[3526,5276,5277],{"class":3950}," javafx.controls;\n",[3526,5279,5280,5282],{"class":3528,"line":3541},[3526,5281,5274],{"class":3960},[3526,5283,5284],{"class":3950}," javafx.fxml;\n",[3526,5286,5287],{"class":3528,"line":3547},[3526,5288,4060],{"class":3950},[3150,5290,5291],{},"Для простоти, у навчальних прикладах ми не використовуватимемо модульну систему (classpath-based підхід).",[3206,5293,5295],{"id":5294},"крок-2-створення-класу-application","Крок 2: Створення класу Application",[3150,5297,5298,5299,5302,5303,4378],{},"Створіть клас ",[3154,5300,5301],{},"AudiobookApp",", що успадковується від ",[3154,5304,5305],{},"Application",[3518,5307,5309],{"className":3941,"code":5308,"language":3943,"meta":3522,"style":3522},"package dev.kostyl.audiobook;\n\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class AudiobookApp extends Application {\n\n    @Override\n    public void start(Stage primaryStage) {\n        \u002F\u002F Крок 1: Створення Root Node (VBox — вертикальний контейнер)\n        VBox root = new VBox();\n        root.setSpacing(10); \u002F\u002F Відступ між елементами: 10 пікселів\n        root.setStyle(\"-fx-padding: 20; -fx-alignment: center;\");\n        \n        \u002F\u002F Крок 2: Створення UI-компонентів\n        Label titleLabel = new Label(\"Audiobook Platform\");\n        titleLabel.setStyle(\"-fx-font-size: 24px; -fx-font-weight: bold;\");\n        \n        Button loadButton = new Button(\"Load Tracks\");\n        loadButton.setOnAction(event -> {\n            System.out.println(\"Load Tracks button clicked!\");\n        });\n        \n        \u002F\u002F Крок 3: Додавання компонентів до контейнера\n        root.getChildren().addAll(titleLabel, loadButton);\n        \n        \u002F\u002F Крок 4: Створення Scene\n        Scene scene = new Scene(root, 400, 300);\n        \n        \u002F\u002F Крок 5: Налаштування Stage\n        primaryStage.setTitle(\"Audiobook Platform v1.0\");\n        primaryStage.setScene(scene);\n        primaryStage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n",[3154,5310,5311,5319,5323,5331,5338,5345,5352,5359,5366,5370,5384,5388,5394,5410,5415,5430,5451,5467,5472,5477,5497,5513,5517,5539,5557,5577,5582,5587,5593,5610,5615,5621,5645,5650,5656,5673,5684,5695,5700,5705,5726,5734,5739],{"__ignoreMap":3522},[3526,5312,5313,5316],{"class":3528,"line":3529},[3526,5314,5315],{"class":3960},"package",[3526,5317,5318],{"class":3950}," dev.kostyl.audiobook;\n",[3526,5320,5321],{"class":3528,"line":3535},[3526,5322,4837],{"emptyLinePlaceholder":4836},[3526,5324,5325,5328],{"class":3528,"line":3541},[3526,5326,5327],{"class":3960},"import",[3526,5329,5330],{"class":3950}," javafx.application.Application;\n",[3526,5332,5333,5335],{"class":3528,"line":3547},[3526,5334,5327],{"class":3960},[3526,5336,5337],{"class":3950}," javafx.scene.Scene;\n",[3526,5339,5340,5342],{"class":3528,"line":3553},[3526,5341,5327],{"class":3960},[3526,5343,5344],{"class":3950}," javafx.scene.control.Button;\n",[3526,5346,5347,5349],{"class":3528,"line":3559},[3526,5348,5327],{"class":3960},[3526,5350,5351],{"class":3950}," javafx.scene.control.Label;\n",[3526,5353,5354,5356],{"class":3528,"line":3565},[3526,5355,5327],{"class":3960},[3526,5357,5358],{"class":3950}," javafx.scene.layout.VBox;\n",[3526,5360,5361,5363],{"class":3528,"line":3571},[3526,5362,5327],{"class":3960},[3526,5364,5365],{"class":3950}," javafx.stage.Stage;\n",[3526,5367,5368],{"class":3528,"line":3577},[3526,5369,4837],{"emptyLinePlaceholder":4836},[3526,5371,5372,5374,5376,5378,5380,5382],{"class":3528,"line":3583},[3526,5373,3961],{"class":3960},[3526,5375,4390],{"class":3960},[3526,5377,4393],{"class":3954},[3526,5379,4396],{"class":3960},[3526,5381,4399],{"class":3954},[3526,5383,4402],{"class":3950},[3526,5385,5386],{"class":3528,"line":3589},[3526,5387,4837],{"emptyLinePlaceholder":4836},[3526,5389,5390,5392],{"class":3528,"line":3595},[3526,5391,4458],{"class":3950},[3526,5393,3955],{"class":3954},[3526,5395,5396,5398,5400,5402,5404,5406,5408],{"class":3528,"line":3601},[3526,5397,4411],{"class":3960},[3526,5399,3964],{"class":3954},[3526,5401,4094],{"class":3967},[3526,5403,3996],{"class":3950},[3526,5405,3632],{"class":3954},[3526,5407,4475],{"class":3982},[3526,5409,4433],{"class":3950},[3526,5411,5412],{"class":3528,"line":3607},[3526,5413,5414],{"class":3976},"        \u002F\u002F Крок 1: Створення Root Node (VBox — вертикальний контейнер)\n",[3526,5416,5417,5420,5422,5424,5426,5428],{"class":3528,"line":3613},[3526,5418,5419],{"class":3954},"        VBox",[3526,5421,4140],{"class":3982},[3526,5423,4026],{"class":3950},[3526,5425,4040],{"class":4039},[3526,5427,4147],{"class":3967},[3526,5429,4150],{"class":3950},[3526,5431,5432,5435,5437,5440,5442,5445,5448],{"class":3528,"line":3619},[3526,5433,5434],{"class":3982},"        root",[3526,5436,3166],{"class":3950},[3526,5438,5439],{"class":3967},"setSpacing",[3526,5441,3996],{"class":3950},[3526,5443,5444],{"class":4206},"10",[3526,5446,5447],{"class":3950},"); ",[3526,5449,5450],{"class":3976},"\u002F\u002F Відступ між елементами: 10 пікселів\n",[3526,5452,5453,5455,5457,5460,5462,5465],{"class":3528,"line":4612},[3526,5454,5434],{"class":3982},[3526,5456,3166],{"class":3950},[3526,5458,5459],{"class":3967},"setStyle",[3526,5461,3996],{"class":3950},[3526,5463,5464],{"class":3999},"\"-fx-padding: 20; -fx-alignment: center;\"",[3526,5466,4003],{"class":3950},[3526,5468,5469],{"class":3528,"line":4618},[3526,5470,5471],{"class":3950},"        \n",[3526,5473,5474],{"class":3528,"line":4624},[3526,5475,5476],{"class":3976},"        \u002F\u002F Крок 2: Створення UI-компонентів\n",[3526,5478,5479,5482,5485,5487,5489,5491,5493,5495],{"class":3528,"line":4630},[3526,5480,5481],{"class":3954},"        Label",[3526,5483,5484],{"class":3982}," titleLabel",[3526,5486,4026],{"class":3950},[3526,5488,4040],{"class":4039},[3526,5490,4173],{"class":3967},[3526,5492,3996],{"class":3950},[3526,5494,4121],{"class":3999},[3526,5496,4003],{"class":3950},[3526,5498,5499,5502,5504,5506,5508,5511],{"class":3528,"line":4918},[3526,5500,5501],{"class":3982},"        titleLabel",[3526,5503,3166],{"class":3950},[3526,5505,5459],{"class":3967},[3526,5507,3996],{"class":3950},[3526,5509,5510],{"class":3999},"\"-fx-font-size: 24px; -fx-font-weight: bold;\"",[3526,5512,4003],{"class":3950},[3526,5514,5515],{"class":3528,"line":4928},[3526,5516,5471],{"class":3950},[3526,5518,5519,5522,5525,5527,5529,5532,5534,5537],{"class":3528,"line":4948},[3526,5520,5521],{"class":3954},"        Button",[3526,5523,5524],{"class":3982}," loadButton",[3526,5526,4026],{"class":3950},[3526,5528,4040],{"class":4039},[3526,5530,5531],{"class":3967}," Button",[3526,5533,3996],{"class":3950},[3526,5535,5536],{"class":3999},"\"Load Tracks\"",[3526,5538,4003],{"class":3950},[3526,5540,5541,5544,5546,5549,5552,5555],{"class":3528,"line":4958},[3526,5542,5543],{"class":3982},"        loadButton",[3526,5545,3166],{"class":3950},[3526,5547,5548],{"class":3967},"setOnAction",[3526,5550,5551],{"class":3950},"(event ",[3526,5553,5554],{"class":3960},"->",[3526,5556,4402],{"class":3950},[3526,5558,5559,5562,5564,5566,5568,5570,5572,5575],{"class":3528,"line":4968},[3526,5560,5561],{"class":3982},"            System",[3526,5563,3166],{"class":3950},[3526,5565,3988],{"class":3982},[3526,5567,3166],{"class":3950},[3526,5569,3993],{"class":3967},[3526,5571,3996],{"class":3950},[3526,5573,5574],{"class":3999},"\"Load Tracks button clicked!\"",[3526,5576,4003],{"class":3950},[3526,5578,5579],{"class":3528,"line":4977},[3526,5580,5581],{"class":3950},"        });\n",[3526,5583,5585],{"class":3528,"line":5584},27,[3526,5586,5471],{"class":3950},[3526,5588,5590],{"class":3528,"line":5589},28,[3526,5591,5592],{"class":3976},"        \u002F\u002F Крок 3: Додавання компонентів до контейнера\n",[3526,5594,5596,5598,5600,5602,5604,5607],{"class":3528,"line":5595},29,[3526,5597,5434],{"class":3982},[3526,5599,3166],{"class":3950},[3526,5601,4160],{"class":3967},[3526,5603,4163],{"class":3950},[3526,5605,5606],{"class":3967},"addAll",[3526,5608,5609],{"class":3950},"(titleLabel, loadButton);\n",[3526,5611,5613],{"class":3528,"line":5612},30,[3526,5614,5471],{"class":3950},[3526,5616,5618],{"class":3528,"line":5617},31,[3526,5619,5620],{"class":3976},"        \u002F\u002F Крок 4: Створення Scene\n",[3526,5622,5624,5627,5629,5631,5633,5635,5637,5639,5641,5643],{"class":3528,"line":5623},32,[3526,5625,5626],{"class":3954},"        Scene",[3526,5628,4193],{"class":3982},[3526,5630,4026],{"class":3950},[3526,5632,4040],{"class":4039},[3526,5634,4200],{"class":3967},[3526,5636,4203],{"class":3950},[3526,5638,3763],{"class":4206},[3526,5640,3173],{"class":3950},[3526,5642,3859],{"class":4206},[3526,5644,4003],{"class":3950},[3526,5646,5648],{"class":3528,"line":5647},33,[3526,5649,5471],{"class":3950},[3526,5651,5653],{"class":3528,"line":5652},34,[3526,5654,5655],{"class":3976},"        \u002F\u002F Крок 5: Налаштування Stage\n",[3526,5657,5659,5662,5664,5666,5668,5671],{"class":3528,"line":5658},35,[3526,5660,5661],{"class":3982},"        primaryStage",[3526,5663,3166],{"class":3950},[3526,5665,4116],{"class":3967},[3526,5667,3996],{"class":3950},[3526,5669,5670],{"class":3999},"\"Audiobook Platform v1.0\"",[3526,5672,4003],{"class":3950},[3526,5674,5676,5678,5680,5682],{"class":3528,"line":5675},36,[3526,5677,5661],{"class":3982},[3526,5679,3166],{"class":3950},[3526,5681,4223],{"class":3967},[3526,5683,4226],{"class":3950},[3526,5685,5687,5689,5691,5693],{"class":3528,"line":5686},37,[3526,5688,5661],{"class":3982},[3526,5690,3166],{"class":3950},[3526,5692,4235],{"class":3967},[3526,5694,4150],{"class":3950},[3526,5696,5698],{"class":3528,"line":5697},38,[3526,5699,4449],{"class":3950},[3526,5701,5703],{"class":3528,"line":5702},39,[3526,5704,4837],{"emptyLinePlaceholder":4836},[3526,5706,5708,5710,5712,5714,5716,5718,5720,5722,5724],{"class":3528,"line":5707},40,[3526,5709,4411],{"class":3960},[3526,5711,4414],{"class":3960},[3526,5713,3964],{"class":3954},[3526,5715,4419],{"class":3967},[3526,5717,3996],{"class":3950},[3526,5719,4424],{"class":3954},[3526,5721,4427],{"class":3950},[3526,5723,4430],{"class":3982},[3526,5725,4433],{"class":3950},[3526,5727,5729,5731],{"class":3528,"line":5728},41,[3526,5730,4438],{"class":3967},[3526,5732,5733],{"class":3950},"(args);\n",[3526,5735,5737],{"class":3528,"line":5736},42,[3526,5738,4449],{"class":3950},[3526,5740,5742],{"class":3528,"line":5741},43,[3526,5743,4060],{"class":3950},[3206,5745,5747],{"id":5746},"розбір-коду-анатомія-першого-додатку","Розбір коду: Анатомія першого додатку",[3150,5749,5750,5751,5754,5755,5758],{},"Розглянемо кожен рядок цього коду, щоб зрозуміти, ",[3163,5752,5753],{},"що"," він робить та ",[3163,5756,5757],{},"чому"," саме так.",[3150,5760,5761,5764,5765,3173,5768,3173,5771,3282,5774,5777],{},[3163,5762,5763],{},"Рядок 1-6: Імпорти."," Ми імпортуємо класи з пакетів ",[3154,5766,5767],{},"javafx.application",[3154,5769,5770],{},"javafx.scene",[3154,5772,5773],{},"javafx.scene.control",[3154,5775,5776],{},"javafx.scene.layout",". Це базові пакети JavaFX, що містять класи для побудови додатків, сцен, контролів та layout-контейнерів.",[3150,5779,5780,5785,5786,5788,5789,5791,5792,5794],{},[3163,5781,5782,5783,3166],{},"Рядок 8: Успадкування від ",[3154,5784,5305],{}," Наш клас ",[3154,5787,5301],{}," успадковується від ",[3154,5790,3907],{},". Це робить його точкою входу для JavaFX Runtime. Без цього успадкування метод ",[3154,5793,4373],{}," не працюватиме.",[3150,5796,5797,5802,5803,5806],{},[3163,5798,5799,5800,3166],{},"Рядок 11: Метод ",[3154,5801,4068],{}," Це серце нашого додатку. Параметр ",[3154,5804,5805],{},"primaryStage"," — це головне вікно, яке JavaFX створила для нас автоматично. Наше завдання — наповнити його контентом.",[3150,5808,5809,5812,5813,5815,5816,5819,5820,5823,5824,5827,5828,5831],{},[3163,5810,5811],{},"Рядки 13-15: Створення Root Node."," Ми створюємо ",[3154,5814,3475],{}," — вертикальний контейнер, що розташовує дочірні елементи один під одним. Метод ",[3154,5817,5818],{},"setSpacing(10)"," встановлює відступ між елементами у 10 пікселів. Метод ",[3154,5821,5822],{},"setStyle()"," застосовує inline CSS: ",[3154,5825,5826],{},"padding"," (внутрішні відступи) та ",[3154,5829,5830],{},"alignment"," (вирівнювання по центру).",[3150,5833,5834,3689,5837,5839,5840,5843],{},[3163,5835,5836],{},"Рядки 18-19: Створення Label.",[3154,5838,3509],{}," — це текстовий елемент, що відображає статичний текст. Ми встановлюємо текст \"Audiobook Platform\" та застосовуємо стиль: розмір шрифту 24 пікселі, жирний шрифт. Зверніть увагу на префікс ",[3154,5841,5842],{},"-fx-"," у CSS-властивостях — це стандарт JavaFX.",[3150,5845,5846,3689,5849,5851,5852,5855],{},[3163,5847,5848],{},"Рядки 21-24: Створення Button.",[3154,5850,3503],{}," — це інтерактивний елемент, що реагує на клік. Метод ",[3154,5853,5854],{},"setOnAction()"," приймає обробник події — лямбда-вираз, що виконується при натисканні кнопки. У нашому випадку ми просто виводимо повідомлення у консоль. Пізніше тут буде виклик методу ViewModel для завантаження даних.",[3150,5857,5858,5861,5862,5865,5866,5868,5869,5872,5873,3282,5875,5877],{},[3163,5859,5860],{},"Рядок 27: Додавання компонентів до контейнера."," Метод ",[3154,5863,5864],{},"getChildren()"," повертає список дочірніх вузлів ",[3154,5867,3475],{},". Метод ",[3154,5870,5871],{},"addAll()"," додає ",[3154,5874,3509],{},[3154,5876,3503],{}," до цього списку. Порядок додавання визначає порядок відображення: спочатку Label (зверху), потім Button (знизу).",[3150,5879,5880,5812,5883,5885,5886,5889,5890,5892,5893,4333],{},[3163,5881,5882],{},"Рядок 30: Створення Scene.",[3154,5884,3635],{}," з кореневим вузлом ",[3154,5887,5888],{},"root"," та розмірами 400×300 пікселів. Ці розміри визначають початковий розмір вікна (користувач зможе змінити його, якщо ",[3154,5891,3675],{}," не встановлено у ",[3154,5894,5895],{},"false",[3150,5897,5898,5901,5902,5904,5905,5907,5908,5910,5911,5913,5914,5916,5917,5920],{},[3163,5899,5900],{},"Рядки 33-35: Налаштування Stage."," Ми встановлюємо заголовок вікна (",[3154,5903,4116],{},"), прив'язуємо ",[3154,5906,3635],{}," до ",[3154,5909,3632],{}," (",[3154,5912,4223],{},") та показуємо вікно (",[3154,5915,4235],{},"). Без виклику ",[3154,5918,5919],{},"show()"," вікно залишиться невидимим.",[3150,5922,5923,5928,5929,5932,5933,5935,5936,3166],{},[3163,5924,5925,5926,3166],{},"Рядки 38-40: Метод ",[3154,5927,4377],{}," Це точка входу Java-програми. Метод ",[3154,5930,5931],{},"launch(args)"," запускає JavaFX Runtime, який, у свою чергу, створює екземпляр ",[3154,5934,5301],{}," та викликає ",[3154,5937,3658],{},[3368,5939,5940,5949,6031],{},[3150,5941,5942,5948],{},[3163,5943,5944,5945,5947],{},"Чому ",[3154,5946,5854],{}," приймає лямбда-вираз?"," До Java 8 ви б писали анонімний клас:",[3518,5950,5952],{"className":3941,"code":5951,"language":3943,"meta":3522,"style":3522},"loadButton.setOnAction(new EventHandler\u003CActionEvent>() {\n    @Override\n    public void handle(ActionEvent event) {\n        System.out.println(\"Button clicked!\");\n    }\n});\n",[3154,5953,5954,5978,5984,6002,6022,6026],{"__ignoreMap":3522},[3526,5955,5956,5959,5961,5963,5965,5967,5970,5972,5975],{"class":3528,"line":3529},[3526,5957,5958],{"class":3982},"loadButton",[3526,5960,3166],{"class":3950},[3526,5962,5548],{"class":3967},[3526,5964,3996],{"class":3950},[3526,5966,4040],{"class":4039},[3526,5968,5969],{"class":3954}," EventHandler",[3526,5971,4675],{"class":3950},[3526,5973,5974],{"class":3954},"ActionEvent",[3526,5976,5977],{"class":3950},">() {\n",[3526,5979,5980,5982],{"class":3528,"line":3535},[3526,5981,4458],{"class":3950},[3526,5983,3955],{"class":3954},[3526,5985,5986,5988,5990,5993,5995,5997,6000],{"class":3528,"line":3541},[3526,5987,4411],{"class":3960},[3526,5989,3964],{"class":3954},[3526,5991,5992],{"class":3967}," handle",[3526,5994,3996],{"class":3950},[3526,5996,5974],{"class":3954},[3526,5998,5999],{"class":3982}," event",[3526,6001,4433],{"class":3950},[3526,6003,6004,6007,6009,6011,6013,6015,6017,6020],{"class":3528,"line":3547},[3526,6005,6006],{"class":3982},"        System",[3526,6008,3166],{"class":3950},[3526,6010,3988],{"class":3982},[3526,6012,3166],{"class":3950},[3526,6014,3993],{"class":3967},[3526,6016,3996],{"class":3950},[3526,6018,6019],{"class":3999},"\"Button clicked!\"",[3526,6021,4003],{"class":3950},[3526,6023,6024],{"class":3528,"line":3553},[3526,6025,4449],{"class":3950},[3526,6027,6028],{"class":3528,"line":3559},[3526,6029,6030],{"class":3950},"});\n",[3150,6032,6033,6034,6037],{},"Лямбда-вираз ",[3154,6035,6036],{},"event -> { ... }"," — це синтаксичний цукор для того самого коду. JavaFX активно використовує функціональні інтерфейси, що робить код лаконічнішим.",[3206,6039,6041],{"id":6040},"запуск-та-результат","Запуск та результат",[3150,6043,6044,6045,6048],{},"Запустіть ",[3154,6046,6047],{},"AudiobookApp.main()",". Ви побачите вікно з заголовком \"Audiobook Platform v1.0\", всередині якого — напис \"Audiobook Platform\" та кнопка \"Load Tracks\". Натисніть кнопку — у консолі з'явиться повідомлення \"Load Tracks button clicked!\".",[3150,6050,6051,6052,3173,6054,3173,6056,3173,6058,6060],{},"Вітаємо! Ви щойно створили свій перший JavaFX-додаток. Він простий, але містить всі ключові елементи: ",[3154,6053,5305],{},[3154,6055,3632],{},[3154,6057,3635],{},[3154,6059,3638],{},", контроли та обробник подій.",[3739,6062,3747,3747,6065,3747,3747,6072,3747,6077,3747,3747,6084,3747,6091,3747,6096,3747,3747,6101,3747,3747,6126,3747,6131,3747,6137,3747,6144],{"viewBox":6063,"className":6064,"xmlns":3746},"0 0 800 500",[3743,3744,3745],[3749,6066],{"x":6067,"y":3774,"width":6068,"height":6069,"rx":3754,"className":6070,"strokeWidth":3784},"150","500","360",[6071,3757,3758,3759],"fill-white",[3749,6073],{"x":6067,"y":3774,"width":6068,"height":3764,"rx":3754,"className":6074},[6075,6076],"fill-slate-200","dark:fill-slate-700",[3735,6078,6083],{"x":3763,"y":6079,"textAnchor":3765,"className":6080},"103",[3767,3792,6081,6082],"fill-slate-700","dark:fill-slate-200","Audiobook Platform v1.0",[6085,6086],"circle",{"cx":6087,"cy":6088,"r":3778,"className":6089},"168","97",[6090],"fill-red-400",[6085,6092],{"cx":6093,"cy":6088,"r":3778,"className":6094},"188",[6095],"fill-yellow-400",[6085,6097],{"cx":6098,"cy":6088,"r":3778,"className":6099},"208",[6100],"fill-green-400",[6102,6103,3864,3864,6105,3864,3864,6114,3864,6120,3747],"g",{"transform":6104},"translate(400, 250)",[3735,6106,6113],{"x":6107,"y":6108,"textAnchor":3765,"className":6109},"0","-40",[6110,3768,6111,6112],"text-2xl","fill-slate-800","dark:fill-slate-100","Audiobook Platform",[3749,6115],{"x":6116,"y":6107,"width":3847,"height":3819,"rx":3778,"className":6117,"strokeWidth":3824},"-80",[3877,6118,6119],"hover:fill-blue-600","stroke-blue-600",[3735,6121,6125],{"x":6107,"y":6122,"textAnchor":3765,"className":6123},"27",[6124,3792,6071],"text-base","Load Tracks",[3528,6127],{"x1":3763,"y1":6128,"x2":6068,"y2":3847,"className":6129,"strokeWidth":3824,"strokeDashArray":6130},"210",[3806,3807],[3760,3760],[3735,6132,6136],{"x":6133,"y":6134,"className":6135},"510","155",[3791,3812,3813],"Label: заголовок",[3528,6138],{"x1":3763,"y1":6139,"x2":6068,"y2":6140,"className":6141,"strokeWidth":3824,"strokeDashArray":6143},"290","340",[3890,6142],"dark:stroke-blue-400",[3760,3760],[3735,6145,6150],{"x":6133,"y":6146,"className":6147},"345",[3791,6148,6149],"fill-blue-700","dark:fill-blue-300","Button: інтерактивний елемент",[3375,6152],{},[3145,6154,6156],{"id":6155},"layout-контейнери-організація-простору","Layout-контейнери: Організація простору",[3150,6158,6159,6160,6162],{},"У попередньому прикладі ми використали ",[3154,6161,3475],{}," — найпростіший layout-контейнер, що розташовує елементи вертикально. Але JavaFX пропонує набагато більше можливостей для організації UI. Розуміння layout-контейнерів — це ключ до побудови складних, адаптивних інтерфейсів.",[3206,6164,6166],{"id":6165},"чому-не-абсолютне-позиціонування","Чому не абсолютне позиціонування?",[3150,6168,6169],{},"У деяких UI-фреймворках (наприклад, у Windows Forms) ви можете встановлювати абсолютні координати для кожного елемента: \"Кнопка на позиції (100, 50), розміром 80×30 пікселів\". Це просто для маленьких додатків, але стає кошмаром при масштабуванні:",[6171,6172,6173,6179,6185,6191],"ul",{},[4501,6174,6175,6178],{},[3163,6176,6177],{},"Різні розміри екранів."," Те, що виглядає добре на екрані 1920×1080, може бути зламаним на 1366×768.",[4501,6180,6181,6184],{},[3163,6182,6183],{},"Зміна розміру вікна."," Користувач розтягує вікно — елементи залишаються на місці, з'являються порожні простори.",[4501,6186,6187,6190],{},[3163,6188,6189],{},"Локалізація."," Текст німецькою мовою довший за англійський — кнопка обрізає текст.",[4501,6192,6193,6196],{},[3163,6194,6195],{},"Підтримка."," Додавання нового елемента вимагає перерахунку позицій усіх інших.",[3150,6198,6199,6200,6203,6204,6207],{},"JavaFX вирішує ці проблеми через ",[3163,6201,6202],{},"layout-контейнери"," — компоненти, що автоматично розташовують дочірні елементи за певними правилами. Ви описуєте ",[3163,6205,6206],{},"логіку розташування"," (вертикально, горизонтально, у таблиці), а контейнер сам обчислює координати та розміри.",[3206,6209,6211],{"id":6210},"vbox-та-hbox-лінійне-розташування","VBox та HBox: Лінійне розташування",[3150,6213,6214,6217,6218,6221,6222,3651,6225,6228],{},[3163,6215,6216],{},"VBox (Vertical Box)"," розташовує дочірні елементи ",[3163,6219,6220],{},"вертикально",", один під одним. ",[3163,6223,6224],{},"HBox (Horizontal Box)",[3163,6226,6227],{},"горизонтально",", один поруч з одним. Це найпростіші та найчастіше використовувані контейнери.",[3150,6230,6231],{},[3163,6232,6233],{},"Основні властивості VBox\u002FHBox:",[6171,6235,6236,6244,6261,6268],{},[4501,6237,6238,6243],{},[3163,6239,6240],{},[3154,6241,6242],{},"spacing"," — відступ між елементами (у пікселях).",[4501,6245,6246,6250,6251,3173,6254,3173,6257,6260],{},[3163,6247,6248],{},[3154,6249,5830],{}," — вирівнювання елементів: ",[3154,6252,6253],{},"TOP_LEFT",[3154,6255,6256],{},"CENTER",[3154,6258,6259],{},"BOTTOM_RIGHT",", тощо.",[4501,6262,6263,6267],{},[3163,6264,6265],{},[3154,6266,5826],{}," — внутрішні відступи контейнера (відстань від країв до дочірніх елементів).",[4501,6269,6270,6280],{},[3163,6271,6272,6275,6276,6279],{},[3154,6273,6274],{},"fillWidth"," (VBox) \u002F ",[3154,6277,6278],{},"fillHeight"," (HBox)"," — чи розтягувати дочірні елементи на всю ширину\u002Fвисоту.",[3150,6282,6283],{},"Приклад: панель інструментів з кнопками (HBox):",[3518,6285,6287],{"className":3941,"code":6286,"language":3943,"meta":3522,"style":3522},"HBox toolbar = new HBox();\ntoolbar.setSpacing(10);\ntoolbar.setPadding(new Insets(10)); \u002F\u002F Відступи: 10px з усіх боків\ntoolbar.setAlignment(Pos.CENTER_LEFT);\n\nButton addButton = new Button(\"Add\");\nButton editButton = new Button(\"Edit\");\nButton deleteButton = new Button(\"Delete\");\n\ntoolbar.getChildren().addAll(addButton, editButton, deleteButton);\n",[3154,6288,6289,6305,6320,6346,6367,6371,6391,6411,6431,6435],{"__ignoreMap":3522},[3526,6290,6291,6293,6296,6298,6300,6303],{"class":3528,"line":3529},[3526,6292,3478],{"class":3954},[3526,6294,6295],{"class":3982}," toolbar",[3526,6297,4026],{"class":3950},[3526,6299,4040],{"class":4039},[3526,6301,6302],{"class":3967}," HBox",[3526,6304,4150],{"class":3950},[3526,6306,6307,6310,6312,6314,6316,6318],{"class":3528,"line":3535},[3526,6308,6309],{"class":3982},"toolbar",[3526,6311,3166],{"class":3950},[3526,6313,5439],{"class":3967},[3526,6315,3996],{"class":3950},[3526,6317,5444],{"class":4206},[3526,6319,4003],{"class":3950},[3526,6321,6322,6324,6326,6329,6331,6333,6336,6338,6340,6343],{"class":3528,"line":3541},[3526,6323,6309],{"class":3982},[3526,6325,3166],{"class":3950},[3526,6327,6328],{"class":3967},"setPadding",[3526,6330,3996],{"class":3950},[3526,6332,4040],{"class":4039},[3526,6334,6335],{"class":3967}," Insets",[3526,6337,3996],{"class":3950},[3526,6339,5444],{"class":4206},[3526,6341,6342],{"class":3950},")); ",[3526,6344,6345],{"class":3976},"\u002F\u002F Відступи: 10px з усіх боків\n",[3526,6347,6348,6350,6352,6355,6357,6360,6362,6365],{"class":3528,"line":3547},[3526,6349,6309],{"class":3982},[3526,6351,3166],{"class":3950},[3526,6353,6354],{"class":3967},"setAlignment",[3526,6356,3996],{"class":3950},[3526,6358,6359],{"class":3982},"Pos",[3526,6361,3166],{"class":3950},[3526,6363,6364],{"class":3982},"CENTER_LEFT",[3526,6366,4003],{"class":3950},[3526,6368,6369],{"class":3528,"line":3553},[3526,6370,4837],{"emptyLinePlaceholder":4836},[3526,6372,6373,6375,6378,6380,6382,6384,6386,6389],{"class":3528,"line":3559},[3526,6374,3503],{"class":3954},[3526,6376,6377],{"class":3982}," addButton",[3526,6379,4026],{"class":3950},[3526,6381,4040],{"class":4039},[3526,6383,5531],{"class":3967},[3526,6385,3996],{"class":3950},[3526,6387,6388],{"class":3999},"\"Add\"",[3526,6390,4003],{"class":3950},[3526,6392,6393,6395,6398,6400,6402,6404,6406,6409],{"class":3528,"line":3565},[3526,6394,3503],{"class":3954},[3526,6396,6397],{"class":3982}," editButton",[3526,6399,4026],{"class":3950},[3526,6401,4040],{"class":4039},[3526,6403,5531],{"class":3967},[3526,6405,3996],{"class":3950},[3526,6407,6408],{"class":3999},"\"Edit\"",[3526,6410,4003],{"class":3950},[3526,6412,6413,6415,6418,6420,6422,6424,6426,6429],{"class":3528,"line":3571},[3526,6414,3503],{"class":3954},[3526,6416,6417],{"class":3982}," deleteButton",[3526,6419,4026],{"class":3950},[3526,6421,4040],{"class":4039},[3526,6423,5531],{"class":3967},[3526,6425,3996],{"class":3950},[3526,6427,6428],{"class":3999},"\"Delete\"",[3526,6430,4003],{"class":3950},[3526,6432,6433],{"class":3528,"line":3577},[3526,6434,4837],{"emptyLinePlaceholder":4836},[3526,6436,6437,6439,6441,6443,6445,6447],{"class":3528,"line":3583},[3526,6438,6309],{"class":3982},[3526,6440,3166],{"class":3950},[3526,6442,4160],{"class":3967},[3526,6444,4163],{"class":3950},[3526,6446,5606],{"class":3967},[3526,6448,6449],{"class":3950},"(addButton, editButton, deleteButton);\n",[3150,6451,6452],{},"Результат: три кнопки розташовані горизонтально, з відступом 10 пікселів між ними, вирівняні по лівому краю.",[6454,6455,6456,6617],"tabs",{},[6457,6458,6461,6611],"tabs-item",{"icon":6459,"label":6460},"i-heroicons-arrows-up-down","VBox (вертикально)",[3518,6462,6464],{"className":3941,"code":6463,"language":3943,"meta":3522,"style":3522},"VBox vbox = new VBox(10); \u002F\u002F spacing = 10\nvbox.setPadding(new Insets(20));\nvbox.setAlignment(Pos.CENTER);\n\nvbox.getChildren().addAll(\n    new Label(\"Name:\"),\n    new TextField(),\n    new Label(\"Email:\"),\n    new TextField(),\n    new Button(\"Submit\")\n);\n",[3154,6465,6466,6488,6510,6528,6532,6547,6562,6572,6585,6593,6607],{"__ignoreMap":3522},[3526,6467,6468,6470,6473,6475,6477,6479,6481,6483,6485],{"class":3528,"line":3529},[3526,6469,3475],{"class":3954},[3526,6471,6472],{"class":3982}," vbox",[3526,6474,4026],{"class":3950},[3526,6476,4040],{"class":4039},[3526,6478,4147],{"class":3967},[3526,6480,3996],{"class":3950},[3526,6482,5444],{"class":4206},[3526,6484,5447],{"class":3950},[3526,6486,6487],{"class":3976},"\u002F\u002F spacing = 10\n",[3526,6489,6490,6493,6495,6497,6499,6501,6503,6505,6508],{"class":3528,"line":3535},[3526,6491,6492],{"class":3982},"vbox",[3526,6494,3166],{"class":3950},[3526,6496,6328],{"class":3967},[3526,6498,3996],{"class":3950},[3526,6500,4040],{"class":4039},[3526,6502,6335],{"class":3967},[3526,6504,3996],{"class":3950},[3526,6506,6507],{"class":4206},"20",[3526,6509,4181],{"class":3950},[3526,6511,6512,6514,6516,6518,6520,6522,6524,6526],{"class":3528,"line":3541},[3526,6513,6492],{"class":3982},[3526,6515,3166],{"class":3950},[3526,6517,6354],{"class":3967},[3526,6519,3996],{"class":3950},[3526,6521,6359],{"class":3982},[3526,6523,3166],{"class":3950},[3526,6525,6256],{"class":3982},[3526,6527,4003],{"class":3950},[3526,6529,6530],{"class":3528,"line":3547},[3526,6531,4837],{"emptyLinePlaceholder":4836},[3526,6533,6534,6536,6538,6540,6542,6544],{"class":3528,"line":3553},[3526,6535,6492],{"class":3982},[3526,6537,3166],{"class":3950},[3526,6539,4160],{"class":3967},[3526,6541,4163],{"class":3950},[3526,6543,5606],{"class":3967},[3526,6545,6546],{"class":3950},"(\n",[3526,6548,6549,6552,6554,6556,6559],{"class":3528,"line":3559},[3526,6550,6551],{"class":4039},"    new",[3526,6553,4173],{"class":3967},[3526,6555,3996],{"class":3950},[3526,6557,6558],{"class":3999},"\"Name:\"",[3526,6560,6561],{"class":3950},"),\n",[3526,6563,6564,6566,6569],{"class":3528,"line":3565},[3526,6565,6551],{"class":4039},[3526,6567,6568],{"class":3967}," TextField",[3526,6570,6571],{"class":3950},"(),\n",[3526,6573,6574,6576,6578,6580,6583],{"class":3528,"line":3571},[3526,6575,6551],{"class":4039},[3526,6577,4173],{"class":3967},[3526,6579,3996],{"class":3950},[3526,6581,6582],{"class":3999},"\"Email:\"",[3526,6584,6561],{"class":3950},[3526,6586,6587,6589,6591],{"class":3528,"line":3577},[3526,6588,6551],{"class":4039},[3526,6590,6568],{"class":3967},[3526,6592,6571],{"class":3950},[3526,6594,6595,6597,6599,6601,6604],{"class":3528,"line":3583},[3526,6596,6551],{"class":4039},[3526,6598,5531],{"class":3967},[3526,6600,3996],{"class":3950},[3526,6602,6603],{"class":3999},"\"Submit\"",[3526,6605,6606],{"class":3950},")\n",[3526,6608,6609],{"class":3528,"line":3589},[3526,6610,4003],{"class":3950},[3150,6612,6613,6616],{},[3163,6614,6615],{},"Результат:"," Label → TextField → Label → TextField → Button (вертикально).",[6457,6618,6621,6747],{"icon":6619,"label":6620},"i-heroicons-arrows-right-left","HBox (горизонтально)",[3518,6622,6624],{"className":3941,"code":6623,"language":3943,"meta":3522,"style":3522},"HBox hbox = new HBox(10); \u002F\u002F spacing = 10\nhbox.setPadding(new Insets(20));\nhbox.setAlignment(Pos.CENTER);\n\nhbox.getChildren().addAll(\n    new Button(\"Previous\"),\n    new Label(\"Page 1 of 10\"),\n    new Button(\"Next\")\n);\n",[3154,6625,6626,6647,6668,6686,6690,6704,6717,6730,6743],{"__ignoreMap":3522},[3526,6627,6628,6630,6633,6635,6637,6639,6641,6643,6645],{"class":3528,"line":3529},[3526,6629,3478],{"class":3954},[3526,6631,6632],{"class":3982}," hbox",[3526,6634,4026],{"class":3950},[3526,6636,4040],{"class":4039},[3526,6638,6302],{"class":3967},[3526,6640,3996],{"class":3950},[3526,6642,5444],{"class":4206},[3526,6644,5447],{"class":3950},[3526,6646,6487],{"class":3976},[3526,6648,6649,6652,6654,6656,6658,6660,6662,6664,6666],{"class":3528,"line":3535},[3526,6650,6651],{"class":3982},"hbox",[3526,6653,3166],{"class":3950},[3526,6655,6328],{"class":3967},[3526,6657,3996],{"class":3950},[3526,6659,4040],{"class":4039},[3526,6661,6335],{"class":3967},[3526,6663,3996],{"class":3950},[3526,6665,6507],{"class":4206},[3526,6667,4181],{"class":3950},[3526,6669,6670,6672,6674,6676,6678,6680,6682,6684],{"class":3528,"line":3541},[3526,6671,6651],{"class":3982},[3526,6673,3166],{"class":3950},[3526,6675,6354],{"class":3967},[3526,6677,3996],{"class":3950},[3526,6679,6359],{"class":3982},[3526,6681,3166],{"class":3950},[3526,6683,6256],{"class":3982},[3526,6685,4003],{"class":3950},[3526,6687,6688],{"class":3528,"line":3547},[3526,6689,4837],{"emptyLinePlaceholder":4836},[3526,6691,6692,6694,6696,6698,6700,6702],{"class":3528,"line":3553},[3526,6693,6651],{"class":3982},[3526,6695,3166],{"class":3950},[3526,6697,4160],{"class":3967},[3526,6699,4163],{"class":3950},[3526,6701,5606],{"class":3967},[3526,6703,6546],{"class":3950},[3526,6705,6706,6708,6710,6712,6715],{"class":3528,"line":3559},[3526,6707,6551],{"class":4039},[3526,6709,5531],{"class":3967},[3526,6711,3996],{"class":3950},[3526,6713,6714],{"class":3999},"\"Previous\"",[3526,6716,6561],{"class":3950},[3526,6718,6719,6721,6723,6725,6728],{"class":3528,"line":3565},[3526,6720,6551],{"class":4039},[3526,6722,4173],{"class":3967},[3526,6724,3996],{"class":3950},[3526,6726,6727],{"class":3999},"\"Page 1 of 10\"",[3526,6729,6561],{"class":3950},[3526,6731,6732,6734,6736,6738,6741],{"class":3528,"line":3571},[3526,6733,6551],{"class":4039},[3526,6735,5531],{"class":3967},[3526,6737,3996],{"class":3950},[3526,6739,6740],{"class":3999},"\"Next\"",[3526,6742,6606],{"class":3950},[3526,6744,6745],{"class":3528,"line":3577},[3526,6746,4003],{"class":3950},[3150,6748,6749,6751],{},[3163,6750,6615],{}," Button ← Label ← Button (горизонтально).",[3206,6753,6755],{"id":6754},"borderpane-пять-зон-для-класичного-layout","BorderPane: П'ять зон для класичного layout",[3150,6757,6758,6760,6761,6764,6765,3173,6768,3173,6771,3173,6774,3282,6777,6780],{},[3163,6759,3481],{}," — це один з найпопулярніших контейнерів для побудови додатків. Він ділить простір на ",[3163,6762,6763],{},"п'ять зон",": ",[3154,6766,6767],{},"top",[3154,6769,6770],{},"bottom",[3154,6772,6773],{},"left",[3154,6775,6776],{},"right",[3154,6778,6779],{},"center",". Кожна зона може містити один вузол (зазвичай це інший контейнер, що містить кілька елементів).",[3150,6782,6783],{},"Це класичний layout для desktop-додатків:",[6171,6785,6786,6792,6798,6804,6810],{},[4501,6787,6788,6791],{},[3163,6789,6790],{},"Top"," — меню або панель інструментів.",[4501,6793,6794,6797],{},[3163,6795,6796],{},"Left"," — бічна панель навігації або дерево категорій.",[4501,6799,6800,6803],{},[3163,6801,6802],{},"Center"," — основний контент (таблиця, форма, текстовий редактор).",[4501,6805,6806,6809],{},[3163,6807,6808],{},"Right"," — панель властивостей або додаткова інформація.",[4501,6811,6812,6815],{},[3163,6813,6814],{},"Bottom"," — статус-бар або панель прогресу.",[3518,6817,6819],{"className":3941,"code":6818,"language":3943,"meta":3522,"style":3522},"BorderPane borderPane = new BorderPane();\n\n\u002F\u002F Top: меню\nHBox menuBar = new HBox(10);\nmenuBar.getChildren().addAll(\n    new Button(\"File\"),\n    new Button(\"Edit\"),\n    new Button(\"View\")\n);\nborderPane.setTop(menuBar);\n\n\u002F\u002F Left: навігація\nVBox navigation = new VBox(5);\nnavigation.getChildren().addAll(\n    new Button(\"Audiobooks\"),\n    new Button(\"Authors\"),\n    new Button(\"Genres\")\n);\nborderPane.setLeft(navigation);\n\n\u002F\u002F Center: основний контент\nLabel content = new Label(\"Main content area\");\nborderPane.setCenter(content);\n\n\u002F\u002F Bottom: статус-бар\nLabel statusBar = new Label(\"Ready\");\nborderPane.setBottom(statusBar);\n",[3154,6820,6821,6837,6841,6846,6865,6880,6893,6905,6918,6922,6935,6939,6944,6963,6978,6991,7004,7017,7021,7033,7037,7042,7062,7074,7078,7083,7103],{"__ignoreMap":3522},[3526,6822,6823,6825,6828,6830,6832,6835],{"class":3528,"line":3529},[3526,6824,3481],{"class":3954},[3526,6826,6827],{"class":3982}," borderPane",[3526,6829,4026],{"class":3950},[3526,6831,4040],{"class":4039},[3526,6833,6834],{"class":3967}," BorderPane",[3526,6836,4150],{"class":3950},[3526,6838,6839],{"class":3528,"line":3535},[3526,6840,4837],{"emptyLinePlaceholder":4836},[3526,6842,6843],{"class":3528,"line":3541},[3526,6844,6845],{"class":3976},"\u002F\u002F Top: меню\n",[3526,6847,6848,6850,6853,6855,6857,6859,6861,6863],{"class":3528,"line":3547},[3526,6849,3478],{"class":3954},[3526,6851,6852],{"class":3982}," menuBar",[3526,6854,4026],{"class":3950},[3526,6856,4040],{"class":4039},[3526,6858,6302],{"class":3967},[3526,6860,3996],{"class":3950},[3526,6862,5444],{"class":4206},[3526,6864,4003],{"class":3950},[3526,6866,6867,6870,6872,6874,6876,6878],{"class":3528,"line":3553},[3526,6868,6869],{"class":3982},"menuBar",[3526,6871,3166],{"class":3950},[3526,6873,4160],{"class":3967},[3526,6875,4163],{"class":3950},[3526,6877,5606],{"class":3967},[3526,6879,6546],{"class":3950},[3526,6881,6882,6884,6886,6888,6891],{"class":3528,"line":3559},[3526,6883,6551],{"class":4039},[3526,6885,5531],{"class":3967},[3526,6887,3996],{"class":3950},[3526,6889,6890],{"class":3999},"\"File\"",[3526,6892,6561],{"class":3950},[3526,6894,6895,6897,6899,6901,6903],{"class":3528,"line":3565},[3526,6896,6551],{"class":4039},[3526,6898,5531],{"class":3967},[3526,6900,3996],{"class":3950},[3526,6902,6408],{"class":3999},[3526,6904,6561],{"class":3950},[3526,6906,6907,6909,6911,6913,6916],{"class":3528,"line":3571},[3526,6908,6551],{"class":4039},[3526,6910,5531],{"class":3967},[3526,6912,3996],{"class":3950},[3526,6914,6915],{"class":3999},"\"View\"",[3526,6917,6606],{"class":3950},[3526,6919,6920],{"class":3528,"line":3577},[3526,6921,4003],{"class":3950},[3526,6923,6924,6927,6929,6932],{"class":3528,"line":3583},[3526,6925,6926],{"class":3982},"borderPane",[3526,6928,3166],{"class":3950},[3526,6930,6931],{"class":3967},"setTop",[3526,6933,6934],{"class":3950},"(menuBar);\n",[3526,6936,6937],{"class":3528,"line":3589},[3526,6938,4837],{"emptyLinePlaceholder":4836},[3526,6940,6941],{"class":3528,"line":3595},[3526,6942,6943],{"class":3976},"\u002F\u002F Left: навігація\n",[3526,6945,6946,6948,6951,6953,6955,6957,6959,6961],{"class":3528,"line":3601},[3526,6947,3475],{"class":3954},[3526,6949,6950],{"class":3982}," navigation",[3526,6952,4026],{"class":3950},[3526,6954,4040],{"class":4039},[3526,6956,4147],{"class":3967},[3526,6958,3996],{"class":3950},[3526,6960,3786],{"class":4206},[3526,6962,4003],{"class":3950},[3526,6964,6965,6968,6970,6972,6974,6976],{"class":3528,"line":3607},[3526,6966,6967],{"class":3982},"navigation",[3526,6969,3166],{"class":3950},[3526,6971,4160],{"class":3967},[3526,6973,4163],{"class":3950},[3526,6975,5606],{"class":3967},[3526,6977,6546],{"class":3950},[3526,6979,6980,6982,6984,6986,6989],{"class":3528,"line":3613},[3526,6981,6551],{"class":4039},[3526,6983,5531],{"class":3967},[3526,6985,3996],{"class":3950},[3526,6987,6988],{"class":3999},"\"Audiobooks\"",[3526,6990,6561],{"class":3950},[3526,6992,6993,6995,6997,6999,7002],{"class":3528,"line":3619},[3526,6994,6551],{"class":4039},[3526,6996,5531],{"class":3967},[3526,6998,3996],{"class":3950},[3526,7000,7001],{"class":3999},"\"Authors\"",[3526,7003,6561],{"class":3950},[3526,7005,7006,7008,7010,7012,7015],{"class":3528,"line":4612},[3526,7007,6551],{"class":4039},[3526,7009,5531],{"class":3967},[3526,7011,3996],{"class":3950},[3526,7013,7014],{"class":3999},"\"Genres\"",[3526,7016,6606],{"class":3950},[3526,7018,7019],{"class":3528,"line":4618},[3526,7020,4003],{"class":3950},[3526,7022,7023,7025,7027,7030],{"class":3528,"line":4624},[3526,7024,6926],{"class":3982},[3526,7026,3166],{"class":3950},[3526,7028,7029],{"class":3967},"setLeft",[3526,7031,7032],{"class":3950},"(navigation);\n",[3526,7034,7035],{"class":3528,"line":4630},[3526,7036,4837],{"emptyLinePlaceholder":4836},[3526,7038,7039],{"class":3528,"line":4918},[3526,7040,7041],{"class":3976},"\u002F\u002F Center: основний контент\n",[3526,7043,7044,7046,7049,7051,7053,7055,7057,7060],{"class":3528,"line":4928},[3526,7045,3509],{"class":3954},[3526,7047,7048],{"class":3982}," content",[3526,7050,4026],{"class":3950},[3526,7052,4040],{"class":4039},[3526,7054,4173],{"class":3967},[3526,7056,3996],{"class":3950},[3526,7058,7059],{"class":3999},"\"Main content area\"",[3526,7061,4003],{"class":3950},[3526,7063,7064,7066,7068,7071],{"class":3528,"line":4948},[3526,7065,6926],{"class":3982},[3526,7067,3166],{"class":3950},[3526,7069,7070],{"class":3967},"setCenter",[3526,7072,7073],{"class":3950},"(content);\n",[3526,7075,7076],{"class":3528,"line":4958},[3526,7077,4837],{"emptyLinePlaceholder":4836},[3526,7079,7080],{"class":3528,"line":4968},[3526,7081,7082],{"class":3976},"\u002F\u002F Bottom: статус-бар\n",[3526,7084,7085,7087,7090,7092,7094,7096,7098,7101],{"class":3528,"line":4977},[3526,7086,3509],{"class":3954},[3526,7088,7089],{"class":3982}," statusBar",[3526,7091,4026],{"class":3950},[3526,7093,4040],{"class":4039},[3526,7095,4173],{"class":3967},[3526,7097,3996],{"class":3950},[3526,7099,7100],{"class":3999},"\"Ready\"",[3526,7102,4003],{"class":3950},[3526,7104,7105,7107,7109,7112],{"class":3528,"line":5584},[3526,7106,6926],{"class":3982},[3526,7108,3166],{"class":3950},[3526,7110,7111],{"class":3967},"setBottom",[3526,7113,7114],{"class":3950},"(statusBar);\n",[3150,7116,7117],{},[3163,7118,7119],{},"Поведінка розмірів:",[6171,7121,7122,7128,7134],{},[4501,7123,7124,7127],{},[3163,7125,7126],{},"Top та Bottom"," розтягуються на всю ширину, висота визначається контентом.",[4501,7129,7130,7133],{},[3163,7131,7132],{},"Left та Right"," розтягуються на всю висоту (мінус top\u002Fbottom), ширина визначається контентом.",[4501,7135,7136,7138],{},[3163,7137,6802],{}," займає весь залишковий простір.",[3189,7140,7141,7143,7144,7146,7147,7149],{},[3163,7142,3481],{}," не вимагає заповнення всіх зон. Якщо ви не встановите ",[3154,7145,6773],{},", ця зона просто не буде відображатися, а ",[3154,7148,6779],{}," розшириться на її місце.",[3206,7151,7153],{"id":7152},"gridpane-табличне-розташування","GridPane: Табличне розташування",[3150,7155,7156,7158,7159,7162],{},[3163,7157,3484],{}," організовує елементи у ",[3163,7160,7161],{},"таблицю"," з рядками та колонками. Це ідеальний вибір для форм, де кожен рядок містить Label (назва поля) та TextField (введення).",[3518,7164,7166],{"className":3941,"code":7165,"language":3943,"meta":3522,"style":3522},"GridPane grid = new GridPane();\ngrid.setHgap(10); \u002F\u002F Горизонтальний відступ між колонками\ngrid.setVgap(10); \u002F\u002F Вертикальний відступ між рядками\ngrid.setPadding(new Insets(20));\n\n\u002F\u002F Додавання елементів: (node, columnIndex, rowIndex)\ngrid.add(new Label(\"Title:\"), 0, 0);\ngrid.add(new TextField(), 1, 0);\n\ngrid.add(new Label(\"Author:\"), 0, 1);\ngrid.add(new TextField(), 1, 1);\n\ngrid.add(new Label(\"Duration:\"), 0, 2);\ngrid.add(new TextField(), 1, 2);\n\nButton saveButton = new Button(\"Save\");\ngrid.add(saveButton, 1, 3); \u002F\u002F Колонка 1, рядок 3\n",[3154,7167,7168,7184,7203,7221,7241,7245,7250,7280,7305,7309,7338,7362,7366,7395,7419,7423,7443],{"__ignoreMap":3522},[3526,7169,7170,7172,7175,7177,7179,7182],{"class":3528,"line":3529},[3526,7171,3484],{"class":3954},[3526,7173,7174],{"class":3982}," grid",[3526,7176,4026],{"class":3950},[3526,7178,4040],{"class":4039},[3526,7180,7181],{"class":3967}," GridPane",[3526,7183,4150],{"class":3950},[3526,7185,7186,7189,7191,7194,7196,7198,7200],{"class":3528,"line":3535},[3526,7187,7188],{"class":3982},"grid",[3526,7190,3166],{"class":3950},[3526,7192,7193],{"class":3967},"setHgap",[3526,7195,3996],{"class":3950},[3526,7197,5444],{"class":4206},[3526,7199,5447],{"class":3950},[3526,7201,7202],{"class":3976},"\u002F\u002F Горизонтальний відступ між колонками\n",[3526,7204,7205,7207,7209,7212,7214,7216,7218],{"class":3528,"line":3541},[3526,7206,7188],{"class":3982},[3526,7208,3166],{"class":3950},[3526,7210,7211],{"class":3967},"setVgap",[3526,7213,3996],{"class":3950},[3526,7215,5444],{"class":4206},[3526,7217,5447],{"class":3950},[3526,7219,7220],{"class":3976},"\u002F\u002F Вертикальний відступ між рядками\n",[3526,7222,7223,7225,7227,7229,7231,7233,7235,7237,7239],{"class":3528,"line":3547},[3526,7224,7188],{"class":3982},[3526,7226,3166],{"class":3950},[3526,7228,6328],{"class":3967},[3526,7230,3996],{"class":3950},[3526,7232,4040],{"class":4039},[3526,7234,6335],{"class":3967},[3526,7236,3996],{"class":3950},[3526,7238,6507],{"class":4206},[3526,7240,4181],{"class":3950},[3526,7242,7243],{"class":3528,"line":3553},[3526,7244,4837],{"emptyLinePlaceholder":4836},[3526,7246,7247],{"class":3528,"line":3559},[3526,7248,7249],{"class":3976},"\u002F\u002F Додавання елементів: (node, columnIndex, rowIndex)\n",[3526,7251,7252,7254,7256,7258,7260,7262,7264,7266,7269,7272,7274,7276,7278],{"class":3528,"line":3565},[3526,7253,7188],{"class":3982},[3526,7255,3166],{"class":3950},[3526,7257,4166],{"class":3967},[3526,7259,3996],{"class":3950},[3526,7261,4040],{"class":4039},[3526,7263,4173],{"class":3967},[3526,7265,3996],{"class":3950},[3526,7267,7268],{"class":3999},"\"Title:\"",[3526,7270,7271],{"class":3950},"), ",[3526,7273,6107],{"class":4206},[3526,7275,3173],{"class":3950},[3526,7277,6107],{"class":4206},[3526,7279,4003],{"class":3950},[3526,7281,7282,7284,7286,7288,7290,7292,7294,7297,7299,7301,7303],{"class":3528,"line":3571},[3526,7283,7188],{"class":3982},[3526,7285,3166],{"class":3950},[3526,7287,4166],{"class":3967},[3526,7289,3996],{"class":3950},[3526,7291,4040],{"class":4039},[3526,7293,6568],{"class":3967},[3526,7295,7296],{"class":3950},"(), ",[3526,7298,3852],{"class":4206},[3526,7300,3173],{"class":3950},[3526,7302,6107],{"class":4206},[3526,7304,4003],{"class":3950},[3526,7306,7307],{"class":3528,"line":3577},[3526,7308,4837],{"emptyLinePlaceholder":4836},[3526,7310,7311,7313,7315,7317,7319,7321,7323,7325,7328,7330,7332,7334,7336],{"class":3528,"line":3583},[3526,7312,7188],{"class":3982},[3526,7314,3166],{"class":3950},[3526,7316,4166],{"class":3967},[3526,7318,3996],{"class":3950},[3526,7320,4040],{"class":4039},[3526,7322,4173],{"class":3967},[3526,7324,3996],{"class":3950},[3526,7326,7327],{"class":3999},"\"Author:\"",[3526,7329,7271],{"class":3950},[3526,7331,6107],{"class":4206},[3526,7333,3173],{"class":3950},[3526,7335,3852],{"class":4206},[3526,7337,4003],{"class":3950},[3526,7339,7340,7342,7344,7346,7348,7350,7352,7354,7356,7358,7360],{"class":3528,"line":3589},[3526,7341,7188],{"class":3982},[3526,7343,3166],{"class":3950},[3526,7345,4166],{"class":3967},[3526,7347,3996],{"class":3950},[3526,7349,4040],{"class":4039},[3526,7351,6568],{"class":3967},[3526,7353,7296],{"class":3950},[3526,7355,3852],{"class":4206},[3526,7357,3173],{"class":3950},[3526,7359,3852],{"class":4206},[3526,7361,4003],{"class":3950},[3526,7363,7364],{"class":3528,"line":3595},[3526,7365,4837],{"emptyLinePlaceholder":4836},[3526,7367,7368,7370,7372,7374,7376,7378,7380,7382,7385,7387,7389,7391,7393],{"class":3528,"line":3601},[3526,7369,7188],{"class":3982},[3526,7371,3166],{"class":3950},[3526,7373,4166],{"class":3967},[3526,7375,3996],{"class":3950},[3526,7377,4040],{"class":4039},[3526,7379,4173],{"class":3967},[3526,7381,3996],{"class":3950},[3526,7383,7384],{"class":3999},"\"Duration:\"",[3526,7386,7271],{"class":3950},[3526,7388,6107],{"class":4206},[3526,7390,3173],{"class":3950},[3526,7392,3784],{"class":4206},[3526,7394,4003],{"class":3950},[3526,7396,7397,7399,7401,7403,7405,7407,7409,7411,7413,7415,7417],{"class":3528,"line":3607},[3526,7398,7188],{"class":3982},[3526,7400,3166],{"class":3950},[3526,7402,4166],{"class":3967},[3526,7404,3996],{"class":3950},[3526,7406,4040],{"class":4039},[3526,7408,6568],{"class":3967},[3526,7410,7296],{"class":3950},[3526,7412,3852],{"class":4206},[3526,7414,3173],{"class":3950},[3526,7416,3784],{"class":4206},[3526,7418,4003],{"class":3950},[3526,7420,7421],{"class":3528,"line":3613},[3526,7422,4837],{"emptyLinePlaceholder":4836},[3526,7424,7425,7427,7430,7432,7434,7436,7438,7441],{"class":3528,"line":3619},[3526,7426,3503],{"class":3954},[3526,7428,7429],{"class":3982}," saveButton",[3526,7431,4026],{"class":3950},[3526,7433,4040],{"class":4039},[3526,7435,5531],{"class":3967},[3526,7437,3996],{"class":3950},[3526,7439,7440],{"class":3999},"\"Save\"",[3526,7442,4003],{"class":3950},[3526,7444,7445,7447,7449,7451,7454,7456,7458,7460,7462],{"class":3528,"line":4612},[3526,7446,7188],{"class":3982},[3526,7448,3166],{"class":3950},[3526,7450,4166],{"class":3967},[3526,7452,7453],{"class":3950},"(saveButton, ",[3526,7455,3852],{"class":4206},[3526,7457,3173],{"class":3950},[3526,7459,3760],{"class":4206},[3526,7461,5447],{"class":3950},[3526,7463,7464],{"class":3976},"\u002F\u002F Колонка 1, рядок 3\n",[3150,7466,7467,7469],{},[3163,7468,6615],{}," Форма з трьома полями (Title, Author, Duration) та кнопкою Save. Labels у лівій колонці, TextFields у правій.",[3150,7471,7472],{},[3163,7473,7474],{},"Розширені можливості GridPane:",[6171,7476,7477,7485,7493],{},[4501,7478,7479,7484],{},[3163,7480,7481],{},[3154,7482,7483],{},"GridPane.setColumnSpan(node, span)"," — елемент займає кілька колонок.",[4501,7486,7487,7492],{},[3163,7488,7489],{},[3154,7490,7491],{},"GridPane.setRowSpan(node, span)"," — елемент займає кілька рядків.",[4501,7494,7495,7503],{},[3163,7496,7497,3282,7500],{},[3154,7498,7499],{},"ColumnConstraints",[3154,7501,7502],{},"RowConstraints"," — точне керування розмірами колонок\u002Fрядків.",[3150,7505,7506],{},"Приклад: кнопка Save на всю ширину форми:",[3518,7508,7510],{"className":3941,"code":7509,"language":3943,"meta":3522,"style":3522},"Button saveButton = new Button(\"Save Audiobook\");\nsaveButton.setMaxWidth(Double.MAX_VALUE); \u002F\u002F Розтягнути на всю доступну ширину\ngrid.add(saveButton, 0, 3, 2, 1); \u002F\u002F columnIndex=0, rowIndex=3, colspan=2, rowspan=1\n",[3154,7511,7512,7531,7556],{"__ignoreMap":3522},[3526,7513,7514,7516,7518,7520,7522,7524,7526,7529],{"class":3528,"line":3529},[3526,7515,3503],{"class":3954},[3526,7517,7429],{"class":3982},[3526,7519,4026],{"class":3950},[3526,7521,4040],{"class":4039},[3526,7523,5531],{"class":3967},[3526,7525,3996],{"class":3950},[3526,7527,7528],{"class":3999},"\"Save Audiobook\"",[3526,7530,4003],{"class":3950},[3526,7532,7533,7536,7538,7541,7543,7546,7548,7551,7553],{"class":3528,"line":3535},[3526,7534,7535],{"class":3982},"saveButton",[3526,7537,3166],{"class":3950},[3526,7539,7540],{"class":3967},"setMaxWidth",[3526,7542,3996],{"class":3950},[3526,7544,7545],{"class":3982},"Double",[3526,7547,3166],{"class":3950},[3526,7549,7550],{"class":3982},"MAX_VALUE",[3526,7552,5447],{"class":3950},[3526,7554,7555],{"class":3976},"\u002F\u002F Розтягнути на всю доступну ширину\n",[3526,7557,7558,7560,7562,7564,7566,7568,7570,7572,7574,7576,7578,7580,7582],{"class":3528,"line":3541},[3526,7559,7188],{"class":3982},[3526,7561,3166],{"class":3950},[3526,7563,4166],{"class":3967},[3526,7565,7453],{"class":3950},[3526,7567,6107],{"class":4206},[3526,7569,3173],{"class":3950},[3526,7571,3760],{"class":4206},[3526,7573,3173],{"class":3950},[3526,7575,3784],{"class":4206},[3526,7577,3173],{"class":3950},[3526,7579,3852],{"class":4206},[3526,7581,5447],{"class":3950},[3526,7583,7584],{"class":3976},"\u002F\u002F columnIndex=0, rowIndex=3, colspan=2, rowspan=1\n",[3206,7586,7588],{"id":7587},"stackpane-накладання-елементів","StackPane: Накладання елементів",[3150,7590,7591,6217,7594,7597],{},[3163,7592,7593],{},"StackPane",[3163,7595,7596],{},"один на одному",", як стопку карт. Перший доданий елемент — внизу, останній — зверху. Це корисно для створення оверлеїв, діалогів поверх контенту або іконок поверх зображень.",[3518,7599,7601],{"className":3941,"code":7600,"language":3943,"meta":3522,"style":3522},"StackPane stack = new StackPane();\n\n\u002F\u002F Фонове зображення\nRectangle background = new Rectangle(300, 200);\nbackground.setFill(Color.LIGHTBLUE);\n\n\u002F\u002F Текст поверх фону\nLabel overlayText = new Label(\"Loading...\");\noverlayText.setStyle(\"-fx-font-size: 24px; -fx-text-fill: white;\");\n\nstack.getChildren().addAll(background, overlayText);\n",[3154,7602,7603,7619,7623,7628,7654,7676,7680,7685,7705,7721,7725],{"__ignoreMap":3522},[3526,7604,7605,7607,7610,7612,7614,7617],{"class":3528,"line":3529},[3526,7606,7593],{"class":3954},[3526,7608,7609],{"class":3982}," stack",[3526,7611,4026],{"class":3950},[3526,7613,4040],{"class":4039},[3526,7615,7616],{"class":3967}," StackPane",[3526,7618,4150],{"class":3950},[3526,7620,7621],{"class":3528,"line":3535},[3526,7622,4837],{"emptyLinePlaceholder":4836},[3526,7624,7625],{"class":3528,"line":3541},[3526,7626,7627],{"class":3976},"\u002F\u002F Фонове зображення\n",[3526,7629,7630,7633,7636,7638,7640,7643,7645,7647,7649,7652],{"class":3528,"line":3547},[3526,7631,7632],{"class":3954},"Rectangle",[3526,7634,7635],{"class":3982}," background",[3526,7637,4026],{"class":3950},[3526,7639,4040],{"class":4039},[3526,7641,7642],{"class":3967}," Rectangle",[3526,7644,3996],{"class":3950},[3526,7646,3859],{"class":4206},[3526,7648,3173],{"class":3950},[3526,7650,7651],{"class":4206},"200",[3526,7653,4003],{"class":3950},[3526,7655,7656,7659,7661,7664,7666,7669,7671,7674],{"class":3528,"line":3553},[3526,7657,7658],{"class":3982},"background",[3526,7660,3166],{"class":3950},[3526,7662,7663],{"class":3967},"setFill",[3526,7665,3996],{"class":3950},[3526,7667,7668],{"class":3982},"Color",[3526,7670,3166],{"class":3950},[3526,7672,7673],{"class":3982},"LIGHTBLUE",[3526,7675,4003],{"class":3950},[3526,7677,7678],{"class":3528,"line":3559},[3526,7679,4837],{"emptyLinePlaceholder":4836},[3526,7681,7682],{"class":3528,"line":3565},[3526,7683,7684],{"class":3976},"\u002F\u002F Текст поверх фону\n",[3526,7686,7687,7689,7692,7694,7696,7698,7700,7703],{"class":3528,"line":3571},[3526,7688,3509],{"class":3954},[3526,7690,7691],{"class":3982}," overlayText",[3526,7693,4026],{"class":3950},[3526,7695,4040],{"class":4039},[3526,7697,4173],{"class":3967},[3526,7699,3996],{"class":3950},[3526,7701,7702],{"class":3999},"\"Loading...\"",[3526,7704,4003],{"class":3950},[3526,7706,7707,7710,7712,7714,7716,7719],{"class":3528,"line":3577},[3526,7708,7709],{"class":3982},"overlayText",[3526,7711,3166],{"class":3950},[3526,7713,5459],{"class":3967},[3526,7715,3996],{"class":3950},[3526,7717,7718],{"class":3999},"\"-fx-font-size: 24px; -fx-text-fill: white;\"",[3526,7720,4003],{"class":3950},[3526,7722,7723],{"class":3528,"line":3583},[3526,7724,4837],{"emptyLinePlaceholder":4836},[3526,7726,7727,7730,7732,7734,7736,7738],{"class":3528,"line":3589},[3526,7728,7729],{"class":3982},"stack",[3526,7731,3166],{"class":3950},[3526,7733,4160],{"class":3967},[3526,7735,4163],{"class":3950},[3526,7737,5606],{"class":3967},[3526,7739,7740],{"class":3950},"(background, overlayText);\n",[3150,7742,7743,7745],{},[3163,7744,6615],{}," Синій прямокутник з текстом \"Loading...\" по центру.",[4344,7747,7748,7750,7751,7754],{},[3163,7749,7593],{}," вирівнює всі елементи по центру за замовчуванням. Якщо вам потрібне інше вирівнювання, використовуйте ",[3154,7752,7753],{},"StackPane.setAlignment(node, Pos.TOP_LEFT)"," для конкретного вузла.",[3206,7756,7758],{"id":7757},"порівняльна-таблиця-layout-контейнерів","Порівняльна таблиця layout-контейнерів",[7760,7761,7762,7778],"table",{},[7763,7764,7765],"thead",{},[7766,7767,7768,7772,7775],"tr",{},[7769,7770,7771],"th",{},"Контейнер",[7769,7773,7774],{},"Призначення",[7769,7776,7777],{},"Коли використовувати",[7779,7780,7781,7794,7806,7818,7830,7842,7855,7868],"tbody",{},[7766,7782,7783,7788,7791],{},[7784,7785,7786],"td",{},[3163,7787,3475],{},[7784,7789,7790],{},"Вертикальне розташування",[7784,7792,7793],{},"Списки, форми, меню",[7766,7795,7796,7800,7803],{},[7784,7797,7798],{},[3163,7799,3478],{},[7784,7801,7802],{},"Горизонтальне розташування",[7784,7804,7805],{},"Панелі інструментів, кнопки дій",[7766,7807,7808,7812,7815],{},[7784,7809,7810],{},[3163,7811,3481],{},[7784,7813,7814],{},"5 зон (top, bottom, left, right, center)",[7784,7816,7817],{},"Класичний layout додатку (меню + контент)",[7766,7819,7820,7824,7827],{},[7784,7821,7822],{},[3163,7823,3484],{},[7784,7825,7826],{},"Табличне розташування",[7784,7828,7829],{},"Форми з полями, калькулятори",[7766,7831,7832,7836,7839],{},[7784,7833,7834],{},[3163,7835,7593],{},[7784,7837,7838],{},"Накладання елементів",[7784,7840,7841],{},"Оверлеї, діалоги, іконки поверх зображень",[7766,7843,7844,7849,7852],{},[7784,7845,7846],{},[3163,7847,7848],{},"FlowPane",[7784,7850,7851],{},"Автоматичне перенесення на новий рядок",[7784,7853,7854],{},"Теги, галереї зображень",[7766,7856,7857,7862,7865],{},[7784,7858,7859],{},[3163,7860,7861],{},"TilePane",[7784,7863,7864],{},"Рівномірні комірки (як плитка)",[7784,7866,7867],{},"Іконки додатків, галереї з фіксованим розміром",[7766,7869,7870,7875,7878],{},[7784,7871,7872],{},[3163,7873,7874],{},"AnchorPane",[7784,7876,7877],{},"Прив'язка до країв контейнера",[7784,7879,7880],{},"Складні custom layouts (рідко використовується)",[3375,7882],{},[3145,7884,7886],{"id":7885},"controls-основні-компоненти-ui","Controls: Основні компоненти UI",[3150,7888,7889,7890,7893],{},"Layout-контейнери організовують простір, але самі по собі вони порожні. Щоб додаток став інтерактивним, потрібні ",[3163,7891,7892],{},"контроли"," (Controls) — компоненти, з якими користувач безпосередньо взаємодіє: кнопки, текстові поля, списки, таблиці.",[3150,7895,7896],{},"JavaFX пропонує багатий набір вбудованих контролів, що покривають 90% типових потреб desktop-додатків. Розглянемо найважливіші з них.",[3206,7898,7900],{"id":7899},"button-кнопка-для-дій","Button: Кнопка для дій",[3150,7902,7903,7905],{},[3163,7904,3503],{}," — найпростіший інтерактивний елемент. Користувач натискає кнопку → виконується дія.",[3518,7907,7909],{"className":3941,"code":7908,"language":3943,"meta":3522,"style":3522},"Button saveButton = new Button(\"Save Audiobook\");\nsaveButton.setOnAction(event -> {\n    System.out.println(\"Saving audiobook...\");\n    \u002F\u002F Тут буде виклик viewModel.saveAudiobook()\n});\n",[3154,7910,7911,7929,7943,7962,7967],{"__ignoreMap":3522},[3526,7912,7913,7915,7917,7919,7921,7923,7925,7927],{"class":3528,"line":3529},[3526,7914,3503],{"class":3954},[3526,7916,7429],{"class":3982},[3526,7918,4026],{"class":3950},[3526,7920,4040],{"class":4039},[3526,7922,5531],{"class":3967},[3526,7924,3996],{"class":3950},[3526,7926,7528],{"class":3999},[3526,7928,4003],{"class":3950},[3526,7930,7931,7933,7935,7937,7939,7941],{"class":3528,"line":3535},[3526,7932,7535],{"class":3982},[3526,7934,3166],{"class":3950},[3526,7936,5548],{"class":3967},[3526,7938,5551],{"class":3950},[3526,7940,5554],{"class":3960},[3526,7942,4402],{"class":3950},[3526,7944,7945,7947,7949,7951,7953,7955,7957,7960],{"class":3528,"line":3541},[3526,7946,3983],{"class":3982},[3526,7948,3166],{"class":3950},[3526,7950,3988],{"class":3982},[3526,7952,3166],{"class":3950},[3526,7954,3993],{"class":3967},[3526,7956,3996],{"class":3950},[3526,7958,7959],{"class":3999},"\"Saving audiobook...\"",[3526,7961,4003],{"class":3950},[3526,7963,7964],{"class":3528,"line":3547},[3526,7965,7966],{"class":3976},"    \u002F\u002F Тут буде виклик viewModel.saveAudiobook()\n",[3526,7968,7969],{"class":3528,"line":3553},[3526,7970,6030],{"class":3950},[3150,7972,7973],{},[3163,7974,7975],{},"Властивості Button:",[6171,7977,7978,7985,7993,8001,8009],{},[4501,7979,7980,7984],{},[3163,7981,7982],{},[3154,7983,3735],{}," — текст на кнопці.",[4501,7986,7987,7992],{},[3163,7988,7989],{},[3154,7990,7991],{},"graphic"," — іконка (Node, зазвичай ImageView або SVGPath).",[4501,7994,7995,8000],{},[3163,7996,7997],{},[3154,7998,7999],{},"disable"," — чи кнопка неактивна (сіра, не реагує на кліки).",[4501,8002,8003,8008],{},[3163,8004,8005],{},[3154,8006,8007],{},"defaultButton"," — чи кнопка активується при натисканні Enter.",[4501,8010,8011,8016],{},[3163,8012,8013],{},[3154,8014,8015],{},"cancelButton"," — чи кнопка активується при натисканні Esc.",[3150,8018,8019],{},"Приклад кнопки з іконкою:",[3518,8021,8023],{"className":3941,"code":8022,"language":3943,"meta":3522,"style":3522},"Button addButton = new Button(\"Add\");\nImageView icon = new ImageView(new Image(\"icons\u002Fadd.png\"));\nicon.setFitWidth(16);\nicon.setFitHeight(16);\naddButton.setGraphic(icon);\n",[3154,8024,8025,8043,8072,8089,8104],{"__ignoreMap":3522},[3526,8026,8027,8029,8031,8033,8035,8037,8039,8041],{"class":3528,"line":3529},[3526,8028,3503],{"class":3954},[3526,8030,6377],{"class":3982},[3526,8032,4026],{"class":3950},[3526,8034,4040],{"class":4039},[3526,8036,5531],{"class":3967},[3526,8038,3996],{"class":3950},[3526,8040,6388],{"class":3999},[3526,8042,4003],{"class":3950},[3526,8044,8045,8048,8051,8053,8055,8058,8060,8062,8065,8067,8070],{"class":3528,"line":3535},[3526,8046,8047],{"class":3954},"ImageView",[3526,8049,8050],{"class":3982}," icon",[3526,8052,4026],{"class":3950},[3526,8054,4040],{"class":4039},[3526,8056,8057],{"class":3967}," ImageView",[3526,8059,3996],{"class":3950},[3526,8061,4040],{"class":4039},[3526,8063,8064],{"class":3967}," Image",[3526,8066,3996],{"class":3950},[3526,8068,8069],{"class":3999},"\"icons\u002Fadd.png\"",[3526,8071,4181],{"class":3950},[3526,8073,8074,8077,8079,8082,8084,8087],{"class":3528,"line":3541},[3526,8075,8076],{"class":3982},"icon",[3526,8078,3166],{"class":3950},[3526,8080,8081],{"class":3967},"setFitWidth",[3526,8083,3996],{"class":3950},[3526,8085,8086],{"class":4206},"16",[3526,8088,4003],{"class":3950},[3526,8090,8091,8093,8095,8098,8100,8102],{"class":3528,"line":3547},[3526,8092,8076],{"class":3982},[3526,8094,3166],{"class":3950},[3526,8096,8097],{"class":3967},"setFitHeight",[3526,8099,3996],{"class":3950},[3526,8101,8086],{"class":4206},[3526,8103,4003],{"class":3950},[3526,8105,8106,8109,8111,8114],{"class":3528,"line":3553},[3526,8107,8108],{"class":3982},"addButton",[3526,8110,3166],{"class":3950},[3526,8112,8113],{"class":3967},"setGraphic",[3526,8115,8116],{"class":3950},"(icon);\n",[3206,8118,8120],{"id":8119},"label-статичний-текст","Label: Статичний текст",[3150,8122,8123,8125],{},[3163,8124,3509],{}," відображає текст, що не редагується користувачем. Використовується для підписів, заголовків, повідомлень.",[3518,8127,8129],{"className":3941,"code":8128,"language":3943,"meta":3522,"style":3522},"Label titleLabel = new Label(\"Audiobook Details\");\ntitleLabel.setStyle(\"-fx-font-size: 18px; -fx-font-weight: bold;\");\n",[3154,8130,8131,8150],{"__ignoreMap":3522},[3526,8132,8133,8135,8137,8139,8141,8143,8145,8148],{"class":3528,"line":3529},[3526,8134,3509],{"class":3954},[3526,8136,5484],{"class":3982},[3526,8138,4026],{"class":3950},[3526,8140,4040],{"class":4039},[3526,8142,4173],{"class":3967},[3526,8144,3996],{"class":3950},[3526,8146,8147],{"class":3999},"\"Audiobook Details\"",[3526,8149,4003],{"class":3950},[3526,8151,8152,8155,8157,8159,8161,8164],{"class":3528,"line":3535},[3526,8153,8154],{"class":3982},"titleLabel",[3526,8156,3166],{"class":3950},[3526,8158,5459],{"class":3967},[3526,8160,3996],{"class":3950},[3526,8162,8163],{"class":3999},"\"-fx-font-size: 18px; -fx-font-weight: bold;\"",[3526,8165,4003],{"class":3950},[3150,8167,8168,8171,8172,3166],{},[3163,8169,8170],{},"Особливість:"," Label може містити не лише текст, а й графіку (іконку) через властивість ",[3154,8173,7991],{},[3206,8175,8177],{"id":8176},"textfield-та-textarea-введення-тексту","TextField та TextArea: Введення тексту",[3150,8179,8180,8182,8183,8186],{},[3163,8181,3506],{}," — однорядкове текстове поле. ",[3163,8184,8185],{},"TextArea"," — багаторядкове.",[3518,8188,8190],{"className":3941,"code":8189,"language":3943,"meta":3522,"style":3522},"TextField titleField = new TextField();\ntitleField.setPromptText(\"Enter audiobook title\"); \u002F\u002F Placeholder-текст\n\nTextArea descriptionArea = new TextArea();\ndescriptionArea.setPrefRowCount(5); \u002F\u002F Висота: 5 рядків\ndescriptionArea.setWrapText(true); \u002F\u002F Автоматичне перенесення тексту\n",[3154,8191,8192,8207,8227,8231,8247,8266],{"__ignoreMap":3522},[3526,8193,8194,8196,8199,8201,8203,8205],{"class":3528,"line":3529},[3526,8195,3506],{"class":3954},[3526,8197,8198],{"class":3982}," titleField",[3526,8200,4026],{"class":3950},[3526,8202,4040],{"class":4039},[3526,8204,6568],{"class":3967},[3526,8206,4150],{"class":3950},[3526,8208,8209,8212,8214,8217,8219,8222,8224],{"class":3528,"line":3535},[3526,8210,8211],{"class":3982},"titleField",[3526,8213,3166],{"class":3950},[3526,8215,8216],{"class":3967},"setPromptText",[3526,8218,3996],{"class":3950},[3526,8220,8221],{"class":3999},"\"Enter audiobook title\"",[3526,8223,5447],{"class":3950},[3526,8225,8226],{"class":3976},"\u002F\u002F Placeholder-текст\n",[3526,8228,8229],{"class":3528,"line":3541},[3526,8230,4837],{"emptyLinePlaceholder":4836},[3526,8232,8233,8235,8238,8240,8242,8245],{"class":3528,"line":3547},[3526,8234,8185],{"class":3954},[3526,8236,8237],{"class":3982}," descriptionArea",[3526,8239,4026],{"class":3950},[3526,8241,4040],{"class":4039},[3526,8243,8244],{"class":3967}," TextArea",[3526,8246,4150],{"class":3950},[3526,8248,8249,8252,8254,8257,8259,8261,8263],{"class":3528,"line":3553},[3526,8250,8251],{"class":3982},"descriptionArea",[3526,8253,3166],{"class":3950},[3526,8255,8256],{"class":3967},"setPrefRowCount",[3526,8258,3996],{"class":3950},[3526,8260,3786],{"class":4206},[3526,8262,5447],{"class":3950},[3526,8264,8265],{"class":3976},"\u002F\u002F Висота: 5 рядків\n",[3526,8267,8268,8270,8272,8275,8277,8280,8282],{"class":3528,"line":3559},[3526,8269,8251],{"class":3982},[3526,8271,3166],{"class":3950},[3526,8273,8274],{"class":3967},"setWrapText",[3526,8276,3996],{"class":3950},[3526,8278,8279],{"class":3960},"true",[3526,8281,5447],{"class":3950},[3526,8283,8284],{"class":3976},"\u002F\u002F Автоматичне перенесення тексту\n",[3150,8286,8287],{},[3163,8288,8289],{},"Отримання введеного тексту:",[3518,8291,8293],{"className":3941,"code":8292,"language":3943,"meta":3522,"style":3522},"String title = titleField.getText();\nString description = descriptionArea.getText();\n",[3154,8294,8295,8313],{"__ignoreMap":3522},[3526,8296,8297,8299,8302,8304,8306,8308,8311],{"class":3528,"line":3529},[3526,8298,4424],{"class":3954},[3526,8300,8301],{"class":3982}," title",[3526,8303,4026],{"class":3950},[3526,8305,8211],{"class":3982},[3526,8307,3166],{"class":3950},[3526,8309,8310],{"class":3967},"getText",[3526,8312,4150],{"class":3950},[3526,8314,8315,8317,8320,8322,8324,8326,8328],{"class":3528,"line":3535},[3526,8316,4424],{"class":3954},[3526,8318,8319],{"class":3982}," description",[3526,8321,4026],{"class":3950},[3526,8323,8251],{"class":3982},[3526,8325,3166],{"class":3950},[3526,8327,8310],{"class":3967},[3526,8329,4150],{"class":3950},[3150,8331,8332,8335],{},[3163,8333,8334],{},"Валідація у реальному часі"," (ми детально розглянемо це у статті про MVVM):",[3518,8337,8339],{"className":3941,"code":8338,"language":3943,"meta":3522,"style":3522},"titleField.textProperty().addListener((observable, oldValue, newValue) -> {\n    if (newValue.length() > 255) {\n        titleField.setStyle(\"-fx-border-color: red;\");\n    } else {\n        titleField.setStyle(\"\");\n    }\n});\n",[3154,8340,8341,8362,8385,8401,8411,8426,8430],{"__ignoreMap":3522},[3526,8342,8343,8345,8347,8350,8352,8355,8358,8360],{"class":3528,"line":3529},[3526,8344,8211],{"class":3982},[3526,8346,3166],{"class":3950},[3526,8348,8349],{"class":3967},"textProperty",[3526,8351,4163],{"class":3950},[3526,8353,8354],{"class":3967},"addListener",[3526,8356,8357],{"class":3950},"((observable, oldValue, newValue) ",[3526,8359,5554],{"class":3960},[3526,8361,4402],{"class":3950},[3526,8363,8364,8367,8369,8372,8374,8377,8380,8383],{"class":3528,"line":3535},[3526,8365,8366],{"class":4039},"    if",[3526,8368,5910],{"class":3950},[3526,8370,8371],{"class":3982},"newValue",[3526,8373,3166],{"class":3950},[3526,8375,8376],{"class":3967},"length",[3526,8378,8379],{"class":3950},"() > ",[3526,8381,8382],{"class":4206},"255",[3526,8384,4433],{"class":3950},[3526,8386,8387,8390,8392,8394,8396,8399],{"class":3528,"line":3541},[3526,8388,8389],{"class":3982},"        titleField",[3526,8391,3166],{"class":3950},[3526,8393,5459],{"class":3967},[3526,8395,3996],{"class":3950},[3526,8397,8398],{"class":3999},"\"-fx-border-color: red;\"",[3526,8400,4003],{"class":3950},[3526,8402,8403,8406,8409],{"class":3528,"line":3547},[3526,8404,8405],{"class":3950},"    } ",[3526,8407,8408],{"class":4039},"else",[3526,8410,4402],{"class":3950},[3526,8412,8413,8415,8417,8419,8421,8424],{"class":3528,"line":3553},[3526,8414,8389],{"class":3982},[3526,8416,3166],{"class":3950},[3526,8418,5459],{"class":3967},[3526,8420,3996],{"class":3950},[3526,8422,8423],{"class":3999},"\"\"",[3526,8425,4003],{"class":3950},[3526,8427,8428],{"class":3528,"line":3559},[3526,8429,4449],{"class":3950},[3526,8431,8432],{"class":3528,"line":3565},[3526,8433,6030],{"class":3950},[3206,8435,8437],{"id":8436},"combobox-випадаючий-список","ComboBox: Випадаючий список",[3150,8439,8440,8443],{},[3163,8441,8442],{},"ComboBox"," дозволяє користувачу обрати один елемент зі списку.",[3518,8445,8447],{"className":3941,"code":8446,"language":3943,"meta":3522,"style":3522},"ComboBox\u003CString> genreComboBox = new ComboBox\u003C>();\ngenreComboBox.getItems().addAll(\"Fiction\", \"Non-Fiction\", \"Science\", \"History\");\ngenreComboBox.setValue(\"Fiction\"); \u002F\u002F Значення за замовчуванням\n\n\u002F\u002F Отримання обраного значення\nString selectedGenre = genreComboBox.getValue();\n",[3154,8448,8449,8473,8508,8526,8530,8535],{"__ignoreMap":3522},[3526,8450,8451,8453,8455,8457,8460,8463,8465,8467,8470],{"class":3528,"line":3529},[3526,8452,8442],{"class":3954},[3526,8454,4675],{"class":3950},[3526,8456,4424],{"class":3954},[3526,8458,8459],{"class":3950},"> ",[3526,8461,8462],{"class":3982},"genreComboBox",[3526,8464,4026],{"class":3950},[3526,8466,4040],{"class":4039},[3526,8468,8469],{"class":3954}," ComboBox",[3526,8471,8472],{"class":3950},"\u003C>();\n",[3526,8474,8475,8477,8479,8482,8484,8486,8488,8491,8493,8496,8498,8501,8503,8506],{"class":3528,"line":3535},[3526,8476,8462],{"class":3982},[3526,8478,3166],{"class":3950},[3526,8480,8481],{"class":3967},"getItems",[3526,8483,4163],{"class":3950},[3526,8485,5606],{"class":3967},[3526,8487,3996],{"class":3950},[3526,8489,8490],{"class":3999},"\"Fiction\"",[3526,8492,3173],{"class":3950},[3526,8494,8495],{"class":3999},"\"Non-Fiction\"",[3526,8497,3173],{"class":3950},[3526,8499,8500],{"class":3999},"\"Science\"",[3526,8502,3173],{"class":3950},[3526,8504,8505],{"class":3999},"\"History\"",[3526,8507,4003],{"class":3950},[3526,8509,8510,8512,8514,8517,8519,8521,8523],{"class":3528,"line":3541},[3526,8511,8462],{"class":3982},[3526,8513,3166],{"class":3950},[3526,8515,8516],{"class":3967},"setValue",[3526,8518,3996],{"class":3950},[3526,8520,8490],{"class":3999},[3526,8522,5447],{"class":3950},[3526,8524,8525],{"class":3976},"\u002F\u002F Значення за замовчуванням\n",[3526,8527,8528],{"class":3528,"line":3547},[3526,8529,4837],{"emptyLinePlaceholder":4836},[3526,8531,8532],{"class":3528,"line":3553},[3526,8533,8534],{"class":3976},"\u002F\u002F Отримання обраного значення\n",[3526,8536,8537,8539,8542,8544,8546,8548,8551],{"class":3528,"line":3559},[3526,8538,4424],{"class":3954},[3526,8540,8541],{"class":3982}," selectedGenre",[3526,8543,4026],{"class":3950},[3526,8545,8462],{"class":3982},[3526,8547,3166],{"class":3950},[3526,8549,8550],{"class":3967},"getValue",[3526,8552,4150],{"class":3950},[3150,8554,8555],{},[3163,8556,8557],{},"Для об'єктів (не лише String):",[3518,8559,8561],{"className":3941,"code":8560,"language":3943,"meta":3522,"style":3522},"ComboBox\u003CGenre> genreComboBox = new ComboBox\u003C>();\ngenreComboBox.getItems().addAll(genre1, genre2, genre3);\n\n\u002F\u002F Налаштування відображення (за замовчуванням викликається toString())\ngenreComboBox.setCellFactory(param -> new ListCell\u003CGenre>() {\n    @Override\n    protected void updateItem(Genre item, boolean empty) {\n        super.updateItem(item, empty);\n        setText(empty || item == null ? null : item.getName());\n    }\n});\ngenreComboBox.setButtonCell(genreComboBox.getCellFactory().call(null));\n",[3154,8562,8563,8584,8599,8603,8608,8634,8640,8667,8680,8709,8713,8717],{"__ignoreMap":3522},[3526,8564,8565,8567,8569,8572,8574,8576,8578,8580,8582],{"class":3528,"line":3529},[3526,8566,8442],{"class":3954},[3526,8568,4675],{"class":3950},[3526,8570,8571],{"class":3954},"Genre",[3526,8573,8459],{"class":3950},[3526,8575,8462],{"class":3982},[3526,8577,4026],{"class":3950},[3526,8579,4040],{"class":4039},[3526,8581,8469],{"class":3954},[3526,8583,8472],{"class":3950},[3526,8585,8586,8588,8590,8592,8594,8596],{"class":3528,"line":3535},[3526,8587,8462],{"class":3982},[3526,8589,3166],{"class":3950},[3526,8591,8481],{"class":3967},[3526,8593,4163],{"class":3950},[3526,8595,5606],{"class":3967},[3526,8597,8598],{"class":3950},"(genre1, genre2, genre3);\n",[3526,8600,8601],{"class":3528,"line":3541},[3526,8602,4837],{"emptyLinePlaceholder":4836},[3526,8604,8605],{"class":3528,"line":3547},[3526,8606,8607],{"class":3976},"\u002F\u002F Налаштування відображення (за замовчуванням викликається toString())\n",[3526,8609,8610,8612,8614,8617,8620,8622,8625,8628,8630,8632],{"class":3528,"line":3553},[3526,8611,8462],{"class":3982},[3526,8613,3166],{"class":3950},[3526,8615,8616],{"class":3967},"setCellFactory",[3526,8618,8619],{"class":3950},"(param ",[3526,8621,5554],{"class":3960},[3526,8623,8624],{"class":4039}," new",[3526,8626,8627],{"class":3954}," ListCell",[3526,8629,4675],{"class":3950},[3526,8631,8571],{"class":3954},[3526,8633,5977],{"class":3950},[3526,8635,8636,8638],{"class":3528,"line":3559},[3526,8637,4458],{"class":3950},[3526,8639,3955],{"class":3954},[3526,8641,8642,8645,8647,8650,8652,8654,8657,8659,8662,8665],{"class":3528,"line":3565},[3526,8643,8644],{"class":3960},"    protected",[3526,8646,3964],{"class":3954},[3526,8648,8649],{"class":3967}," updateItem",[3526,8651,3996],{"class":3950},[3526,8653,8571],{"class":3954},[3526,8655,8656],{"class":3982}," item",[3526,8658,3173],{"class":3950},[3526,8660,8661],{"class":3954},"boolean",[3526,8663,8664],{"class":3982}," empty",[3526,8666,4433],{"class":3950},[3526,8668,8669,8672,8674,8677],{"class":3528,"line":3571},[3526,8670,8671],{"class":3960},"        super",[3526,8673,3166],{"class":3950},[3526,8675,8676],{"class":3967},"updateItem",[3526,8678,8679],{"class":3950},"(item, empty);\n",[3526,8681,8682,8685,8688,8691,8694,8697,8700,8702,8704,8707],{"class":3528,"line":3577},[3526,8683,8684],{"class":3967},"        setText",[3526,8686,8687],{"class":3950},"(empty || item == ",[3526,8689,8690],{"class":3960},"null",[3526,8692,8693],{"class":4039}," ?",[3526,8695,8696],{"class":3960}," null",[3526,8698,8699],{"class":4039}," :",[3526,8701,8656],{"class":3982},[3526,8703,3166],{"class":3950},[3526,8705,8706],{"class":3967},"getName",[3526,8708,4046],{"class":3950},[3526,8710,8711],{"class":3528,"line":3583},[3526,8712,4449],{"class":3950},[3526,8714,8715],{"class":3528,"line":3589},[3526,8716,6030],{"class":3950},[3526,8718,8719,8721,8723,8726,8728,8730,8732,8735,8737,8740,8742,8744],{"class":3528,"line":3595},[3526,8720,8462],{"class":3982},[3526,8722,3166],{"class":3950},[3526,8724,8725],{"class":3967},"setButtonCell",[3526,8727,3996],{"class":3950},[3526,8729,8462],{"class":3982},[3526,8731,3166],{"class":3950},[3526,8733,8734],{"class":3967},"getCellFactory",[3526,8736,4163],{"class":3950},[3526,8738,8739],{"class":3967},"call",[3526,8741,3996],{"class":3950},[3526,8743,8690],{"class":3960},[3526,8745,4181],{"class":3950},[3206,8747,8749],{"id":8748},"checkbox-та-radiobutton-вибір-опцій","CheckBox та RadioButton: Вибір опцій",[3150,8751,8752,8755,8756,8759],{},[3163,8753,8754],{},"CheckBox"," — незалежний прапорець (можна обрати кілька). ",[3163,8757,8758],{},"RadioButton"," — взаємовиключні опції (лише одна з групи).",[3518,8761,8763],{"className":3941,"code":8762,"language":3943,"meta":3522,"style":3522},"\u002F\u002F CheckBox\nCheckBox notifyCheckBox = new CheckBox(\"Notify me about new releases\");\nboolean isChecked = notifyCheckBox.isSelected();\n\n\u002F\u002F RadioButton (група)\nToggleGroup formatGroup = new ToggleGroup();\n\nRadioButton mp3Radio = new RadioButton(\"MP3\");\nmp3Radio.setToggleGroup(formatGroup);\n\nRadioButton flacRadio = new RadioButton(\"FLAC\");\nflacRadio.setToggleGroup(formatGroup);\n\nRadioButton oggRadio = new RadioButton(\"OGG\");\noggRadio.setToggleGroup(formatGroup);\n\nmp3Radio.setSelected(true); \u002F\u002F За замовчуванням\n\n\u002F\u002F Отримання обраного\nRadioButton selected = (RadioButton) formatGroup.getSelectedToggle();\nString format = selected.getText();\n",[3154,8764,8765,8770,8791,8810,8814,8819,8836,8840,8861,8874,8878,8898,8909,8913,8933,8944,8948,8966,8970,8975,8995],{"__ignoreMap":3522},[3526,8766,8767],{"class":3528,"line":3529},[3526,8768,8769],{"class":3976},"\u002F\u002F CheckBox\n",[3526,8771,8772,8774,8777,8779,8781,8784,8786,8789],{"class":3528,"line":3535},[3526,8773,8754],{"class":3954},[3526,8775,8776],{"class":3982}," notifyCheckBox",[3526,8778,4026],{"class":3950},[3526,8780,4040],{"class":4039},[3526,8782,8783],{"class":3967}," CheckBox",[3526,8785,3996],{"class":3950},[3526,8787,8788],{"class":3999},"\"Notify me about new releases\"",[3526,8790,4003],{"class":3950},[3526,8792,8793,8795,8798,8800,8803,8805,8808],{"class":3528,"line":3541},[3526,8794,8661],{"class":3954},[3526,8796,8797],{"class":3982}," isChecked",[3526,8799,4026],{"class":3950},[3526,8801,8802],{"class":3982},"notifyCheckBox",[3526,8804,3166],{"class":3950},[3526,8806,8807],{"class":3967},"isSelected",[3526,8809,4150],{"class":3950},[3526,8811,8812],{"class":3528,"line":3547},[3526,8813,4837],{"emptyLinePlaceholder":4836},[3526,8815,8816],{"class":3528,"line":3553},[3526,8817,8818],{"class":3976},"\u002F\u002F RadioButton (група)\n",[3526,8820,8821,8824,8827,8829,8831,8834],{"class":3528,"line":3559},[3526,8822,8823],{"class":3954},"ToggleGroup",[3526,8825,8826],{"class":3982}," formatGroup",[3526,8828,4026],{"class":3950},[3526,8830,4040],{"class":4039},[3526,8832,8833],{"class":3967}," ToggleGroup",[3526,8835,4150],{"class":3950},[3526,8837,8838],{"class":3528,"line":3565},[3526,8839,4837],{"emptyLinePlaceholder":4836},[3526,8841,8842,8844,8847,8849,8851,8854,8856,8859],{"class":3528,"line":3571},[3526,8843,8758],{"class":3954},[3526,8845,8846],{"class":3982}," mp3Radio",[3526,8848,4026],{"class":3950},[3526,8850,4040],{"class":4039},[3526,8852,8853],{"class":3967}," RadioButton",[3526,8855,3996],{"class":3950},[3526,8857,8858],{"class":3999},"\"MP3\"",[3526,8860,4003],{"class":3950},[3526,8862,8863,8866,8868,8871],{"class":3528,"line":3577},[3526,8864,8865],{"class":3982},"mp3Radio",[3526,8867,3166],{"class":3950},[3526,8869,8870],{"class":3967},"setToggleGroup",[3526,8872,8873],{"class":3950},"(formatGroup);\n",[3526,8875,8876],{"class":3528,"line":3583},[3526,8877,4837],{"emptyLinePlaceholder":4836},[3526,8879,8880,8882,8885,8887,8889,8891,8893,8896],{"class":3528,"line":3589},[3526,8881,8758],{"class":3954},[3526,8883,8884],{"class":3982}," flacRadio",[3526,8886,4026],{"class":3950},[3526,8888,4040],{"class":4039},[3526,8890,8853],{"class":3967},[3526,8892,3996],{"class":3950},[3526,8894,8895],{"class":3999},"\"FLAC\"",[3526,8897,4003],{"class":3950},[3526,8899,8900,8903,8905,8907],{"class":3528,"line":3595},[3526,8901,8902],{"class":3982},"flacRadio",[3526,8904,3166],{"class":3950},[3526,8906,8870],{"class":3967},[3526,8908,8873],{"class":3950},[3526,8910,8911],{"class":3528,"line":3601},[3526,8912,4837],{"emptyLinePlaceholder":4836},[3526,8914,8915,8917,8920,8922,8924,8926,8928,8931],{"class":3528,"line":3607},[3526,8916,8758],{"class":3954},[3526,8918,8919],{"class":3982}," oggRadio",[3526,8921,4026],{"class":3950},[3526,8923,4040],{"class":4039},[3526,8925,8853],{"class":3967},[3526,8927,3996],{"class":3950},[3526,8929,8930],{"class":3999},"\"OGG\"",[3526,8932,4003],{"class":3950},[3526,8934,8935,8938,8940,8942],{"class":3528,"line":3613},[3526,8936,8937],{"class":3982},"oggRadio",[3526,8939,3166],{"class":3950},[3526,8941,8870],{"class":3967},[3526,8943,8873],{"class":3950},[3526,8945,8946],{"class":3528,"line":3619},[3526,8947,4837],{"emptyLinePlaceholder":4836},[3526,8949,8950,8952,8954,8957,8959,8961,8963],{"class":3528,"line":4612},[3526,8951,8865],{"class":3982},[3526,8953,3166],{"class":3950},[3526,8955,8956],{"class":3967},"setSelected",[3526,8958,3996],{"class":3950},[3526,8960,8279],{"class":3960},[3526,8962,5447],{"class":3950},[3526,8964,8965],{"class":3976},"\u002F\u002F За замовчуванням\n",[3526,8967,8968],{"class":3528,"line":4618},[3526,8969,4837],{"emptyLinePlaceholder":4836},[3526,8971,8972],{"class":3528,"line":4624},[3526,8973,8974],{"class":3976},"\u002F\u002F Отримання обраного\n",[3526,8976,8977,8979,8982,8985,8988,8990,8993],{"class":3528,"line":4630},[3526,8978,8758],{"class":3954},[3526,8980,8981],{"class":3982}," selected",[3526,8983,8984],{"class":3950}," = (RadioButton) ",[3526,8986,8987],{"class":3982},"formatGroup",[3526,8989,3166],{"class":3950},[3526,8991,8992],{"class":3967},"getSelectedToggle",[3526,8994,4150],{"class":3950},[3526,8996,8997,8999,9002,9004,9007,9009,9011],{"class":3528,"line":4918},[3526,8998,4424],{"class":3954},[3526,9000,9001],{"class":3982}," format",[3526,9003,4026],{"class":3950},[3526,9005,9006],{"class":3982},"selected",[3526,9008,3166],{"class":3950},[3526,9010,8310],{"class":3967},[3526,9012,4150],{"class":3950},[3206,9014,9016],{"id":9015},"listview-список-елементів","ListView: Список елементів",[3150,9018,9019,9022],{},[3163,9020,9021],{},"ListView"," відображає вертикальний список елементів, з яких користувач може обрати один або кілька.",[3518,9024,9026],{"className":3941,"code":9025,"language":3943,"meta":3522,"style":3522},"ListView\u003CString> trackListView = new ListView\u003C>();\ntrackListView.getItems().addAll(\"Track 1.mp3\", \"Track 2.mp3\", \"Track 3.mp3\");\n\n\u002F\u002F Режим вибору\ntrackListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n\u002F\u002F Отримання обраних елементів\nObservableList\u003CString> selectedTracks = trackListView.getSelectionModel().getSelectedItems();\n",[3154,9027,9028,9050,9079,9083,9088,9114,9118,9123],{"__ignoreMap":3522},[3526,9029,9030,9032,9034,9036,9038,9041,9043,9045,9048],{"class":3528,"line":3529},[3526,9031,9021],{"class":3954},[3526,9033,4675],{"class":3950},[3526,9035,4424],{"class":3954},[3526,9037,8459],{"class":3950},[3526,9039,9040],{"class":3982},"trackListView",[3526,9042,4026],{"class":3950},[3526,9044,4040],{"class":4039},[3526,9046,9047],{"class":3954}," ListView",[3526,9049,8472],{"class":3950},[3526,9051,9052,9054,9056,9058,9060,9062,9064,9067,9069,9072,9074,9077],{"class":3528,"line":3535},[3526,9053,9040],{"class":3982},[3526,9055,3166],{"class":3950},[3526,9057,8481],{"class":3967},[3526,9059,4163],{"class":3950},[3526,9061,5606],{"class":3967},[3526,9063,3996],{"class":3950},[3526,9065,9066],{"class":3999},"\"Track 1.mp3\"",[3526,9068,3173],{"class":3950},[3526,9070,9071],{"class":3999},"\"Track 2.mp3\"",[3526,9073,3173],{"class":3950},[3526,9075,9076],{"class":3999},"\"Track 3.mp3\"",[3526,9078,4003],{"class":3950},[3526,9080,9081],{"class":3528,"line":3541},[3526,9082,4837],{"emptyLinePlaceholder":4836},[3526,9084,9085],{"class":3528,"line":3547},[3526,9086,9087],{"class":3976},"\u002F\u002F Режим вибору\n",[3526,9089,9090,9092,9094,9097,9099,9102,9104,9107,9109,9112],{"class":3528,"line":3553},[3526,9091,9040],{"class":3982},[3526,9093,3166],{"class":3950},[3526,9095,9096],{"class":3967},"getSelectionModel",[3526,9098,4163],{"class":3950},[3526,9100,9101],{"class":3967},"setSelectionMode",[3526,9103,3996],{"class":3950},[3526,9105,9106],{"class":3982},"SelectionMode",[3526,9108,3166],{"class":3950},[3526,9110,9111],{"class":3982},"MULTIPLE",[3526,9113,4003],{"class":3950},[3526,9115,9116],{"class":3528,"line":3559},[3526,9117,4837],{"emptyLinePlaceholder":4836},[3526,9119,9120],{"class":3528,"line":3565},[3526,9121,9122],{"class":3976},"\u002F\u002F Отримання обраних елементів\n",[3526,9124,9125,9128,9130,9132,9134,9137,9139,9141,9143,9145,9147,9150],{"class":3528,"line":3571},[3526,9126,9127],{"class":3954},"ObservableList",[3526,9129,4675],{"class":3950},[3526,9131,4424],{"class":3954},[3526,9133,8459],{"class":3950},[3526,9135,9136],{"class":3982},"selectedTracks",[3526,9138,4026],{"class":3950},[3526,9140,9040],{"class":3982},[3526,9142,3166],{"class":3950},[3526,9144,9096],{"class":3967},[3526,9146,4163],{"class":3950},[3526,9148,9149],{"class":3967},"getSelectedItems",[3526,9151,4150],{"class":3950},[3150,9153,9154],{},[3163,9155,9156],{},"Для об'єктів:",[3518,9158,9160],{"className":3941,"code":9159,"language":3943,"meta":3522,"style":3522},"ListView\u003CAudiobook> audiobookListView = new ListView\u003C>();\naudiobookListView.getItems().addAll(audiobook1, audiobook2, audiobook3);\n\n\u002F\u002F Налаштування відображення\naudiobookListView.setCellFactory(param -> new ListCell\u003CAudiobook>() {\n    @Override\n    protected void updateItem(Audiobook item, boolean empty) {\n        super.updateItem(item, empty);\n        if (empty || item == null) {\n            setText(null);\n        } else {\n            setText(item.getTitle() + \" by \" + item.getAuthor().getFullName());\n        }\n    }\n});\n",[3154,9161,9162,9184,9199,9203,9208,9230,9236,9258,9268,9280,9291,9300,9337,9342,9346],{"__ignoreMap":3522},[3526,9163,9164,9166,9168,9171,9173,9176,9178,9180,9182],{"class":3528,"line":3529},[3526,9165,9021],{"class":3954},[3526,9167,4675],{"class":3950},[3526,9169,9170],{"class":3954},"Audiobook",[3526,9172,8459],{"class":3950},[3526,9174,9175],{"class":3982},"audiobookListView",[3526,9177,4026],{"class":3950},[3526,9179,4040],{"class":4039},[3526,9181,9047],{"class":3954},[3526,9183,8472],{"class":3950},[3526,9185,9186,9188,9190,9192,9194,9196],{"class":3528,"line":3535},[3526,9187,9175],{"class":3982},[3526,9189,3166],{"class":3950},[3526,9191,8481],{"class":3967},[3526,9193,4163],{"class":3950},[3526,9195,5606],{"class":3967},[3526,9197,9198],{"class":3950},"(audiobook1, audiobook2, audiobook3);\n",[3526,9200,9201],{"class":3528,"line":3541},[3526,9202,4837],{"emptyLinePlaceholder":4836},[3526,9204,9205],{"class":3528,"line":3547},[3526,9206,9207],{"class":3976},"\u002F\u002F Налаштування відображення\n",[3526,9209,9210,9212,9214,9216,9218,9220,9222,9224,9226,9228],{"class":3528,"line":3553},[3526,9211,9175],{"class":3982},[3526,9213,3166],{"class":3950},[3526,9215,8616],{"class":3967},[3526,9217,8619],{"class":3950},[3526,9219,5554],{"class":3960},[3526,9221,8624],{"class":4039},[3526,9223,8627],{"class":3954},[3526,9225,4675],{"class":3950},[3526,9227,9170],{"class":3954},[3526,9229,5977],{"class":3950},[3526,9231,9232,9234],{"class":3528,"line":3559},[3526,9233,4458],{"class":3950},[3526,9235,3955],{"class":3954},[3526,9237,9238,9240,9242,9244,9246,9248,9250,9252,9254,9256],{"class":3528,"line":3565},[3526,9239,8644],{"class":3960},[3526,9241,3964],{"class":3954},[3526,9243,8649],{"class":3967},[3526,9245,3996],{"class":3950},[3526,9247,9170],{"class":3954},[3526,9249,8656],{"class":3982},[3526,9251,3173],{"class":3950},[3526,9253,8661],{"class":3954},[3526,9255,8664],{"class":3982},[3526,9257,4433],{"class":3950},[3526,9259,9260,9262,9264,9266],{"class":3528,"line":3571},[3526,9261,8671],{"class":3960},[3526,9263,3166],{"class":3950},[3526,9265,8676],{"class":3967},[3526,9267,8679],{"class":3950},[3526,9269,9270,9273,9276,9278],{"class":3528,"line":3577},[3526,9271,9272],{"class":4039},"        if",[3526,9274,9275],{"class":3950}," (empty || item == ",[3526,9277,8690],{"class":3960},[3526,9279,4433],{"class":3950},[3526,9281,9282,9285,9287,9289],{"class":3528,"line":3583},[3526,9283,9284],{"class":3967},"            setText",[3526,9286,3996],{"class":3950},[3526,9288,8690],{"class":3960},[3526,9290,4003],{"class":3950},[3526,9292,9293,9296,9298],{"class":3528,"line":3589},[3526,9294,9295],{"class":3950},"        } ",[3526,9297,8408],{"class":4039},[3526,9299,4402],{"class":3950},[3526,9301,9302,9304,9306,9309,9311,9314,9317,9320,9323,9325,9327,9330,9332,9335],{"class":3528,"line":3595},[3526,9303,9284],{"class":3967},[3526,9305,3996],{"class":3950},[3526,9307,9308],{"class":3982},"item",[3526,9310,3166],{"class":3950},[3526,9312,9313],{"class":3967},"getTitle",[3526,9315,9316],{"class":3950},"() + ",[3526,9318,9319],{"class":3999},"\" by \"",[3526,9321,9322],{"class":3950}," + ",[3526,9324,9308],{"class":3982},[3526,9326,3166],{"class":3950},[3526,9328,9329],{"class":3967},"getAuthor",[3526,9331,4163],{"class":3950},[3526,9333,9334],{"class":3967},"getFullName",[3526,9336,4046],{"class":3950},[3526,9338,9339],{"class":3528,"line":3601},[3526,9340,9341],{"class":3950},"        }\n",[3526,9343,9344],{"class":3528,"line":3607},[3526,9345,4449],{"class":3950},[3526,9347,9348],{"class":3528,"line":3613},[3526,9349,6030],{"class":3950},[3206,9351,9353],{"id":9352},"tableview-таблиця-даних","TableView: Таблиця даних",[3150,9355,9356,9358],{},[3163,9357,3512],{}," — найпотужніший контрол для відображення табличних даних. Він підтримує сортування, фільтрацію, редагування комірок.",[3518,9360,9362],{"className":3941,"code":9361,"language":3943,"meta":3522,"style":3522},"TableView\u003CAudiobook> audiobookTable = new TableView\u003C>();\n\n\u002F\u002F Колонка \"Title\"\nTableColumn\u003CAudiobook, String> titleColumn = new TableColumn\u003C>(\"Title\");\ntitleColumn.setCellValueFactory(new PropertyValueFactory\u003C>(\"title\"));\n\n\u002F\u002F Колонка \"Author\"\nTableColumn\u003CAudiobook, String> authorColumn = new TableColumn\u003C>(\"Author\");\nauthorColumn.setCellValueFactory(cellData -> \n    new SimpleStringProperty(cellData.getValue().getAuthor().getFullName())\n);\n\n\u002F\u002F Колонка \"Duration\"\nTableColumn\u003CAudiobook, Integer> durationColumn = new TableColumn\u003C>(\"Duration (min)\");\ndurationColumn.setCellValueFactory(new PropertyValueFactory\u003C>(\"duration\"));\n\naudiobookTable.getColumns().addAll(titleColumn, authorColumn, durationColumn);\n\n\u002F\u002F Додавання даних\naudiobookTable.getItems().addAll(audiobook1, audiobook2, audiobook3);\n",[3154,9363,9364,9386,9390,9395,9428,9451,9455,9460,9490,9506,9533,9537,9541,9546,9577,9598,9602,9618,9622,9627],{"__ignoreMap":3522},[3526,9365,9366,9368,9370,9372,9374,9377,9379,9381,9384],{"class":3528,"line":3529},[3526,9367,3512],{"class":3954},[3526,9369,4675],{"class":3950},[3526,9371,9170],{"class":3954},[3526,9373,8459],{"class":3950},[3526,9375,9376],{"class":3982},"audiobookTable",[3526,9378,4026],{"class":3950},[3526,9380,4040],{"class":4039},[3526,9382,9383],{"class":3954}," TableView",[3526,9385,8472],{"class":3950},[3526,9387,9388],{"class":3528,"line":3535},[3526,9389,4837],{"emptyLinePlaceholder":4836},[3526,9391,9392],{"class":3528,"line":3541},[3526,9393,9394],{"class":3976},"\u002F\u002F Колонка \"Title\"\n",[3526,9396,9397,9400,9402,9404,9406,9408,9410,9413,9415,9417,9420,9423,9426],{"class":3528,"line":3547},[3526,9398,9399],{"class":3954},"TableColumn",[3526,9401,4675],{"class":3950},[3526,9403,9170],{"class":3954},[3526,9405,3173],{"class":3950},[3526,9407,4424],{"class":3954},[3526,9409,8459],{"class":3950},[3526,9411,9412],{"class":3982},"titleColumn",[3526,9414,4026],{"class":3950},[3526,9416,4040],{"class":4039},[3526,9418,9419],{"class":3954}," TableColumn",[3526,9421,9422],{"class":3950},"\u003C>(",[3526,9424,9425],{"class":3999},"\"Title\"",[3526,9427,4003],{"class":3950},[3526,9429,9430,9432,9434,9437,9439,9441,9444,9446,9449],{"class":3528,"line":3553},[3526,9431,9412],{"class":3982},[3526,9433,3166],{"class":3950},[3526,9435,9436],{"class":3967},"setCellValueFactory",[3526,9438,3996],{"class":3950},[3526,9440,4040],{"class":4039},[3526,9442,9443],{"class":3954}," PropertyValueFactory",[3526,9445,9422],{"class":3950},[3526,9447,9448],{"class":3999},"\"title\"",[3526,9450,4181],{"class":3950},[3526,9452,9453],{"class":3528,"line":3559},[3526,9454,4837],{"emptyLinePlaceholder":4836},[3526,9456,9457],{"class":3528,"line":3565},[3526,9458,9459],{"class":3976},"\u002F\u002F Колонка \"Author\"\n",[3526,9461,9462,9464,9466,9468,9470,9472,9474,9477,9479,9481,9483,9485,9488],{"class":3528,"line":3571},[3526,9463,9399],{"class":3954},[3526,9465,4675],{"class":3950},[3526,9467,9170],{"class":3954},[3526,9469,3173],{"class":3950},[3526,9471,4424],{"class":3954},[3526,9473,8459],{"class":3950},[3526,9475,9476],{"class":3982},"authorColumn",[3526,9478,4026],{"class":3950},[3526,9480,4040],{"class":4039},[3526,9482,9419],{"class":3954},[3526,9484,9422],{"class":3950},[3526,9486,9487],{"class":3999},"\"Author\"",[3526,9489,4003],{"class":3950},[3526,9491,9492,9494,9496,9498,9501,9503],{"class":3528,"line":3577},[3526,9493,9476],{"class":3982},[3526,9495,3166],{"class":3950},[3526,9497,9436],{"class":3967},[3526,9499,9500],{"class":3950},"(cellData ",[3526,9502,5554],{"class":3960},[3526,9504,9505],{"class":3950}," \n",[3526,9507,9508,9510,9513,9515,9518,9520,9522,9524,9526,9528,9530],{"class":3528,"line":3583},[3526,9509,6551],{"class":4039},[3526,9511,9512],{"class":3967}," SimpleStringProperty",[3526,9514,3996],{"class":3950},[3526,9516,9517],{"class":3982},"cellData",[3526,9519,3166],{"class":3950},[3526,9521,8550],{"class":3967},[3526,9523,4163],{"class":3950},[3526,9525,9329],{"class":3967},[3526,9527,4163],{"class":3950},[3526,9529,9334],{"class":3967},[3526,9531,9532],{"class":3950},"())\n",[3526,9534,9535],{"class":3528,"line":3589},[3526,9536,4003],{"class":3950},[3526,9538,9539],{"class":3528,"line":3595},[3526,9540,4837],{"emptyLinePlaceholder":4836},[3526,9542,9543],{"class":3528,"line":3601},[3526,9544,9545],{"class":3976},"\u002F\u002F Колонка \"Duration\"\n",[3526,9547,9548,9550,9552,9554,9556,9559,9561,9564,9566,9568,9570,9572,9575],{"class":3528,"line":3607},[3526,9549,9399],{"class":3954},[3526,9551,4675],{"class":3950},[3526,9553,9170],{"class":3954},[3526,9555,3173],{"class":3950},[3526,9557,9558],{"class":3954},"Integer",[3526,9560,8459],{"class":3950},[3526,9562,9563],{"class":3982},"durationColumn",[3526,9565,4026],{"class":3950},[3526,9567,4040],{"class":4039},[3526,9569,9419],{"class":3954},[3526,9571,9422],{"class":3950},[3526,9573,9574],{"class":3999},"\"Duration (min)\"",[3526,9576,4003],{"class":3950},[3526,9578,9579,9581,9583,9585,9587,9589,9591,9593,9596],{"class":3528,"line":3613},[3526,9580,9563],{"class":3982},[3526,9582,3166],{"class":3950},[3526,9584,9436],{"class":3967},[3526,9586,3996],{"class":3950},[3526,9588,4040],{"class":4039},[3526,9590,9443],{"class":3954},[3526,9592,9422],{"class":3950},[3526,9594,9595],{"class":3999},"\"duration\"",[3526,9597,4181],{"class":3950},[3526,9599,9600],{"class":3528,"line":3619},[3526,9601,4837],{"emptyLinePlaceholder":4836},[3526,9603,9604,9606,9608,9611,9613,9615],{"class":3528,"line":4612},[3526,9605,9376],{"class":3982},[3526,9607,3166],{"class":3950},[3526,9609,9610],{"class":3967},"getColumns",[3526,9612,4163],{"class":3950},[3526,9614,5606],{"class":3967},[3526,9616,9617],{"class":3950},"(titleColumn, authorColumn, durationColumn);\n",[3526,9619,9620],{"class":3528,"line":4618},[3526,9621,4837],{"emptyLinePlaceholder":4836},[3526,9623,9624],{"class":3528,"line":4624},[3526,9625,9626],{"class":3976},"\u002F\u002F Додавання даних\n",[3526,9628,9629,9631,9633,9635,9637,9639],{"class":3528,"line":4630},[3526,9630,9376],{"class":3982},[3526,9632,3166],{"class":3950},[3526,9634,8481],{"class":3967},[3526,9636,4163],{"class":3950},[3526,9638,5606],{"class":3967},[3526,9640,9198],{"class":3950},[3150,9642,9643,9649,9650,9653,9654,9657,9658,9660],{},[3163,9644,9645,9646,4378],{},"Пояснення ",[3154,9647,9648],{},"PropertyValueFactory"," Це спрощений спосіб зв'язати колонку з полем об'єкта. ",[3154,9651,9652],{},"new PropertyValueFactory\u003C>(\"title\")"," шукає метод ",[3154,9655,9656],{},"getTitle()"," у класі ",[3154,9659,9170],{}," та використовує його значення для комірки.",[3150,9662,9663,9666],{},[3163,9664,9665],{},"Для складніших випадків"," (наприклад, вкладені об'єкти) використовуйте лямбда-вираз:",[3518,9668,9670],{"className":3941,"code":9669,"language":3943,"meta":3522,"style":3522},"authorColumn.setCellValueFactory(cellData -> {\n    Audiobook audiobook = cellData.getValue();\n    String authorName = audiobook.getAuthor().getFullName();\n    return new SimpleStringProperty(authorName);\n});\n",[3154,9671,9672,9686,9704,9727,9739],{"__ignoreMap":3522},[3526,9673,9674,9676,9678,9680,9682,9684],{"class":3528,"line":3529},[3526,9675,9476],{"class":3982},[3526,9677,3166],{"class":3950},[3526,9679,9436],{"class":3967},[3526,9681,9500],{"class":3950},[3526,9683,5554],{"class":3960},[3526,9685,4402],{"class":3950},[3526,9687,9688,9691,9694,9696,9698,9700,9702],{"class":3528,"line":3535},[3526,9689,9690],{"class":3954},"    Audiobook",[3526,9692,9693],{"class":3982}," audiobook",[3526,9695,4026],{"class":3950},[3526,9697,9517],{"class":3982},[3526,9699,3166],{"class":3950},[3526,9701,8550],{"class":3967},[3526,9703,4150],{"class":3950},[3526,9705,9706,9709,9712,9714,9717,9719,9721,9723,9725],{"class":3528,"line":3541},[3526,9707,9708],{"class":3954},"    String",[3526,9710,9711],{"class":3982}," authorName",[3526,9713,4026],{"class":3950},[3526,9715,9716],{"class":3982},"audiobook",[3526,9718,3166],{"class":3950},[3526,9720,9329],{"class":3967},[3526,9722,4163],{"class":3950},[3526,9724,9334],{"class":3967},[3526,9726,4150],{"class":3950},[3526,9728,9729,9732,9734,9736],{"class":3528,"line":3547},[3526,9730,9731],{"class":4039},"    return",[3526,9733,8624],{"class":4039},[3526,9735,9512],{"class":3967},[3526,9737,9738],{"class":3950},"(authorName);\n",[3526,9740,9741],{"class":3528,"line":3553},[3526,9742,6030],{"class":3950},[3189,9744,9745,9748,9749,9751,9752,9754],{},[3163,9746,9747],{},"TableView та MVVM:"," У наступних статтях ми побачимо, як ",[3154,9750,3512],{}," автоматично оновлюється при зміні даних через ",[3154,9753,9127],{}," у ViewModel. Це ключова перевага JavaFX Properties.",[3206,9756,9758],{"id":9757},"spinner-числове-введення-з-кнопками","Spinner: Числове введення з кнопками",[3150,9760,9761,9764],{},[3163,9762,9763],{},"Spinner"," дозволяє вводити числа з можливістю збільшення\u002Fзменшення через кнопки.",[3518,9766,9768],{"className":3941,"code":9767,"language":3943,"meta":3522,"style":3522},"Spinner\u003CInteger> durationSpinner = new Spinner\u003C>(0, 1000, 60, 5);\n\u002F\u002F min=0, max=1000, initialValue=60, step=5\n\nint duration = durationSpinner.getValue();\n",[3154,9769,9770,9810,9815,9819],{"__ignoreMap":3522},[3526,9771,9772,9774,9776,9778,9780,9783,9785,9787,9790,9792,9794,9796,9799,9801,9804,9806,9808],{"class":3528,"line":3529},[3526,9773,9763],{"class":3954},[3526,9775,4675],{"class":3950},[3526,9777,9558],{"class":3954},[3526,9779,8459],{"class":3950},[3526,9781,9782],{"class":3982},"durationSpinner",[3526,9784,4026],{"class":3950},[3526,9786,4040],{"class":4039},[3526,9788,9789],{"class":3954}," Spinner",[3526,9791,9422],{"class":3950},[3526,9793,6107],{"class":4206},[3526,9795,3173],{"class":3950},[3526,9797,9798],{"class":4206},"1000",[3526,9800,3173],{"class":3950},[3526,9802,9803],{"class":4206},"60",[3526,9805,3173],{"class":3950},[3526,9807,3786],{"class":4206},[3526,9809,4003],{"class":3950},[3526,9811,9812],{"class":3528,"line":3535},[3526,9813,9814],{"class":3976},"\u002F\u002F min=0, max=1000, initialValue=60, step=5\n",[3526,9816,9817],{"class":3528,"line":3541},[3526,9818,4837],{"emptyLinePlaceholder":4836},[3526,9820,9821,9824,9827,9829,9831,9833,9835],{"class":3528,"line":3547},[3526,9822,9823],{"class":3954},"int",[3526,9825,9826],{"class":3982}," duration",[3526,9828,4026],{"class":3950},[3526,9830,9782],{"class":3982},[3526,9832,3166],{"class":3950},[3526,9834,8550],{"class":3967},[3526,9836,4150],{"class":3950},[3206,9838,9840],{"id":9839},"datepicker-вибір-дати","DatePicker: Вибір дати",[3150,9842,9843,9845],{},[3163,9844,3347],{}," — календар для вибору дати.",[3518,9847,9849],{"className":3941,"code":9848,"language":3943,"meta":3522,"style":3522},"DatePicker releaseDatePicker = new DatePicker();\nreleaseDatePicker.setValue(LocalDate.now());\n\nLocalDate selectedDate = releaseDatePicker.getValue();\n",[3154,9850,9851,9867,9888,9892],{"__ignoreMap":3522},[3526,9852,9853,9855,9858,9860,9862,9865],{"class":3528,"line":3529},[3526,9854,3347],{"class":3954},[3526,9856,9857],{"class":3982}," releaseDatePicker",[3526,9859,4026],{"class":3950},[3526,9861,4040],{"class":4039},[3526,9863,9864],{"class":3967}," DatePicker",[3526,9866,4150],{"class":3950},[3526,9868,9869,9872,9874,9876,9878,9881,9883,9886],{"class":3528,"line":3535},[3526,9870,9871],{"class":3982},"releaseDatePicker",[3526,9873,3166],{"class":3950},[3526,9875,8516],{"class":3967},[3526,9877,3996],{"class":3950},[3526,9879,9880],{"class":3982},"LocalDate",[3526,9882,3166],{"class":3950},[3526,9884,9885],{"class":3967},"now",[3526,9887,4046],{"class":3950},[3526,9889,9890],{"class":3528,"line":3541},[3526,9891,4837],{"emptyLinePlaceholder":4836},[3526,9893,9894,9896,9899,9901,9903,9905,9907],{"class":3528,"line":3547},[3526,9895,9880],{"class":3954},[3526,9897,9898],{"class":3982}," selectedDate",[3526,9900,4026],{"class":3950},[3526,9902,9871],{"class":3982},[3526,9904,3166],{"class":3950},[3526,9906,8550],{"class":3967},[3526,9908,4150],{"class":3950},[3375,9910],{},[3145,9912,9914],{"id":9913},"event-handling-реакція-на-дії-користувача","Event Handling: Реакція на дії користувача",[3150,9916,9917,9918,9921],{},"Контроли без обробників подій — це лише статичні картинки. Справжня інтерактивність починається, коли додаток ",[3163,9919,9920],{},"реагує"," на дії користувача: клік миші, натискання клавіші, зміну тексту у полі.",[3206,9923,9925],{"id":9924},"обробники-подій-eventhandler-та-лямбда-вирази","Обробники подій: EventHandler та лямбда-вирази",[3150,9927,9928,9929,9932,9933,9936,9937,9939],{},"У JavaFX події обробляються через інтерфейс ",[3154,9930,9931],{},"EventHandler\u003CT>",", де ",[3154,9934,9935],{},"T"," — тип події. Найпоширеніша подія — ",[3154,9938,5974],{},", що генерується при натисканні кнопки, виборі елемента у списку, тощо.",[3150,9941,9942],{},[3163,9943,9944],{},"Традиційний підхід (до Java 8):",[3518,9946,9948],{"className":3941,"code":9947,"language":3943,"meta":3522,"style":3522},"button.setOnAction(new EventHandler\u003CActionEvent>() {\n    @Override\n    public void handle(ActionEvent event) {\n        System.out.println(\"Button clicked!\");\n    }\n});\n",[3154,9949,9950,9971,9977,9993,10011,10015],{"__ignoreMap":3522},[3526,9951,9952,9955,9957,9959,9961,9963,9965,9967,9969],{"class":3528,"line":3529},[3526,9953,9954],{"class":3982},"button",[3526,9956,3166],{"class":3950},[3526,9958,5548],{"class":3967},[3526,9960,3996],{"class":3950},[3526,9962,4040],{"class":4039},[3526,9964,5969],{"class":3954},[3526,9966,4675],{"class":3950},[3526,9968,5974],{"class":3954},[3526,9970,5977],{"class":3950},[3526,9972,9973,9975],{"class":3528,"line":3535},[3526,9974,4458],{"class":3950},[3526,9976,3955],{"class":3954},[3526,9978,9979,9981,9983,9985,9987,9989,9991],{"class":3528,"line":3541},[3526,9980,4411],{"class":3960},[3526,9982,3964],{"class":3954},[3526,9984,5992],{"class":3967},[3526,9986,3996],{"class":3950},[3526,9988,5974],{"class":3954},[3526,9990,5999],{"class":3982},[3526,9992,4433],{"class":3950},[3526,9994,9995,9997,9999,10001,10003,10005,10007,10009],{"class":3528,"line":3547},[3526,9996,6006],{"class":3982},[3526,9998,3166],{"class":3950},[3526,10000,3988],{"class":3982},[3526,10002,3166],{"class":3950},[3526,10004,3993],{"class":3967},[3526,10006,3996],{"class":3950},[3526,10008,6019],{"class":3999},[3526,10010,4003],{"class":3950},[3526,10012,10013],{"class":3528,"line":3553},[3526,10014,4449],{"class":3950},[3526,10016,10017],{"class":3528,"line":3559},[3526,10018,6030],{"class":3950},[3150,10020,10021],{},[3163,10022,10023],{},"Сучасний підхід (лямбда-вираз):",[3518,10025,10027],{"className":3941,"code":10026,"language":3943,"meta":3522,"style":3522},"button.setOnAction(event -> {\n    System.out.println(\"Button clicked!\");\n});\n",[3154,10028,10029,10043,10061],{"__ignoreMap":3522},[3526,10030,10031,10033,10035,10037,10039,10041],{"class":3528,"line":3529},[3526,10032,9954],{"class":3982},[3526,10034,3166],{"class":3950},[3526,10036,5548],{"class":3967},[3526,10038,5551],{"class":3950},[3526,10040,5554],{"class":3960},[3526,10042,4402],{"class":3950},[3526,10044,10045,10047,10049,10051,10053,10055,10057,10059],{"class":3528,"line":3535},[3526,10046,3983],{"class":3982},[3526,10048,3166],{"class":3950},[3526,10050,3988],{"class":3982},[3526,10052,3166],{"class":3950},[3526,10054,3993],{"class":3967},[3526,10056,3996],{"class":3950},[3526,10058,6019],{"class":3999},[3526,10060,4003],{"class":3950},[3526,10062,10063],{"class":3528,"line":3541},[3526,10064,6030],{"class":3950},[3150,10066,10067],{},"Якщо обробник складається з одного виразу, фігурні дужки можна опустити:",[3518,10069,10071],{"className":3941,"code":10070,"language":3943,"meta":3522,"style":3522},"button.setOnAction(event -> System.out.println(\"Button clicked!\"));\n",[3154,10072,10073],{"__ignoreMap":3522},[3526,10074,10075,10077,10079,10081,10083,10085,10088,10090,10092,10094,10096,10098,10100],{"class":3528,"line":3529},[3526,10076,9954],{"class":3982},[3526,10078,3166],{"class":3950},[3526,10080,5548],{"class":3967},[3526,10082,5551],{"class":3950},[3526,10084,5554],{"class":3960},[3526,10086,10087],{"class":3982}," System",[3526,10089,3166],{"class":3950},[3526,10091,3988],{"class":3982},[3526,10093,3166],{"class":3950},[3526,10095,3993],{"class":3967},[3526,10097,3996],{"class":3950},[3526,10099,6019],{"class":3999},[3526,10101,4181],{"class":3950},[3206,10103,10105],{"id":10104},"типи-подій","Типи подій",[3150,10107,10108],{},"JavaFX підтримує широкий спектр подій:",[3150,10110,10111],{},[3163,10112,10113],{},"Події миші:",[6171,10115,10116,10122,10128,10134],{},[4501,10117,10118,10121],{},[3154,10119,10120],{},"setOnMouseClicked()"," — клік миші.",[4501,10123,10124,10127],{},[3154,10125,10126],{},"setOnMouseEntered()"," — курсор увійшов у межі елемента.",[4501,10129,10130,10133],{},[3154,10131,10132],{},"setOnMouseExited()"," — курсор вийшов з меж елемента.",[4501,10135,10136,10139,10140,10143],{},[3154,10137,10138],{},"setOnMousePressed()"," \u002F ",[3154,10141,10142],{},"setOnMouseReleased()"," — натискання\u002Fвідпускання кнопки миші.",[3150,10145,10146],{},[3163,10147,10148],{},"Події клавіатури:",[6171,10150,10151,10157,10163],{},[4501,10152,10153,10156],{},[3154,10154,10155],{},"setOnKeyPressed()"," — клавіша натиснута.",[4501,10158,10159,10162],{},[3154,10160,10161],{},"setOnKeyReleased()"," — клавіша відпущена.",[4501,10164,10165,10168],{},[3154,10166,10167],{},"setOnKeyTyped()"," — символ введено (враховує модифікатори, наприклад Shift).",[3150,10170,10171],{},[3163,10172,10173],{},"Події зміни значення:",[6171,10175,10176,10182],{},[4501,10177,10178,10181],{},[3154,10179,10180],{},"textProperty().addListener()"," — зміна тексту у TextField.",[4501,10183,10184,10187],{},[3154,10185,10186],{},"selectedItemProperty().addListener()"," — зміна вибору у ComboBox\u002FListView.",[3150,10189,10190],{},"Приклад: підсвічування кнопки при наведенні миші:",[3518,10192,10194],{"className":3941,"code":10193,"language":3943,"meta":3522,"style":3522},"Button hoverButton = new Button(\"Hover me\");\n\nhoverButton.setOnMouseEntered(event -> {\n    hoverButton.setStyle(\"-fx-background-color: lightblue;\");\n});\n\nhoverButton.setOnMouseExited(event -> {\n    hoverButton.setStyle(\"\");\n});\n",[3154,10195,10196,10216,10220,10236,10252,10256,10260,10275,10289],{"__ignoreMap":3522},[3526,10197,10198,10200,10203,10205,10207,10209,10211,10214],{"class":3528,"line":3529},[3526,10199,3503],{"class":3954},[3526,10201,10202],{"class":3982}," hoverButton",[3526,10204,4026],{"class":3950},[3526,10206,4040],{"class":4039},[3526,10208,5531],{"class":3967},[3526,10210,3996],{"class":3950},[3526,10212,10213],{"class":3999},"\"Hover me\"",[3526,10215,4003],{"class":3950},[3526,10217,10218],{"class":3528,"line":3535},[3526,10219,4837],{"emptyLinePlaceholder":4836},[3526,10221,10222,10225,10227,10230,10232,10234],{"class":3528,"line":3541},[3526,10223,10224],{"class":3982},"hoverButton",[3526,10226,3166],{"class":3950},[3526,10228,10229],{"class":3967},"setOnMouseEntered",[3526,10231,5551],{"class":3950},[3526,10233,5554],{"class":3960},[3526,10235,4402],{"class":3950},[3526,10237,10238,10241,10243,10245,10247,10250],{"class":3528,"line":3547},[3526,10239,10240],{"class":3982},"    hoverButton",[3526,10242,3166],{"class":3950},[3526,10244,5459],{"class":3967},[3526,10246,3996],{"class":3950},[3526,10248,10249],{"class":3999},"\"-fx-background-color: lightblue;\"",[3526,10251,4003],{"class":3950},[3526,10253,10254],{"class":3528,"line":3553},[3526,10255,6030],{"class":3950},[3526,10257,10258],{"class":3528,"line":3559},[3526,10259,4837],{"emptyLinePlaceholder":4836},[3526,10261,10262,10264,10266,10269,10271,10273],{"class":3528,"line":3565},[3526,10263,10224],{"class":3982},[3526,10265,3166],{"class":3950},[3526,10267,10268],{"class":3967},"setOnMouseExited",[3526,10270,5551],{"class":3950},[3526,10272,5554],{"class":3960},[3526,10274,4402],{"class":3950},[3526,10276,10277,10279,10281,10283,10285,10287],{"class":3528,"line":3571},[3526,10278,10240],{"class":3982},[3526,10280,3166],{"class":3950},[3526,10282,5459],{"class":3967},[3526,10284,3996],{"class":3950},[3526,10286,8423],{"class":3999},[3526,10288,4003],{"class":3950},[3526,10290,10291],{"class":3528,"line":3577},[3526,10292,6030],{"class":3950},[3206,10294,10296],{"id":10295},"делегування-до-viewmodel-розділення-відповідальностей","Делегування до ViewModel: Розділення відповідальностей",[3150,10298,10299,10300,10303],{},"У реальних додатках обробник події не повинен містити бізнес-логіку. Його завдання — ",[3163,10301,10302],{},"делегувати"," виклик до ViewModel або Service.",[3150,10305,10306],{},[3163,10307,10308],{},"Антипатерн (вся логіка у Controller):",[3518,10310,10312],{"className":3941,"code":10311,"language":3943,"meta":3522,"style":3522},"saveButton.setOnAction(event -> {\n    String title = titleField.getText();\n    if (title.isEmpty()) {\n        showError(\"Title is required\");\n        return;\n    }\n    \n    try (Connection conn = dataSource.getConnection()) {\n        PreparedStatement stmt = conn.prepareStatement(\n            \"INSERT INTO audiobooks (title, author_id) VALUES (?, ?)\"\n        );\n        stmt.setString(1, title);\n        stmt.setUUID(2, selectedAuthor.getId());\n        stmt.executeUpdate();\n        showSuccess(\"Audiobook saved\");\n    } catch (SQLException e) {\n        showError(\"Failed to save: \" + e.getMessage());\n    }\n});\n",[3154,10313,10314,10328,10344,10360,10372,10380,10384,10388,10413,10433,10438,10443,10460,10485,10496,10508,10525,10546,10550],{"__ignoreMap":3522},[3526,10315,10316,10318,10320,10322,10324,10326],{"class":3528,"line":3529},[3526,10317,7535],{"class":3982},[3526,10319,3166],{"class":3950},[3526,10321,5548],{"class":3967},[3526,10323,5551],{"class":3950},[3526,10325,5554],{"class":3960},[3526,10327,4402],{"class":3950},[3526,10329,10330,10332,10334,10336,10338,10340,10342],{"class":3528,"line":3535},[3526,10331,9708],{"class":3954},[3526,10333,8301],{"class":3982},[3526,10335,4026],{"class":3950},[3526,10337,8211],{"class":3982},[3526,10339,3166],{"class":3950},[3526,10341,8310],{"class":3967},[3526,10343,4150],{"class":3950},[3526,10345,10346,10348,10350,10352,10354,10357],{"class":3528,"line":3541},[3526,10347,8366],{"class":4039},[3526,10349,5910],{"class":3950},[3526,10351,3666],{"class":3982},[3526,10353,3166],{"class":3950},[3526,10355,10356],{"class":3967},"isEmpty",[3526,10358,10359],{"class":3950},"()) {\n",[3526,10361,10362,10365,10367,10370],{"class":3528,"line":3547},[3526,10363,10364],{"class":3967},"        showError",[3526,10366,3996],{"class":3950},[3526,10368,10369],{"class":3999},"\"Title is required\"",[3526,10371,4003],{"class":3950},[3526,10373,10374,10377],{"class":3528,"line":3553},[3526,10375,10376],{"class":4039},"        return",[3526,10378,10379],{"class":3950},";\n",[3526,10381,10382],{"class":3528,"line":3559},[3526,10383,4449],{"class":3950},[3526,10385,10386],{"class":3528,"line":3565},[3526,10387,4008],{"class":3950},[3526,10389,10390,10393,10395,10398,10401,10403,10406,10408,10411],{"class":3528,"line":3571},[3526,10391,10392],{"class":4039},"    try",[3526,10394,5910],{"class":3950},[3526,10396,10397],{"class":3954},"Connection",[3526,10399,10400],{"class":3982}," conn",[3526,10402,4026],{"class":3950},[3526,10404,10405],{"class":3982},"dataSource",[3526,10407,3166],{"class":3950},[3526,10409,10410],{"class":3967},"getConnection",[3526,10412,10359],{"class":3950},[3526,10414,10415,10418,10421,10423,10426,10428,10431],{"class":3528,"line":3577},[3526,10416,10417],{"class":3954},"        PreparedStatement",[3526,10419,10420],{"class":3982}," stmt",[3526,10422,4026],{"class":3950},[3526,10424,10425],{"class":3982},"conn",[3526,10427,3166],{"class":3950},[3526,10429,10430],{"class":3967},"prepareStatement",[3526,10432,6546],{"class":3950},[3526,10434,10435],{"class":3528,"line":3583},[3526,10436,10437],{"class":3999},"            \"INSERT INTO audiobooks (title, author_id) VALUES (?, ?)\"\n",[3526,10439,10440],{"class":3528,"line":3589},[3526,10441,10442],{"class":3950},"        );\n",[3526,10444,10445,10448,10450,10453,10455,10457],{"class":3528,"line":3595},[3526,10446,10447],{"class":3982},"        stmt",[3526,10449,3166],{"class":3950},[3526,10451,10452],{"class":3967},"setString",[3526,10454,3996],{"class":3950},[3526,10456,3852],{"class":4206},[3526,10458,10459],{"class":3950},", title);\n",[3526,10461,10462,10464,10466,10469,10471,10473,10475,10478,10480,10483],{"class":3528,"line":3601},[3526,10463,10447],{"class":3982},[3526,10465,3166],{"class":3950},[3526,10467,10468],{"class":3967},"setUUID",[3526,10470,3996],{"class":3950},[3526,10472,3784],{"class":4206},[3526,10474,3173],{"class":3950},[3526,10476,10477],{"class":3982},"selectedAuthor",[3526,10479,3166],{"class":3950},[3526,10481,10482],{"class":3967},"getId",[3526,10484,4046],{"class":3950},[3526,10486,10487,10489,10491,10494],{"class":3528,"line":3607},[3526,10488,10447],{"class":3982},[3526,10490,3166],{"class":3950},[3526,10492,10493],{"class":3967},"executeUpdate",[3526,10495,4150],{"class":3950},[3526,10497,10498,10501,10503,10506],{"class":3528,"line":3613},[3526,10499,10500],{"class":3967},"        showSuccess",[3526,10502,3996],{"class":3950},[3526,10504,10505],{"class":3999},"\"Audiobook saved\"",[3526,10507,4003],{"class":3950},[3526,10509,10510,10512,10515,10517,10520,10523],{"class":3528,"line":3619},[3526,10511,8405],{"class":3950},[3526,10513,10514],{"class":4039},"catch",[3526,10516,5910],{"class":3950},[3526,10518,10519],{"class":3954},"SQLException",[3526,10521,10522],{"class":3982}," e",[3526,10524,4433],{"class":3950},[3526,10526,10527,10529,10531,10534,10536,10539,10541,10544],{"class":3528,"line":4612},[3526,10528,10364],{"class":3967},[3526,10530,3996],{"class":3950},[3526,10532,10533],{"class":3999},"\"Failed to save: \"",[3526,10535,9322],{"class":3950},[3526,10537,10538],{"class":3982},"e",[3526,10540,3166],{"class":3950},[3526,10542,10543],{"class":3967},"getMessage",[3526,10545,4046],{"class":3950},[3526,10547,10548],{"class":3528,"line":4618},[3526,10549,4449],{"class":3950},[3526,10551,10552],{"class":3528,"line":4624},[3526,10553,6030],{"class":3950},[3150,10555,10556],{},[3163,10557,10558],{},"Правильний підхід (делегування до ViewModel):",[3518,10560,10562],{"className":3941,"code":10561,"language":3943,"meta":3522,"style":3522},"saveButton.setOnAction(event -> {\n    viewModel.saveAudiobook();\n});\n",[3154,10563,10564,10578,10590],{"__ignoreMap":3522},[3526,10565,10566,10568,10570,10572,10574,10576],{"class":3528,"line":3529},[3526,10567,7535],{"class":3982},[3526,10569,3166],{"class":3950},[3526,10571,5548],{"class":3967},[3526,10573,5551],{"class":3950},[3526,10575,5554],{"class":3960},[3526,10577,4402],{"class":3950},[3526,10579,10580,10583,10585,10588],{"class":3528,"line":3535},[3526,10581,10582],{"class":3982},"    viewModel",[3526,10584,3166],{"class":3950},[3526,10586,10587],{"class":3967},"saveAudiobook",[3526,10589,4150],{"class":3950},[3526,10591,10592],{"class":3528,"line":3541},[3526,10593,6030],{"class":3950},[3150,10595,10596],{},"Вся логіка (валідація, виклик Repository, обробка помилок) знаходиться у ViewModel. Controller лише передає подію далі. Це робить код тестованим, повторно використовуваним та відповідає принципу Single Responsibility.",[4344,10598,10599,10602,10603,10606],{},[3163,10600,10601],{},"Не виконуйте тривалі операції у UI-потоці!"," Якщо обробник події виконує JDBC-запит, читання файлу або мережевий запит, додаток \"зависне\" — користувач не зможе взаємодіяти з інтерфейсом, поки операція не завершиться. Для тривалих операцій використовуйте ",[3154,10604,10605],{},"Task"," (ми розглянемо це у статті про ViewModel).",[3375,10608],{},[3145,10610,10612],{"id":10611},"fxml-декларативний-опис-інтерфейсу","FXML: Декларативний опис інтерфейсу",[3150,10614,10615,10616,10619,10620,3173,10623,10626],{},"До цього моменту ми створювали UI ",[3163,10617,10618],{},"імперативно"," — через Java-код: ",[3154,10621,10622],{},"new VBox()",[3154,10624,10625],{},"button.setOnAction()",", тощо. Це працює, але має недоліки:",[6171,10628,10629,10635,10641],{},[4501,10630,10631,10634],{},[3163,10632,10633],{},"Змішування логіки та представлення."," Код побудови UI та бізнес-логіка знаходяться в одному класі.",[4501,10636,10637,10640],{},[3163,10638,10639],{},"Важко візуалізувати структуру."," Щоб зрозуміти, як виглядає інтерфейс, потрібно читати десятки рядків Java-коду.",[4501,10642,10643,10646],{},[3163,10644,10645],{},"Неможливість використання візуальних редакторів."," Дизайнери не можуть працювати з Java-кодом.",[3150,10648,10649,10652,10653,10656,10657,10660],{},[3163,10650,10651],{},"FXML"," вирішує ці проблеми. Це XML-формат для ",[3163,10654,10655],{},"декларативного"," опису UI, подібний до HTML у веб-розробці. Ви описуєте структуру інтерфейсу у ",[3154,10658,10659],{},".fxml"," файлі, а Java-код лише завантажує його та підключає логіку.",[3206,10662,10664],{"id":10663},"приклад-fxml-форма-додавання-аудіокниги","Приклад FXML: Форма додавання аудіокниги",[3150,10666,10667,10668,4378],{},"Створіть файл ",[3154,10669,10670],{},"audiobook-form.fxml",[3518,10672,10674],{"className":4660,"code":10673,"language":4662,"meta":3522,"style":3522},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\u003C?import javafx.scene.control.*?>\n\u003C?import javafx.scene.layout.*?>\n\u003C?import javafx.geometry.Insets?>\n\n\u003CVBox xmlns:fx=\"http:\u002F\u002Fjavafx.com\u002Ffxml\" \n      fx:controller=\"dev.kostyl.audiobook.controller.AudiobookFormController\"\n      spacing=\"10\" alignment=\"CENTER\">\n    \n    \u003Cpadding>\n        \u003CInsets top=\"20\" right=\"20\" bottom=\"20\" left=\"20\"\u002F>\n    \u003C\u002Fpadding>\n    \n    \u003CLabel text=\"Add New Audiobook\" style=\"-fx-font-size: 18px; -fx-font-weight: bold;\"\u002F>\n    \n    \u003CGridPane hgap=\"10\" vgap=\"10\">\n        \u003CLabel text=\"Title:\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"\u002F>\n        \u003CTextField fx:id=\"titleField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"\u002F>\n        \n        \u003CLabel text=\"Author:\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"\u002F>\n        \u003CComboBox fx:id=\"authorComboBox\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"\u002F>\n        \n        \u003CLabel text=\"Duration (min):\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"2\"\u002F>\n        \u003CSpinner fx:id=\"durationSpinner\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"\u002F>\n    \u003C\u002FGridPane>\n    \n    \u003CButton fx:id=\"saveButton\" text=\"Save Audiobook\" onAction=\"#onSaveClicked\"\u002F>\n    \n\u003C\u002FVBox>\n",[3154,10675,10676,10705,10709,10723,10736,10749,10753,10769,10779,10799,10803,10811,10850,10858,10862,10885,10889,10911,10940,10969,10973,10999,11026,11030,11058,11085,11093,11097,11126,11130],{"__ignoreMap":3522},[3526,10677,10678,10681,10683,10687,10690,10694,10697,10699,10702],{"class":3528,"line":3529},[3526,10679,10680],{"class":4674},"\u003C?",[3526,10682,4662],{"class":4678},[3526,10684,10686],{"class":10685},"sa4r_"," version",[3526,10688,10689],{"class":3950},"=",[3526,10691,10693],{"class":10692},"su9tN","\"1.0\"",[3526,10695,10696],{"class":10685}," encoding",[3526,10698,10689],{"class":3950},[3526,10700,10701],{"class":10692},"\"UTF-8\"",[3526,10703,10704],{"class":4674},"?>\n",[3526,10706,10707],{"class":3528,"line":3535},[3526,10708,4837],{"emptyLinePlaceholder":4836},[3526,10710,10711,10713,10715,10718,10721],{"class":3528,"line":3541},[3526,10712,10680],{"class":4674},[3526,10714,5327],{"class":4678},[3526,10716,10717],{"class":10685}," javafx",[3526,10719,10720],{"class":3950},".scene.control.*",[3526,10722,10704],{"class":4674},[3526,10724,10725,10727,10729,10731,10734],{"class":3528,"line":3547},[3526,10726,10680],{"class":4674},[3526,10728,5327],{"class":4678},[3526,10730,10717],{"class":10685},[3526,10732,10733],{"class":3950},".scene.layout.*",[3526,10735,10704],{"class":4674},[3526,10737,10738,10740,10742,10744,10747],{"class":3528,"line":3553},[3526,10739,10680],{"class":4674},[3526,10741,5327],{"class":4678},[3526,10743,10717],{"class":10685},[3526,10745,10746],{"class":3950},".geometry.Insets",[3526,10748,10704],{"class":4674},[3526,10750,10751],{"class":3528,"line":3559},[3526,10752,4837],{"emptyLinePlaceholder":4836},[3526,10754,10755,10757,10759,10762,10764,10767],{"class":3528,"line":3565},[3526,10756,4675],{"class":4674},[3526,10758,3475],{"class":4678},[3526,10760,10761],{"class":10685}," xmlns:fx",[3526,10763,10689],{"class":3950},[3526,10765,10766],{"class":10692},"\"http:\u002F\u002Fjavafx.com\u002Ffxml\"",[3526,10768,9505],{"class":3950},[3526,10770,10771,10774,10776],{"class":3528,"line":3571},[3526,10772,10773],{"class":10685},"      fx:controller",[3526,10775,10689],{"class":3950},[3526,10777,10778],{"class":10692},"\"dev.kostyl.audiobook.controller.AudiobookFormController\"\n",[3526,10780,10781,10784,10786,10789,10792,10794,10797],{"class":3528,"line":3577},[3526,10782,10783],{"class":10685},"      spacing",[3526,10785,10689],{"class":3950},[3526,10787,10788],{"class":10692},"\"10\"",[3526,10790,10791],{"class":10685}," alignment",[3526,10793,10689],{"class":3950},[3526,10795,10796],{"class":10692},"\"CENTER\"",[3526,10798,4682],{"class":4674},[3526,10800,10801],{"class":3528,"line":3583},[3526,10802,4008],{"class":3950},[3526,10804,10805,10807,10809],{"class":3528,"line":3589},[3526,10806,4687],{"class":4674},[3526,10808,5826],{"class":4678},[3526,10810,4682],{"class":4674},[3526,10812,10813,10815,10818,10821,10823,10826,10829,10831,10833,10836,10838,10840,10843,10845,10847],{"class":3528,"line":3595},[3526,10814,4697],{"class":4674},[3526,10816,10817],{"class":4678},"Insets",[3526,10819,10820],{"class":10685}," top",[3526,10822,10689],{"class":3950},[3526,10824,10825],{"class":10692},"\"20\"",[3526,10827,10828],{"class":10685}," right",[3526,10830,10689],{"class":3950},[3526,10832,10825],{"class":10692},[3526,10834,10835],{"class":10685}," bottom",[3526,10837,10689],{"class":3950},[3526,10839,10825],{"class":10692},[3526,10841,10842],{"class":10685}," left",[3526,10844,10689],{"class":3950},[3526,10846,10825],{"class":10692},[3526,10848,10849],{"class":4674},"\u002F>\n",[3526,10851,10852,10854,10856],{"class":3528,"line":3601},[3526,10853,4754],{"class":4674},[3526,10855,5826],{"class":4678},[3526,10857,4682],{"class":4674},[3526,10859,10860],{"class":3528,"line":3607},[3526,10861,4008],{"class":3950},[3526,10863,10864,10866,10868,10871,10873,10876,10879,10881,10883],{"class":3528,"line":3613},[3526,10865,4687],{"class":4674},[3526,10867,3509],{"class":4678},[3526,10869,10870],{"class":10685}," text",[3526,10872,10689],{"class":3950},[3526,10874,10875],{"class":10692},"\"Add New Audiobook\"",[3526,10877,10878],{"class":10685}," style",[3526,10880,10689],{"class":3950},[3526,10882,8163],{"class":10692},[3526,10884,10849],{"class":4674},[3526,10886,10887],{"class":3528,"line":3619},[3526,10888,4008],{"class":3950},[3526,10890,10891,10893,10895,10898,10900,10902,10905,10907,10909],{"class":3528,"line":4612},[3526,10892,4687],{"class":4674},[3526,10894,3484],{"class":4678},[3526,10896,10897],{"class":10685}," hgap",[3526,10899,10689],{"class":3950},[3526,10901,10788],{"class":10692},[3526,10903,10904],{"class":10685}," vgap",[3526,10906,10689],{"class":3950},[3526,10908,10788],{"class":10692},[3526,10910,4682],{"class":4674},[3526,10912,10913,10915,10917,10919,10921,10923,10926,10928,10931,10934,10936,10938],{"class":3528,"line":4618},[3526,10914,4697],{"class":4674},[3526,10916,3509],{"class":4678},[3526,10918,10870],{"class":10685},[3526,10920,10689],{"class":3950},[3526,10922,7268],{"class":10692},[3526,10924,10925],{"class":10685}," GridPane.columnIndex",[3526,10927,10689],{"class":3950},[3526,10929,10930],{"class":10692},"\"0\"",[3526,10932,10933],{"class":10685}," GridPane.rowIndex",[3526,10935,10689],{"class":3950},[3526,10937,10930],{"class":10692},[3526,10939,10849],{"class":4674},[3526,10941,10942,10944,10946,10949,10951,10954,10956,10958,10961,10963,10965,10967],{"class":3528,"line":4624},[3526,10943,4697],{"class":4674},[3526,10945,3506],{"class":4678},[3526,10947,10948],{"class":10685}," fx:id",[3526,10950,10689],{"class":3950},[3526,10952,10953],{"class":10692},"\"titleField\"",[3526,10955,10925],{"class":10685},[3526,10957,10689],{"class":3950},[3526,10959,10960],{"class":10692},"\"1\"",[3526,10962,10933],{"class":10685},[3526,10964,10689],{"class":3950},[3526,10966,10930],{"class":10692},[3526,10968,10849],{"class":4674},[3526,10970,10971],{"class":3528,"line":4630},[3526,10972,5471],{"class":3950},[3526,10974,10975,10977,10979,10981,10983,10985,10987,10989,10991,10993,10995,10997],{"class":3528,"line":4918},[3526,10976,4697],{"class":4674},[3526,10978,3509],{"class":4678},[3526,10980,10870],{"class":10685},[3526,10982,10689],{"class":3950},[3526,10984,7327],{"class":10692},[3526,10986,10925],{"class":10685},[3526,10988,10689],{"class":3950},[3526,10990,10930],{"class":10692},[3526,10992,10933],{"class":10685},[3526,10994,10689],{"class":3950},[3526,10996,10960],{"class":10692},[3526,10998,10849],{"class":4674},[3526,11000,11001,11003,11005,11007,11009,11012,11014,11016,11018,11020,11022,11024],{"class":3528,"line":4928},[3526,11002,4697],{"class":4674},[3526,11004,8442],{"class":4678},[3526,11006,10948],{"class":10685},[3526,11008,10689],{"class":3950},[3526,11010,11011],{"class":10692},"\"authorComboBox\"",[3526,11013,10925],{"class":10685},[3526,11015,10689],{"class":3950},[3526,11017,10960],{"class":10692},[3526,11019,10933],{"class":10685},[3526,11021,10689],{"class":3950},[3526,11023,10960],{"class":10692},[3526,11025,10849],{"class":4674},[3526,11027,11028],{"class":3528,"line":4948},[3526,11029,5471],{"class":3950},[3526,11031,11032,11034,11036,11038,11040,11043,11045,11047,11049,11051,11053,11056],{"class":3528,"line":4958},[3526,11033,4697],{"class":4674},[3526,11035,3509],{"class":4678},[3526,11037,10870],{"class":10685},[3526,11039,10689],{"class":3950},[3526,11041,11042],{"class":10692},"\"Duration (min):\"",[3526,11044,10925],{"class":10685},[3526,11046,10689],{"class":3950},[3526,11048,10930],{"class":10692},[3526,11050,10933],{"class":10685},[3526,11052,10689],{"class":3950},[3526,11054,11055],{"class":10692},"\"2\"",[3526,11057,10849],{"class":4674},[3526,11059,11060,11062,11064,11066,11068,11071,11073,11075,11077,11079,11081,11083],{"class":3528,"line":4968},[3526,11061,4697],{"class":4674},[3526,11063,9763],{"class":4678},[3526,11065,10948],{"class":10685},[3526,11067,10689],{"class":3950},[3526,11069,11070],{"class":10692},"\"durationSpinner\"",[3526,11072,10925],{"class":10685},[3526,11074,10689],{"class":3950},[3526,11076,10960],{"class":10692},[3526,11078,10933],{"class":10685},[3526,11080,10689],{"class":3950},[3526,11082,11055],{"class":10692},[3526,11084,10849],{"class":4674},[3526,11086,11087,11089,11091],{"class":3528,"line":4977},[3526,11088,4754],{"class":4674},[3526,11090,3484],{"class":4678},[3526,11092,4682],{"class":4674},[3526,11094,11095],{"class":3528,"line":5584},[3526,11096,4008],{"class":3950},[3526,11098,11099,11101,11103,11105,11107,11110,11112,11114,11116,11119,11121,11124],{"class":3528,"line":5589},[3526,11100,4687],{"class":4674},[3526,11102,3503],{"class":4678},[3526,11104,10948],{"class":10685},[3526,11106,10689],{"class":3950},[3526,11108,11109],{"class":10692},"\"saveButton\"",[3526,11111,10870],{"class":10685},[3526,11113,10689],{"class":3950},[3526,11115,7528],{"class":10692},[3526,11117,11118],{"class":10685}," onAction",[3526,11120,10689],{"class":3950},[3526,11122,11123],{"class":10692},"\"#onSaveClicked\"",[3526,11125,10849],{"class":4674},[3526,11127,11128],{"class":3528,"line":5595},[3526,11129,4008],{"class":3950},[3526,11131,11132,11134,11136],{"class":3528,"line":5612},[3526,11133,4709],{"class":4674},[3526,11135,3475],{"class":4678},[3526,11137,4682],{"class":4674},[3150,11139,11140],{},[3163,11141,11142],{},"Розбір FXML:",[6171,11144,11145,11156,11167,11175,11183,11191],{},[4501,11146,11147,11152,11153,11155],{},[3163,11148,11149],{},[3154,11150,11151],{},"\u003C?import ...?>"," — імпорт класів JavaFX (аналог ",[3154,11154,5327],{}," у Java).",[4501,11157,11158,11163,11164,11166],{},[3163,11159,11160],{},[3154,11161,11162],{},"\u003CVBox>"," — кореневий елемент, відповідає ",[3154,11165,10622],{}," у Java.",[4501,11168,11169,11174],{},[3163,11170,11171],{},[3154,11172,11173],{},"fx:controller"," — вказує клас Controller, що керує цим FXML.",[4501,11176,11177,11182],{},[3163,11178,11179],{},[3154,11180,11181],{},"fx:id"," — унікальний ідентифікатор елемента, що дозволяє звертатися до нього з Java-коду.",[4501,11184,11185,11190],{},[3163,11186,11187],{},[3154,11188,11189],{},"onAction=\"#onSaveClicked\""," — прив'язка події до методу Controller.",[4501,11192,11193,11198],{},[3163,11194,11195],{},[3154,11196,11197],{},"GridPane.columnIndex"," — attached property (властивість, що встановлюється батьківським контейнером).",[3206,11200,11202],{"id":11201},"controller-звязок-fxml-з-java-кодом","Controller: Зв'язок FXML з Java-кодом",[3150,11204,5298,11205,4378],{},[3154,11206,11207],{},"AudiobookFormController",[3518,11209,11211],{"className":3941,"code":11210,"language":3943,"meta":3522,"style":3522},"package dev.kostyl.audiobook.controller;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.*;\n\npublic class AudiobookFormController {\n\n    @FXML\n    private TextField titleField;\n    \n    @FXML\n    private ComboBox\u003CString> authorComboBox;\n    \n    @FXML\n    private Spinner\u003CInteger> durationSpinner;\n    \n    @FXML\n    private Button saveButton;\n    \n    @FXML\n    public void initialize() {\n        \u002F\u002F Викликається автоматично після завантаження FXML\n        authorComboBox.getItems().addAll(\"Author 1\", \"Author 2\", \"Author 3\");\n        durationSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 1000, 60));\n    }\n    \n    @FXML\n    private void onSaveClicked() {\n        String title = titleField.getText();\n        String author = authorComboBox.getValue();\n        int duration = durationSpinner.getValue();\n        \n        System.out.println(\"Saving: \" + title + \" by \" + author + \", \" + duration + \" min\");\n        \u002F\u002F Тут буде виклик viewModel.saveAudiobook()\n    }\n}\n",[3154,11212,11213,11220,11224,11231,11238,11242,11253,11257,11264,11275,11279,11285,11302,11306,11312,11328,11332,11338,11348,11352,11358,11370,11375,11405,11439,11443,11447,11453,11464,11481,11498,11515,11519,11555,11560,11564],{"__ignoreMap":3522},[3526,11214,11215,11217],{"class":3528,"line":3529},[3526,11216,5315],{"class":3960},[3526,11218,11219],{"class":3950}," dev.kostyl.audiobook.controller;\n",[3526,11221,11222],{"class":3528,"line":3535},[3526,11223,4837],{"emptyLinePlaceholder":4836},[3526,11225,11226,11228],{"class":3528,"line":3541},[3526,11227,5327],{"class":3960},[3526,11229,11230],{"class":3950}," javafx.fxml.FXML;\n",[3526,11232,11233,11235],{"class":3528,"line":3547},[3526,11234,5327],{"class":3960},[3526,11236,11237],{"class":3950}," javafx.scene.control.*;\n",[3526,11239,11240],{"class":3528,"line":3553},[3526,11241,4837],{"emptyLinePlaceholder":4836},[3526,11243,11244,11246,11248,11251],{"class":3528,"line":3559},[3526,11245,3961],{"class":3960},[3526,11247,4390],{"class":3960},[3526,11249,11250],{"class":3954}," AudiobookFormController",[3526,11252,4402],{"class":3950},[3526,11254,11255],{"class":3528,"line":3565},[3526,11256,4837],{"emptyLinePlaceholder":4836},[3526,11258,11259,11261],{"class":3528,"line":3571},[3526,11260,4458],{"class":3950},[3526,11262,11263],{"class":3954},"FXML\n",[3526,11265,11266,11269,11271,11273],{"class":3528,"line":3577},[3526,11267,11268],{"class":3960},"    private",[3526,11270,6568],{"class":3954},[3526,11272,8198],{"class":3982},[3526,11274,10379],{"class":3950},[3526,11276,11277],{"class":3528,"line":3583},[3526,11278,4008],{"class":3950},[3526,11280,11281,11283],{"class":3528,"line":3589},[3526,11282,4458],{"class":3950},[3526,11284,11263],{"class":3954},[3526,11286,11287,11289,11291,11293,11295,11297,11300],{"class":3528,"line":3595},[3526,11288,11268],{"class":3960},[3526,11290,8469],{"class":3954},[3526,11292,4675],{"class":3950},[3526,11294,4424],{"class":3954},[3526,11296,8459],{"class":3950},[3526,11298,11299],{"class":3982},"authorComboBox",[3526,11301,10379],{"class":3950},[3526,11303,11304],{"class":3528,"line":3601},[3526,11305,4008],{"class":3950},[3526,11307,11308,11310],{"class":3528,"line":3607},[3526,11309,4458],{"class":3950},[3526,11311,11263],{"class":3954},[3526,11313,11314,11316,11318,11320,11322,11324,11326],{"class":3528,"line":3613},[3526,11315,11268],{"class":3960},[3526,11317,9789],{"class":3954},[3526,11319,4675],{"class":3950},[3526,11321,9558],{"class":3954},[3526,11323,8459],{"class":3950},[3526,11325,9782],{"class":3982},[3526,11327,10379],{"class":3950},[3526,11329,11330],{"class":3528,"line":3619},[3526,11331,4008],{"class":3950},[3526,11333,11334,11336],{"class":3528,"line":4612},[3526,11335,4458],{"class":3950},[3526,11337,11263],{"class":3954},[3526,11339,11340,11342,11344,11346],{"class":3528,"line":4618},[3526,11341,11268],{"class":3960},[3526,11343,5531],{"class":3954},[3526,11345,7429],{"class":3982},[3526,11347,10379],{"class":3950},[3526,11349,11350],{"class":3528,"line":4624},[3526,11351,4008],{"class":3950},[3526,11353,11354,11356],{"class":3528,"line":4630},[3526,11355,4458],{"class":3950},[3526,11357,11263],{"class":3954},[3526,11359,11360,11362,11364,11367],{"class":3528,"line":4918},[3526,11361,4411],{"class":3960},[3526,11363,3964],{"class":3954},[3526,11365,11366],{"class":3967}," initialize",[3526,11368,11369],{"class":3950},"() {\n",[3526,11371,11372],{"class":3528,"line":4928},[3526,11373,11374],{"class":3976},"        \u002F\u002F Викликається автоматично після завантаження FXML\n",[3526,11376,11377,11380,11382,11384,11386,11388,11390,11393,11395,11398,11400,11403],{"class":3528,"line":4948},[3526,11378,11379],{"class":3982},"        authorComboBox",[3526,11381,3166],{"class":3950},[3526,11383,8481],{"class":3967},[3526,11385,4163],{"class":3950},[3526,11387,5606],{"class":3967},[3526,11389,3996],{"class":3950},[3526,11391,11392],{"class":3999},"\"Author 1\"",[3526,11394,3173],{"class":3950},[3526,11396,11397],{"class":3999},"\"Author 2\"",[3526,11399,3173],{"class":3950},[3526,11401,11402],{"class":3999},"\"Author 3\"",[3526,11404,4003],{"class":3950},[3526,11406,11407,11410,11412,11415,11417,11419,11422,11425,11427,11429,11431,11433,11435,11437],{"class":3528,"line":4958},[3526,11408,11409],{"class":3982},"        durationSpinner",[3526,11411,3166],{"class":3950},[3526,11413,11414],{"class":3967},"setValueFactory",[3526,11416,3996],{"class":3950},[3526,11418,4040],{"class":4039},[3526,11420,11421],{"class":3950}," SpinnerValueFactory.",[3526,11423,11424],{"class":3967},"IntegerSpinnerValueFactory",[3526,11426,3996],{"class":3950},[3526,11428,6107],{"class":4206},[3526,11430,3173],{"class":3950},[3526,11432,9798],{"class":4206},[3526,11434,3173],{"class":3950},[3526,11436,9803],{"class":4206},[3526,11438,4181],{"class":3950},[3526,11440,11441],{"class":3528,"line":4968},[3526,11442,4449],{"class":3950},[3526,11444,11445],{"class":3528,"line":4977},[3526,11446,4008],{"class":3950},[3526,11448,11449,11451],{"class":3528,"line":5584},[3526,11450,4458],{"class":3950},[3526,11452,11263],{"class":3954},[3526,11454,11455,11457,11459,11462],{"class":3528,"line":5589},[3526,11456,11268],{"class":3960},[3526,11458,3964],{"class":3954},[3526,11460,11461],{"class":3967}," onSaveClicked",[3526,11463,11369],{"class":3950},[3526,11465,11466,11469,11471,11473,11475,11477,11479],{"class":3528,"line":5595},[3526,11467,11468],{"class":3954},"        String",[3526,11470,8301],{"class":3982},[3526,11472,4026],{"class":3950},[3526,11474,8211],{"class":3982},[3526,11476,3166],{"class":3950},[3526,11478,8310],{"class":3967},[3526,11480,4150],{"class":3950},[3526,11482,11483,11485,11488,11490,11492,11494,11496],{"class":3528,"line":5612},[3526,11484,11468],{"class":3954},[3526,11486,11487],{"class":3982}," author",[3526,11489,4026],{"class":3950},[3526,11491,11299],{"class":3982},[3526,11493,3166],{"class":3950},[3526,11495,8550],{"class":3967},[3526,11497,4150],{"class":3950},[3526,11499,11500,11503,11505,11507,11509,11511,11513],{"class":3528,"line":5617},[3526,11501,11502],{"class":3954},"        int",[3526,11504,9826],{"class":3982},[3526,11506,4026],{"class":3950},[3526,11508,9782],{"class":3982},[3526,11510,3166],{"class":3950},[3526,11512,8550],{"class":3967},[3526,11514,4150],{"class":3950},[3526,11516,11517],{"class":3528,"line":5623},[3526,11518,5471],{"class":3950},[3526,11520,11521,11523,11525,11527,11529,11531,11533,11536,11539,11541,11544,11547,11550,11553],{"class":3528,"line":5647},[3526,11522,6006],{"class":3982},[3526,11524,3166],{"class":3950},[3526,11526,3988],{"class":3982},[3526,11528,3166],{"class":3950},[3526,11530,3993],{"class":3967},[3526,11532,3996],{"class":3950},[3526,11534,11535],{"class":3999},"\"Saving: \"",[3526,11537,11538],{"class":3950}," + title + ",[3526,11540,9319],{"class":3999},[3526,11542,11543],{"class":3950}," + author + ",[3526,11545,11546],{"class":3999},"\", \"",[3526,11548,11549],{"class":3950}," + duration + ",[3526,11551,11552],{"class":3999},"\" min\"",[3526,11554,4003],{"class":3950},[3526,11556,11557],{"class":3528,"line":5652},[3526,11558,11559],{"class":3976},"        \u002F\u002F Тут буде виклик viewModel.saveAudiobook()\n",[3526,11561,11562],{"class":3528,"line":5658},[3526,11563,4449],{"class":3950},[3526,11565,11566],{"class":3528,"line":5675},[3526,11567,4060],{"class":3950},[3150,11569,11570],{},[3163,11571,11572,11573,4378],{},"Анотація ",[3154,11574,11575],{},"@FXML",[6171,11577,11578,11587],{},[4501,11579,11580,11581,11584,11585,3166],{},"На ",[3163,11582,11583],{},"полях"," — зв'язує поле з елементом FXML через ",[3154,11586,11181],{},[4501,11588,11580,11589,11592,11593,3166],{},[3163,11590,11591],{},"методах"," — дозволяє викликати метод з FXML через ",[3154,11594,11595],{},"onAction=\"#methodName\"",[3150,11597,11598,11603,11604,11606],{},[3163,11599,4493,11600],{},[3154,11601,11602],{},"initialize()"," — спеціальний метод, що викликається автоматично після завантаження FXML та ін'єкції всіх ",[3154,11605,11575],{}," полів. Тут ви можете ініціалізувати контроли, налаштувати bindings, завантажити дані.",[3206,11608,11610],{"id":11609},"завантаження-fxml-у-додатку","Завантаження FXML у додатку",[3150,11612,11613,11614,4378],{},"Щоб використати FXML, завантажте його через ",[3154,11615,11616],{},"FXMLLoader",[3518,11618,11620],{"className":3941,"code":11619,"language":3943,"meta":3522,"style":3522},"public class AudiobookApp extends Application {\n    \n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        FXMLLoader loader = new FXMLLoader();\n        loader.setLocation(getClass().getResource(\"\u002Ffxml\u002Faudiobook-form.fxml\"));\n        \n        Parent root = loader.load();\n        \n        Scene scene = new Scene(root, 600, 400);\n        primaryStage.setTitle(\"Audiobook Form\");\n        primaryStage.setScene(scene);\n        primaryStage.show();\n    }\n    \n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n",[3154,11621,11622,11636,11640,11646,11671,11688,11715,11719,11738,11742,11764,11779,11789,11799,11803,11807,11827,11833,11837],{"__ignoreMap":3522},[3526,11623,11624,11626,11628,11630,11632,11634],{"class":3528,"line":3529},[3526,11625,3961],{"class":3960},[3526,11627,4390],{"class":3960},[3526,11629,4393],{"class":3954},[3526,11631,4396],{"class":3960},[3526,11633,4399],{"class":3954},[3526,11635,4402],{"class":3950},[3526,11637,11638],{"class":3528,"line":3535},[3526,11639,4008],{"class":3950},[3526,11641,11642,11644],{"class":3528,"line":3541},[3526,11643,4458],{"class":3950},[3526,11645,3955],{"class":3954},[3526,11647,11648,11650,11652,11654,11656,11658,11660,11663,11666,11669],{"class":3528,"line":3547},[3526,11649,4411],{"class":3960},[3526,11651,3964],{"class":3954},[3526,11653,4094],{"class":3967},[3526,11655,3996],{"class":3950},[3526,11657,3632],{"class":3954},[3526,11659,4475],{"class":3982},[3526,11661,11662],{"class":3950},") ",[3526,11664,11665],{"class":3960},"throws",[3526,11667,11668],{"class":3954}," Exception",[3526,11670,4402],{"class":3950},[3526,11672,11673,11676,11679,11681,11683,11686],{"class":3528,"line":3553},[3526,11674,11675],{"class":3954},"        FXMLLoader",[3526,11677,11678],{"class":3982}," loader",[3526,11680,4026],{"class":3950},[3526,11682,4040],{"class":4039},[3526,11684,11685],{"class":3967}," FXMLLoader",[3526,11687,4150],{"class":3950},[3526,11689,11690,11693,11695,11698,11700,11703,11705,11708,11710,11713],{"class":3528,"line":3559},[3526,11691,11692],{"class":3982},"        loader",[3526,11694,3166],{"class":3950},[3526,11696,11697],{"class":3967},"setLocation",[3526,11699,3996],{"class":3950},[3526,11701,11702],{"class":3967},"getClass",[3526,11704,4163],{"class":3950},[3526,11706,11707],{"class":3967},"getResource",[3526,11709,3996],{"class":3950},[3526,11711,11712],{"class":3999},"\"\u002Ffxml\u002Faudiobook-form.fxml\"",[3526,11714,4181],{"class":3950},[3526,11716,11717],{"class":3528,"line":3565},[3526,11718,5471],{"class":3950},[3526,11720,11721,11724,11726,11728,11731,11733,11736],{"class":3528,"line":3571},[3526,11722,11723],{"class":3954},"        Parent",[3526,11725,4140],{"class":3982},[3526,11727,4026],{"class":3950},[3526,11729,11730],{"class":3982},"loader",[3526,11732,3166],{"class":3950},[3526,11734,11735],{"class":3967},"load",[3526,11737,4150],{"class":3950},[3526,11739,11740],{"class":3528,"line":3577},[3526,11741,5471],{"class":3950},[3526,11743,11744,11746,11748,11750,11752,11754,11756,11758,11760,11762],{"class":3528,"line":3583},[3526,11745,5626],{"class":3954},[3526,11747,4193],{"class":3982},[3526,11749,4026],{"class":3950},[3526,11751,4040],{"class":4039},[3526,11753,4200],{"class":3967},[3526,11755,4203],{"class":3950},[3526,11757,4212],{"class":4206},[3526,11759,3173],{"class":3950},[3526,11761,3763],{"class":4206},[3526,11763,4003],{"class":3950},[3526,11765,11766,11768,11770,11772,11774,11777],{"class":3528,"line":3589},[3526,11767,5661],{"class":3982},[3526,11769,3166],{"class":3950},[3526,11771,4116],{"class":3967},[3526,11773,3996],{"class":3950},[3526,11775,11776],{"class":3999},"\"Audiobook Form\"",[3526,11778,4003],{"class":3950},[3526,11780,11781,11783,11785,11787],{"class":3528,"line":3595},[3526,11782,5661],{"class":3982},[3526,11784,3166],{"class":3950},[3526,11786,4223],{"class":3967},[3526,11788,4226],{"class":3950},[3526,11790,11791,11793,11795,11797],{"class":3528,"line":3601},[3526,11792,5661],{"class":3982},[3526,11794,3166],{"class":3950},[3526,11796,4235],{"class":3967},[3526,11798,4150],{"class":3950},[3526,11800,11801],{"class":3528,"line":3607},[3526,11802,4449],{"class":3950},[3526,11804,11805],{"class":3528,"line":3613},[3526,11806,4008],{"class":3950},[3526,11808,11809,11811,11813,11815,11817,11819,11821,11823,11825],{"class":3528,"line":3619},[3526,11810,4411],{"class":3960},[3526,11812,4414],{"class":3960},[3526,11814,3964],{"class":3954},[3526,11816,4419],{"class":3967},[3526,11818,3996],{"class":3950},[3526,11820,4424],{"class":3954},[3526,11822,4427],{"class":3950},[3526,11824,4430],{"class":3982},[3526,11826,4433],{"class":3950},[3526,11828,11829,11831],{"class":3528,"line":4612},[3526,11830,4438],{"class":3967},[3526,11832,5733],{"class":3950},[3526,11834,11835],{"class":3528,"line":4618},[3526,11836,4449],{"class":3950},[3526,11838,11839],{"class":3528,"line":4624},[3526,11840,4060],{"class":3950},[3150,11842,11843],{},[3163,11844,11845],{},"Структура проєкту:",[3518,11847,11850],{"className":11848,"code":11849,"language":3735},[3733],"src\u002F\n├── main\u002F\n│   ├── java\u002F\n│   │   └── dev\u002Fkostyl\u002Faudiobook\u002F\n│   │       ├── AudiobookApp.java\n│   │       └── controller\u002F\n│   │           └── AudiobookFormController.java\n│   └── resources\u002F\n│       └── fxml\u002F\n│           └── audiobook-form.fxml\n",[3154,11851,11849],{"__ignoreMap":3522},[3368,11853,11854,11857,11858],{},[3163,11855,11856],{},"Scene Builder"," — візуальний редактор для FXML від Gluon. Він дозволяє створювати інтерфейси методом drag-and-drop, як у Figma або Adobe XD, але для JavaFX. Scene Builder генерує FXML-код автоматично. ",[3199,11859,11862],{"href":11860,"rel":11861},"https:\u002F\u002Fgluonhq.com\u002Fproducts\u002Fscene-builder\u002F",[3203],"Завантажити Scene Builder",[3206,11864,11866],{"id":11865},"fxml-vs-java-код-коли-що-використовувати","FXML vs Java-код: Коли що використовувати?",[7760,11868,11869,11881],{},[7763,11870,11871],{},[7766,11872,11873,11876,11878],{},[7769,11874,11875],{},"Критерій",[7769,11877,10651],{},[7769,11879,11880],{},"Java-код",[7779,11882,11883,11896,11909,11922,11935],{},[7766,11884,11885,11890,11893],{},[7784,11886,11887],{},[3163,11888,11889],{},"Структура UI",[7784,11891,11892],{},"✅ Ідеально — декларативно, наочно",[7784,11894,11895],{},"❌ Багато boilerplate-коду",[7766,11897,11898,11903,11906],{},[7784,11899,11900],{},[3163,11901,11902],{},"Динамічний UI",[7784,11904,11905],{},"❌ Складно — потрібен Java-код",[7784,11907,11908],{},"✅ Легко — повний контроль",[7766,11910,11911,11916,11919],{},[7784,11912,11913],{},[3163,11914,11915],{},"Візуальні редактори",[7784,11917,11918],{},"✅ Scene Builder",[7784,11920,11921],{},"❌ Немає",[7766,11923,11924,11929,11932],{},[7784,11925,11926],{},[3163,11927,11928],{},"Розділення concerns",[7784,11930,11931],{},"✅ UI окремо від логіки",[7784,11933,11934],{},"❌ Все в одному класі",[7766,11936,11937,11942,11945],{},[7784,11938,11939],{},[3163,11940,11941],{},"Продуктивність",[7784,11943,11944],{},"⚠️ Трохи повільніше (парсинг XML)",[7784,11946,11947],{},"✅ Швидше (прямі виклики конструкторів)",[3150,11949,11950,11953,11954,11957,11958,11961],{},[3163,11951,11952],{},"Рекомендація:"," Використовуйте FXML для ",[3163,11955,11956],{},"статичних"," частин інтерфейсу (форми, діалоги, головні екрани). Використовуйте Java-код для ",[3163,11959,11960],{},"динамічних"," частин (генерація елементів у циклі, складні анімації).",[3375,11963],{},[3145,11965,11967],{"id":11966},"практичний-приклад-список-аудіокниг-з-borderpane","Практичний приклад: Список аудіокниг з BorderPane",[3150,11969,11970],{},"Об'єднаємо всі вивчені концепції у один практичний приклад: додаток для перегляду списку аудіокниг з можливістю додавання нових записів.",[3206,11972,11974],{"id":11973},"структура-інтерфейсу","Структура інтерфейсу",[6171,11976,11977,11983,11989],{},[4501,11978,11979,11982],{},[3163,11980,11981],{},"Top:"," Панель інструментів з кнопками \"Add\", \"Edit\", \"Delete\".",[4501,11984,11985,11988],{},[3163,11986,11987],{},"Center:"," TableView зі списком аудіокниг (колонки: Title, Author, Duration).",[4501,11990,11991,11994],{},[3163,11992,11993],{},"Bottom:"," Статус-бар з повідомленням \"Ready\".",[3206,11996,11998],{"id":11997},"fxml-audiobook-list-viewfxml","FXML: audiobook-list-view.fxml",[3518,12000,12002],{"className":4660,"code":12001,"language":4662,"meta":3522,"style":3522},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\u003C?import javafx.scene.control.*?>\n\u003C?import javafx.scene.layout.*?>\n\u003C?import javafx.geometry.Insets?>\n\n\u003CBorderPane xmlns:fx=\"http:\u002F\u002Fjavafx.com\u002Ffxml\"\n            fx:controller=\"dev.kostyl.audiobook.controller.AudiobookListController\"\n            prefWidth=\"800\" prefHeight=\"600\">\n    \n    \u003C!-- Top: Toolbar -->\n    \u003Ctop>\n        \u003CHBox spacing=\"10\" style=\"-fx-padding: 10; -fx-background-color: #f0f0f0;\">\n            \u003CButton fx:id=\"addButton\" text=\"Add Audiobook\" onAction=\"#onAddClicked\"\u002F>\n            \u003CButton fx:id=\"editButton\" text=\"Edit\" onAction=\"#onEditClicked\"\u002F>\n            \u003CButton fx:id=\"deleteButton\" text=\"Delete\" onAction=\"#onDeleteClicked\"\u002F>\n            \u003CRegion HBox.hgrow=\"ALWAYS\"\u002F> \u003C!-- Spacer -->\n            \u003CTextField fx:id=\"searchField\" promptText=\"Search audiobooks...\"\u002F>\n        \u003C\u002FHBox>\n    \u003C\u002Ftop>\n    \n    \u003C!-- Center: TableView -->\n    \u003Ccenter>\n        \u003CTableView fx:id=\"audiobookTable\">\n            \u003Ccolumns>\n                \u003CTableColumn fx:id=\"titleColumn\" text=\"Title\" prefWidth=\"300\"\u002F>\n                \u003CTableColumn fx:id=\"authorColumn\" text=\"Author\" prefWidth=\"200\"\u002F>\n                \u003CTableColumn fx:id=\"durationColumn\" text=\"Duration (min)\" prefWidth=\"150\"\u002F>\n                \u003CTableColumn fx:id=\"genreColumn\" text=\"Genre\" prefWidth=\"150\"\u002F>\n            \u003C\u002Fcolumns>\n        \u003C\u002FTableView>\n    \u003C\u002Fcenter>\n    \n    \u003C!-- Bottom: Status Bar -->\n    \u003Cbottom>\n        \u003CLabel fx:id=\"statusLabel\" text=\"Ready\" style=\"-fx-padding: 5; -fx-background-color: #e0e0e0;\"\u002F>\n    \u003C\u002Fbottom>\n    \n\u003C\u002FBorderPane>\n",[3154,12003,12004,12024,12028,12040,12052,12064,12068,12081,12091,12111,12115,12120,12128,12150,12179,12207,12235,12255,12278,12286,12294,12298,12303,12311,12326,12335,12364,12392,12420,12448,12456,12464,12472,12476,12481,12489,12517,12525,12529],{"__ignoreMap":3522},[3526,12005,12006,12008,12010,12012,12014,12016,12018,12020,12022],{"class":3528,"line":3529},[3526,12007,10680],{"class":4674},[3526,12009,4662],{"class":4678},[3526,12011,10686],{"class":10685},[3526,12013,10689],{"class":3950},[3526,12015,10693],{"class":10692},[3526,12017,10696],{"class":10685},[3526,12019,10689],{"class":3950},[3526,12021,10701],{"class":10692},[3526,12023,10704],{"class":4674},[3526,12025,12026],{"class":3528,"line":3535},[3526,12027,4837],{"emptyLinePlaceholder":4836},[3526,12029,12030,12032,12034,12036,12038],{"class":3528,"line":3541},[3526,12031,10680],{"class":4674},[3526,12033,5327],{"class":4678},[3526,12035,10717],{"class":10685},[3526,12037,10720],{"class":3950},[3526,12039,10704],{"class":4674},[3526,12041,12042,12044,12046,12048,12050],{"class":3528,"line":3547},[3526,12043,10680],{"class":4674},[3526,12045,5327],{"class":4678},[3526,12047,10717],{"class":10685},[3526,12049,10733],{"class":3950},[3526,12051,10704],{"class":4674},[3526,12053,12054,12056,12058,12060,12062],{"class":3528,"line":3553},[3526,12055,10680],{"class":4674},[3526,12057,5327],{"class":4678},[3526,12059,10717],{"class":10685},[3526,12061,10746],{"class":3950},[3526,12063,10704],{"class":4674},[3526,12065,12066],{"class":3528,"line":3559},[3526,12067,4837],{"emptyLinePlaceholder":4836},[3526,12069,12070,12072,12074,12076,12078],{"class":3528,"line":3565},[3526,12071,4675],{"class":4674},[3526,12073,3481],{"class":4678},[3526,12075,10761],{"class":10685},[3526,12077,10689],{"class":3950},[3526,12079,12080],{"class":10692},"\"http:\u002F\u002Fjavafx.com\u002Ffxml\"\n",[3526,12082,12083,12086,12088],{"class":3528,"line":3571},[3526,12084,12085],{"class":10685},"            fx:controller",[3526,12087,10689],{"class":3950},[3526,12089,12090],{"class":10692},"\"dev.kostyl.audiobook.controller.AudiobookListController\"\n",[3526,12092,12093,12096,12098,12101,12104,12106,12109],{"class":3528,"line":3577},[3526,12094,12095],{"class":10685},"            prefWidth",[3526,12097,10689],{"class":3950},[3526,12099,12100],{"class":10692},"\"800\"",[3526,12102,12103],{"class":10685}," prefHeight",[3526,12105,10689],{"class":3950},[3526,12107,12108],{"class":10692},"\"600\"",[3526,12110,4682],{"class":4674},[3526,12112,12113],{"class":3528,"line":3583},[3526,12114,4008],{"class":3950},[3526,12116,12117],{"class":3528,"line":3589},[3526,12118,12119],{"class":3976},"    \u003C!-- Top: Toolbar -->\n",[3526,12121,12122,12124,12126],{"class":3528,"line":3595},[3526,12123,4687],{"class":4674},[3526,12125,6767],{"class":4678},[3526,12127,4682],{"class":4674},[3526,12129,12130,12132,12134,12137,12139,12141,12143,12145,12148],{"class":3528,"line":3601},[3526,12131,4697],{"class":4674},[3526,12133,3478],{"class":4678},[3526,12135,12136],{"class":10685}," spacing",[3526,12138,10689],{"class":3950},[3526,12140,10788],{"class":10692},[3526,12142,10878],{"class":10685},[3526,12144,10689],{"class":3950},[3526,12146,12147],{"class":10692},"\"-fx-padding: 10; -fx-background-color: #f0f0f0;\"",[3526,12149,4682],{"class":4674},[3526,12151,12152,12154,12156,12158,12160,12163,12165,12167,12170,12172,12174,12177],{"class":3528,"line":3607},[3526,12153,4869],{"class":4674},[3526,12155,3503],{"class":4678},[3526,12157,10948],{"class":10685},[3526,12159,10689],{"class":3950},[3526,12161,12162],{"class":10692},"\"addButton\"",[3526,12164,10870],{"class":10685},[3526,12166,10689],{"class":3950},[3526,12168,12169],{"class":10692},"\"Add Audiobook\"",[3526,12171,11118],{"class":10685},[3526,12173,10689],{"class":3950},[3526,12175,12176],{"class":10692},"\"#onAddClicked\"",[3526,12178,10849],{"class":4674},[3526,12180,12181,12183,12185,12187,12189,12192,12194,12196,12198,12200,12202,12205],{"class":3528,"line":3613},[3526,12182,4869],{"class":4674},[3526,12184,3503],{"class":4678},[3526,12186,10948],{"class":10685},[3526,12188,10689],{"class":3950},[3526,12190,12191],{"class":10692},"\"editButton\"",[3526,12193,10870],{"class":10685},[3526,12195,10689],{"class":3950},[3526,12197,6408],{"class":10692},[3526,12199,11118],{"class":10685},[3526,12201,10689],{"class":3950},[3526,12203,12204],{"class":10692},"\"#onEditClicked\"",[3526,12206,10849],{"class":4674},[3526,12208,12209,12211,12213,12215,12217,12220,12222,12224,12226,12228,12230,12233],{"class":3528,"line":3619},[3526,12210,4869],{"class":4674},[3526,12212,3503],{"class":4678},[3526,12214,10948],{"class":10685},[3526,12216,10689],{"class":3950},[3526,12218,12219],{"class":10692},"\"deleteButton\"",[3526,12221,10870],{"class":10685},[3526,12223,10689],{"class":3950},[3526,12225,6428],{"class":10692},[3526,12227,11118],{"class":10685},[3526,12229,10689],{"class":3950},[3526,12231,12232],{"class":10692},"\"#onDeleteClicked\"",[3526,12234,10849],{"class":4674},[3526,12236,12237,12239,12241,12244,12246,12249,12252],{"class":3528,"line":4612},[3526,12238,4869],{"class":4674},[3526,12240,3465],{"class":4678},[3526,12242,12243],{"class":10685}," HBox.hgrow",[3526,12245,10689],{"class":3950},[3526,12247,12248],{"class":10692},"\"ALWAYS\"",[3526,12250,12251],{"class":4674},"\u002F>",[3526,12253,12254],{"class":3976}," \u003C!-- Spacer -->\n",[3526,12256,12257,12259,12261,12263,12265,12268,12271,12273,12276],{"class":3528,"line":4618},[3526,12258,4869],{"class":4674},[3526,12260,3506],{"class":4678},[3526,12262,10948],{"class":10685},[3526,12264,10689],{"class":3950},[3526,12266,12267],{"class":10692},"\"searchField\"",[3526,12269,12270],{"class":10685}," promptText",[3526,12272,10689],{"class":3950},[3526,12274,12275],{"class":10692},"\"Search audiobooks...\"",[3526,12277,10849],{"class":4674},[3526,12279,12280,12282,12284],{"class":3528,"line":4624},[3526,12281,4961],{"class":4674},[3526,12283,3478],{"class":4678},[3526,12285,4682],{"class":4674},[3526,12287,12288,12290,12292],{"class":3528,"line":4630},[3526,12289,4754],{"class":4674},[3526,12291,6767],{"class":4678},[3526,12293,4682],{"class":4674},[3526,12295,12296],{"class":3528,"line":4918},[3526,12297,4008],{"class":3950},[3526,12299,12300],{"class":3528,"line":4928},[3526,12301,12302],{"class":3976},"    \u003C!-- Center: TableView -->\n",[3526,12304,12305,12307,12309],{"class":3528,"line":4948},[3526,12306,4687],{"class":4674},[3526,12308,6779],{"class":4678},[3526,12310,4682],{"class":4674},[3526,12312,12313,12315,12317,12319,12321,12324],{"class":3528,"line":4958},[3526,12314,4697],{"class":4674},[3526,12316,3512],{"class":4678},[3526,12318,10948],{"class":10685},[3526,12320,10689],{"class":3950},[3526,12322,12323],{"class":10692},"\"audiobookTable\"",[3526,12325,4682],{"class":4674},[3526,12327,12328,12330,12333],{"class":3528,"line":4968},[3526,12329,4869],{"class":4674},[3526,12331,12332],{"class":4678},"columns",[3526,12334,4682],{"class":4674},[3526,12336,12337,12339,12341,12343,12345,12348,12350,12352,12354,12357,12359,12362],{"class":3528,"line":4977},[3526,12338,4931],{"class":4674},[3526,12340,9399],{"class":4678},[3526,12342,10948],{"class":10685},[3526,12344,10689],{"class":3950},[3526,12346,12347],{"class":10692},"\"titleColumn\"",[3526,12349,10870],{"class":10685},[3526,12351,10689],{"class":3950},[3526,12353,9425],{"class":10692},[3526,12355,12356],{"class":10685}," prefWidth",[3526,12358,10689],{"class":3950},[3526,12360,12361],{"class":10692},"\"300\"",[3526,12363,10849],{"class":4674},[3526,12365,12366,12368,12370,12372,12374,12377,12379,12381,12383,12385,12387,12390],{"class":3528,"line":5584},[3526,12367,4931],{"class":4674},[3526,12369,9399],{"class":4678},[3526,12371,10948],{"class":10685},[3526,12373,10689],{"class":3950},[3526,12375,12376],{"class":10692},"\"authorColumn\"",[3526,12378,10870],{"class":10685},[3526,12380,10689],{"class":3950},[3526,12382,9487],{"class":10692},[3526,12384,12356],{"class":10685},[3526,12386,10689],{"class":3950},[3526,12388,12389],{"class":10692},"\"200\"",[3526,12391,10849],{"class":4674},[3526,12393,12394,12396,12398,12400,12402,12405,12407,12409,12411,12413,12415,12418],{"class":3528,"line":5589},[3526,12395,4931],{"class":4674},[3526,12397,9399],{"class":4678},[3526,12399,10948],{"class":10685},[3526,12401,10689],{"class":3950},[3526,12403,12404],{"class":10692},"\"durationColumn\"",[3526,12406,10870],{"class":10685},[3526,12408,10689],{"class":3950},[3526,12410,9574],{"class":10692},[3526,12412,12356],{"class":10685},[3526,12414,10689],{"class":3950},[3526,12416,12417],{"class":10692},"\"150\"",[3526,12419,10849],{"class":4674},[3526,12421,12422,12424,12426,12428,12430,12433,12435,12437,12440,12442,12444,12446],{"class":3528,"line":5595},[3526,12423,4931],{"class":4674},[3526,12425,9399],{"class":4678},[3526,12427,10948],{"class":10685},[3526,12429,10689],{"class":3950},[3526,12431,12432],{"class":10692},"\"genreColumn\"",[3526,12434,10870],{"class":10685},[3526,12436,10689],{"class":3950},[3526,12438,12439],{"class":10692},"\"Genre\"",[3526,12441,12356],{"class":10685},[3526,12443,10689],{"class":3950},[3526,12445,12417],{"class":10692},[3526,12447,10849],{"class":4674},[3526,12449,12450,12452,12454],{"class":3528,"line":5612},[3526,12451,4951],{"class":4674},[3526,12453,12332],{"class":4678},[3526,12455,4682],{"class":4674},[3526,12457,12458,12460,12462],{"class":3528,"line":5617},[3526,12459,4961],{"class":4674},[3526,12461,3512],{"class":4678},[3526,12463,4682],{"class":4674},[3526,12465,12466,12468,12470],{"class":3528,"line":5623},[3526,12467,4754],{"class":4674},[3526,12469,6779],{"class":4678},[3526,12471,4682],{"class":4674},[3526,12473,12474],{"class":3528,"line":5647},[3526,12475,4008],{"class":3950},[3526,12477,12478],{"class":3528,"line":5652},[3526,12479,12480],{"class":3976},"    \u003C!-- Bottom: Status Bar -->\n",[3526,12482,12483,12485,12487],{"class":3528,"line":5658},[3526,12484,4687],{"class":4674},[3526,12486,6770],{"class":4678},[3526,12488,4682],{"class":4674},[3526,12490,12491,12493,12495,12497,12499,12502,12504,12506,12508,12510,12512,12515],{"class":3528,"line":5675},[3526,12492,4697],{"class":4674},[3526,12494,3509],{"class":4678},[3526,12496,10948],{"class":10685},[3526,12498,10689],{"class":3950},[3526,12500,12501],{"class":10692},"\"statusLabel\"",[3526,12503,10870],{"class":10685},[3526,12505,10689],{"class":3950},[3526,12507,7100],{"class":10692},[3526,12509,10878],{"class":10685},[3526,12511,10689],{"class":3950},[3526,12513,12514],{"class":10692},"\"-fx-padding: 5; -fx-background-color: #e0e0e0;\"",[3526,12516,10849],{"class":4674},[3526,12518,12519,12521,12523],{"class":3528,"line":5686},[3526,12520,4754],{"class":4674},[3526,12522,6770],{"class":4678},[3526,12524,4682],{"class":4674},[3526,12526,12527],{"class":3528,"line":5697},[3526,12528,4008],{"class":3950},[3526,12530,12531,12533,12535],{"class":3528,"line":5702},[3526,12532,4709],{"class":4674},[3526,12534,3481],{"class":4678},[3526,12536,4682],{"class":4674},[3206,12538,12540],{"id":12539},"controller-audiobooklistcontrollerjava","Controller: AudiobookListController.java",[3518,12542,12544],{"className":3941,"code":12543,"language":3943,"meta":3522,"style":3522},"package dev.kostyl.audiobook.controller;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.*;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\n\npublic class AudiobookListController {\n\n    @FXML private TableView\u003CAudiobookData> audiobookTable;\n    @FXML private TableColumn\u003CAudiobookData, String> titleColumn;\n    @FXML private TableColumn\u003CAudiobookData, String> authorColumn;\n    @FXML private TableColumn\u003CAudiobookData, Integer> durationColumn;\n    @FXML private TableColumn\u003CAudiobookData, String> genreColumn;\n    \n    @FXML private Button addButton;\n    @FXML private Button editButton;\n    @FXML private Button deleteButton;\n    @FXML private TextField searchField;\n    @FXML private Label statusLabel;\n    \n    private ObservableList\u003CAudiobookData> audiobooks;\n    \n    @FXML\n    public void initialize() {\n        \u002F\u002F Налаштування колонок\n        titleColumn.setCellValueFactory(cellData -> cellData.getValue().titleProperty());\n        authorColumn.setCellValueFactory(cellData -> cellData.getValue().authorProperty());\n        durationColumn.setCellValueFactory(cellData -> cellData.getValue().durationProperty().asObject());\n        genreColumn.setCellValueFactory(cellData -> cellData.getValue().genreProperty());\n        \n        \u002F\u002F Завантаження тестових даних\n        audiobooks = FXCollections.observableArrayList(\n            new AudiobookData(\"The Great Gatsby\", \"F. Scott Fitzgerald\", 240, \"Fiction\"),\n            new AudiobookData(\"1984\", \"George Orwell\", 360, \"Dystopian\"),\n            new AudiobookData(\"Sapiens\", \"Yuval Noah Harari\", 900, \"Non-Fiction\")\n        );\n        \n        audiobookTable.setItems(audiobooks);\n        \n        \u002F\u002F Вимкнути кнопки Edit\u002FDelete, якщо нічого не обрано\n        editButton.setDisable(true);\n        deleteButton.setDisable(true);\n        \n        audiobookTable.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {\n            boolean hasSelection = newSelection != null;\n            editButton.setDisable(!hasSelection);\n            deleteButton.setDisable(!hasSelection);\n        });\n    }\n    \n    @FXML\n    private void onAddClicked() {\n        statusLabel.setText(\"Add button clicked\");\n        \u002F\u002F Тут буде відкриття діалогу додавання\n    }\n    \n    @FXML\n    private void onEditClicked() {\n        AudiobookData selected = audiobookTable.getSelectionModel().getSelectedItem();\n        if (selected != null) {\n            statusLabel.setText(\"Editing: \" + selected.getTitle());\n        }\n    }\n    \n    @FXML\n    private void onDeleteClicked() {\n        AudiobookData selected = audiobookTable.getSelectionModel().getSelectedItem();\n        if (selected != null) {\n            audiobooks.remove(selected);\n            statusLabel.setText(\"Deleted: \" + selected.getTitle());\n        }\n    }\n}\n",[3154,12545,12546,12552,12556,12562,12568,12575,12582,12586,12597,12601,12623,12647,12671,12695,12720,12724,12738,12752,12766,12781,12796,12800,12818,12822,12828,12838,12843,12870,12896,12927,12953,12957,12962,12977,13006,13033,13060,13064,13068,13081,13085,13090,13106,13121,13126,13151,13167,13180,13192,13197,13202,13207,13214,13226,13244,13250,13255,13260,13267,13279,13302,13314,13339,13344,13349,13354,13361,13373,13394,13405,13419,13443,13448,13453],{"__ignoreMap":3522},[3526,12547,12548,12550],{"class":3528,"line":3529},[3526,12549,5315],{"class":3960},[3526,12551,11219],{"class":3950},[3526,12553,12554],{"class":3528,"line":3535},[3526,12555,4837],{"emptyLinePlaceholder":4836},[3526,12557,12558,12560],{"class":3528,"line":3541},[3526,12559,5327],{"class":3960},[3526,12561,11230],{"class":3950},[3526,12563,12564,12566],{"class":3528,"line":3547},[3526,12565,5327],{"class":3960},[3526,12567,11237],{"class":3950},[3526,12569,12570,12572],{"class":3528,"line":3553},[3526,12571,5327],{"class":3960},[3526,12573,12574],{"class":3950}," javafx.collections.FXCollections;\n",[3526,12576,12577,12579],{"class":3528,"line":3559},[3526,12578,5327],{"class":3960},[3526,12580,12581],{"class":3950}," javafx.collections.ObservableList;\n",[3526,12583,12584],{"class":3528,"line":3565},[3526,12585,4837],{"emptyLinePlaceholder":4836},[3526,12587,12588,12590,12592,12595],{"class":3528,"line":3571},[3526,12589,3961],{"class":3960},[3526,12591,4390],{"class":3960},[3526,12593,12594],{"class":3954}," AudiobookListController",[3526,12596,4402],{"class":3950},[3526,12598,12599],{"class":3528,"line":3577},[3526,12600,4837],{"emptyLinePlaceholder":4836},[3526,12602,12603,12605,12607,12610,12612,12614,12617,12619,12621],{"class":3528,"line":3583},[3526,12604,4458],{"class":3950},[3526,12606,10651],{"class":3954},[3526,12608,12609],{"class":3960}," private",[3526,12611,9383],{"class":3954},[3526,12613,4675],{"class":3950},[3526,12615,12616],{"class":3954},"AudiobookData",[3526,12618,8459],{"class":3950},[3526,12620,9376],{"class":3982},[3526,12622,10379],{"class":3950},[3526,12624,12625,12627,12629,12631,12633,12635,12637,12639,12641,12643,12645],{"class":3528,"line":3589},[3526,12626,4458],{"class":3950},[3526,12628,10651],{"class":3954},[3526,12630,12609],{"class":3960},[3526,12632,9419],{"class":3954},[3526,12634,4675],{"class":3950},[3526,12636,12616],{"class":3954},[3526,12638,3173],{"class":3950},[3526,12640,4424],{"class":3954},[3526,12642,8459],{"class":3950},[3526,12644,9412],{"class":3982},[3526,12646,10379],{"class":3950},[3526,12648,12649,12651,12653,12655,12657,12659,12661,12663,12665,12667,12669],{"class":3528,"line":3595},[3526,12650,4458],{"class":3950},[3526,12652,10651],{"class":3954},[3526,12654,12609],{"class":3960},[3526,12656,9419],{"class":3954},[3526,12658,4675],{"class":3950},[3526,12660,12616],{"class":3954},[3526,12662,3173],{"class":3950},[3526,12664,4424],{"class":3954},[3526,12666,8459],{"class":3950},[3526,12668,9476],{"class":3982},[3526,12670,10379],{"class":3950},[3526,12672,12673,12675,12677,12679,12681,12683,12685,12687,12689,12691,12693],{"class":3528,"line":3601},[3526,12674,4458],{"class":3950},[3526,12676,10651],{"class":3954},[3526,12678,12609],{"class":3960},[3526,12680,9419],{"class":3954},[3526,12682,4675],{"class":3950},[3526,12684,12616],{"class":3954},[3526,12686,3173],{"class":3950},[3526,12688,9558],{"class":3954},[3526,12690,8459],{"class":3950},[3526,12692,9563],{"class":3982},[3526,12694,10379],{"class":3950},[3526,12696,12697,12699,12701,12703,12705,12707,12709,12711,12713,12715,12718],{"class":3528,"line":3607},[3526,12698,4458],{"class":3950},[3526,12700,10651],{"class":3954},[3526,12702,12609],{"class":3960},[3526,12704,9419],{"class":3954},[3526,12706,4675],{"class":3950},[3526,12708,12616],{"class":3954},[3526,12710,3173],{"class":3950},[3526,12712,4424],{"class":3954},[3526,12714,8459],{"class":3950},[3526,12716,12717],{"class":3982},"genreColumn",[3526,12719,10379],{"class":3950},[3526,12721,12722],{"class":3528,"line":3613},[3526,12723,4008],{"class":3950},[3526,12725,12726,12728,12730,12732,12734,12736],{"class":3528,"line":3619},[3526,12727,4458],{"class":3950},[3526,12729,10651],{"class":3954},[3526,12731,12609],{"class":3960},[3526,12733,5531],{"class":3954},[3526,12735,6377],{"class":3982},[3526,12737,10379],{"class":3950},[3526,12739,12740,12742,12744,12746,12748,12750],{"class":3528,"line":4612},[3526,12741,4458],{"class":3950},[3526,12743,10651],{"class":3954},[3526,12745,12609],{"class":3960},[3526,12747,5531],{"class":3954},[3526,12749,6397],{"class":3982},[3526,12751,10379],{"class":3950},[3526,12753,12754,12756,12758,12760,12762,12764],{"class":3528,"line":4618},[3526,12755,4458],{"class":3950},[3526,12757,10651],{"class":3954},[3526,12759,12609],{"class":3960},[3526,12761,5531],{"class":3954},[3526,12763,6417],{"class":3982},[3526,12765,10379],{"class":3950},[3526,12767,12768,12770,12772,12774,12776,12779],{"class":3528,"line":4624},[3526,12769,4458],{"class":3950},[3526,12771,10651],{"class":3954},[3526,12773,12609],{"class":3960},[3526,12775,6568],{"class":3954},[3526,12777,12778],{"class":3982}," searchField",[3526,12780,10379],{"class":3950},[3526,12782,12783,12785,12787,12789,12791,12794],{"class":3528,"line":4630},[3526,12784,4458],{"class":3950},[3526,12786,10651],{"class":3954},[3526,12788,12609],{"class":3960},[3526,12790,4173],{"class":3954},[3526,12792,12793],{"class":3982}," statusLabel",[3526,12795,10379],{"class":3950},[3526,12797,12798],{"class":3528,"line":4918},[3526,12799,4008],{"class":3950},[3526,12801,12802,12804,12807,12809,12811,12813,12816],{"class":3528,"line":4928},[3526,12803,11268],{"class":3960},[3526,12805,12806],{"class":3954}," ObservableList",[3526,12808,4675],{"class":3950},[3526,12810,12616],{"class":3954},[3526,12812,8459],{"class":3950},[3526,12814,12815],{"class":3982},"audiobooks",[3526,12817,10379],{"class":3950},[3526,12819,12820],{"class":3528,"line":4948},[3526,12821,4008],{"class":3950},[3526,12823,12824,12826],{"class":3528,"line":4958},[3526,12825,4458],{"class":3950},[3526,12827,11263],{"class":3954},[3526,12829,12830,12832,12834,12836],{"class":3528,"line":4968},[3526,12831,4411],{"class":3960},[3526,12833,3964],{"class":3954},[3526,12835,11366],{"class":3967},[3526,12837,11369],{"class":3950},[3526,12839,12840],{"class":3528,"line":4977},[3526,12841,12842],{"class":3976},"        \u002F\u002F Налаштування колонок\n",[3526,12844,12845,12848,12850,12852,12854,12856,12859,12861,12863,12865,12868],{"class":3528,"line":5584},[3526,12846,12847],{"class":3982},"        titleColumn",[3526,12849,3166],{"class":3950},[3526,12851,9436],{"class":3967},[3526,12853,9500],{"class":3950},[3526,12855,5554],{"class":3960},[3526,12857,12858],{"class":3982}," cellData",[3526,12860,3166],{"class":3950},[3526,12862,8550],{"class":3967},[3526,12864,4163],{"class":3950},[3526,12866,12867],{"class":3967},"titleProperty",[3526,12869,4046],{"class":3950},[3526,12871,12872,12875,12877,12879,12881,12883,12885,12887,12889,12891,12894],{"class":3528,"line":5589},[3526,12873,12874],{"class":3982},"        authorColumn",[3526,12876,3166],{"class":3950},[3526,12878,9436],{"class":3967},[3526,12880,9500],{"class":3950},[3526,12882,5554],{"class":3960},[3526,12884,12858],{"class":3982},[3526,12886,3166],{"class":3950},[3526,12888,8550],{"class":3967},[3526,12890,4163],{"class":3950},[3526,12892,12893],{"class":3967},"authorProperty",[3526,12895,4046],{"class":3950},[3526,12897,12898,12901,12903,12905,12907,12909,12911,12913,12915,12917,12920,12922,12925],{"class":3528,"line":5595},[3526,12899,12900],{"class":3982},"        durationColumn",[3526,12902,3166],{"class":3950},[3526,12904,9436],{"class":3967},[3526,12906,9500],{"class":3950},[3526,12908,5554],{"class":3960},[3526,12910,12858],{"class":3982},[3526,12912,3166],{"class":3950},[3526,12914,8550],{"class":3967},[3526,12916,4163],{"class":3950},[3526,12918,12919],{"class":3967},"durationProperty",[3526,12921,4163],{"class":3950},[3526,12923,12924],{"class":3967},"asObject",[3526,12926,4046],{"class":3950},[3526,12928,12929,12932,12934,12936,12938,12940,12942,12944,12946,12948,12951],{"class":3528,"line":5612},[3526,12930,12931],{"class":3982},"        genreColumn",[3526,12933,3166],{"class":3950},[3526,12935,9436],{"class":3967},[3526,12937,9500],{"class":3950},[3526,12939,5554],{"class":3960},[3526,12941,12858],{"class":3982},[3526,12943,3166],{"class":3950},[3526,12945,8550],{"class":3967},[3526,12947,4163],{"class":3950},[3526,12949,12950],{"class":3967},"genreProperty",[3526,12952,4046],{"class":3950},[3526,12954,12955],{"class":3528,"line":5617},[3526,12956,5471],{"class":3950},[3526,12958,12959],{"class":3528,"line":5623},[3526,12960,12961],{"class":3976},"        \u002F\u002F Завантаження тестових даних\n",[3526,12963,12964,12967,12970,12972,12975],{"class":3528,"line":5647},[3526,12965,12966],{"class":3950},"        audiobooks = ",[3526,12968,12969],{"class":3982},"FXCollections",[3526,12971,3166],{"class":3950},[3526,12973,12974],{"class":3967},"observableArrayList",[3526,12976,6546],{"class":3950},[3526,12978,12979,12982,12985,12987,12990,12992,12995,12997,13000,13002,13004],{"class":3528,"line":5652},[3526,12980,12981],{"class":4039},"            new",[3526,12983,12984],{"class":3967}," AudiobookData",[3526,12986,3996],{"class":3950},[3526,12988,12989],{"class":3999},"\"The Great Gatsby\"",[3526,12991,3173],{"class":3950},[3526,12993,12994],{"class":3999},"\"F. Scott Fitzgerald\"",[3526,12996,3173],{"class":3950},[3526,12998,12999],{"class":4206},"240",[3526,13001,3173],{"class":3950},[3526,13003,8490],{"class":3999},[3526,13005,6561],{"class":3950},[3526,13007,13008,13010,13012,13014,13017,13019,13022,13024,13026,13028,13031],{"class":3528,"line":5658},[3526,13009,12981],{"class":4039},[3526,13011,12984],{"class":3967},[3526,13013,3996],{"class":3950},[3526,13015,13016],{"class":3999},"\"1984\"",[3526,13018,3173],{"class":3950},[3526,13020,13021],{"class":3999},"\"George Orwell\"",[3526,13023,3173],{"class":3950},[3526,13025,6069],{"class":4206},[3526,13027,3173],{"class":3950},[3526,13029,13030],{"class":3999},"\"Dystopian\"",[3526,13032,6561],{"class":3950},[3526,13034,13035,13037,13039,13041,13044,13046,13049,13051,13054,13056,13058],{"class":3528,"line":5675},[3526,13036,12981],{"class":4039},[3526,13038,12984],{"class":3967},[3526,13040,3996],{"class":3950},[3526,13042,13043],{"class":3999},"\"Sapiens\"",[3526,13045,3173],{"class":3950},[3526,13047,13048],{"class":3999},"\"Yuval Noah Harari\"",[3526,13050,3173],{"class":3950},[3526,13052,13053],{"class":4206},"900",[3526,13055,3173],{"class":3950},[3526,13057,8495],{"class":3999},[3526,13059,6606],{"class":3950},[3526,13061,13062],{"class":3528,"line":5686},[3526,13063,10442],{"class":3950},[3526,13065,13066],{"class":3528,"line":5697},[3526,13067,5471],{"class":3950},[3526,13069,13070,13073,13075,13078],{"class":3528,"line":5702},[3526,13071,13072],{"class":3982},"        audiobookTable",[3526,13074,3166],{"class":3950},[3526,13076,13077],{"class":3967},"setItems",[3526,13079,13080],{"class":3950},"(audiobooks);\n",[3526,13082,13083],{"class":3528,"line":5707},[3526,13084,5471],{"class":3950},[3526,13086,13087],{"class":3528,"line":5728},[3526,13088,13089],{"class":3976},"        \u002F\u002F Вимкнути кнопки Edit\u002FDelete, якщо нічого не обрано\n",[3526,13091,13092,13095,13097,13100,13102,13104],{"class":3528,"line":5736},[3526,13093,13094],{"class":3982},"        editButton",[3526,13096,3166],{"class":3950},[3526,13098,13099],{"class":3967},"setDisable",[3526,13101,3996],{"class":3950},[3526,13103,8279],{"class":3960},[3526,13105,4003],{"class":3950},[3526,13107,13108,13111,13113,13115,13117,13119],{"class":3528,"line":5741},[3526,13109,13110],{"class":3982},"        deleteButton",[3526,13112,3166],{"class":3950},[3526,13114,13099],{"class":3967},[3526,13116,3996],{"class":3950},[3526,13118,8279],{"class":3960},[3526,13120,4003],{"class":3950},[3526,13122,13124],{"class":3528,"line":13123},44,[3526,13125,5471],{"class":3950},[3526,13127,13129,13131,13133,13135,13137,13140,13142,13144,13147,13149],{"class":3528,"line":13128},45,[3526,13130,13072],{"class":3982},[3526,13132,3166],{"class":3950},[3526,13134,9096],{"class":3967},[3526,13136,4163],{"class":3950},[3526,13138,13139],{"class":3967},"selectedItemProperty",[3526,13141,4163],{"class":3950},[3526,13143,8354],{"class":3967},[3526,13145,13146],{"class":3950},"((obs, oldSelection, newSelection) ",[3526,13148,5554],{"class":3960},[3526,13150,4402],{"class":3950},[3526,13152,13154,13157,13160,13163,13165],{"class":3528,"line":13153},46,[3526,13155,13156],{"class":3954},"            boolean",[3526,13158,13159],{"class":3982}," hasSelection",[3526,13161,13162],{"class":3950}," = newSelection != ",[3526,13164,8690],{"class":3960},[3526,13166,10379],{"class":3950},[3526,13168,13170,13173,13175,13177],{"class":3528,"line":13169},47,[3526,13171,13172],{"class":3982},"            editButton",[3526,13174,3166],{"class":3950},[3526,13176,13099],{"class":3967},[3526,13178,13179],{"class":3950},"(!hasSelection);\n",[3526,13181,13183,13186,13188,13190],{"class":3528,"line":13182},48,[3526,13184,13185],{"class":3982},"            deleteButton",[3526,13187,3166],{"class":3950},[3526,13189,13099],{"class":3967},[3526,13191,13179],{"class":3950},[3526,13193,13195],{"class":3528,"line":13194},49,[3526,13196,5581],{"class":3950},[3526,13198,13200],{"class":3528,"line":13199},50,[3526,13201,4449],{"class":3950},[3526,13203,13205],{"class":3528,"line":13204},51,[3526,13206,4008],{"class":3950},[3526,13208,13210,13212],{"class":3528,"line":13209},52,[3526,13211,4458],{"class":3950},[3526,13213,11263],{"class":3954},[3526,13215,13217,13219,13221,13224],{"class":3528,"line":13216},53,[3526,13218,11268],{"class":3960},[3526,13220,3964],{"class":3954},[3526,13222,13223],{"class":3967}," onAddClicked",[3526,13225,11369],{"class":3950},[3526,13227,13229,13232,13234,13237,13239,13242],{"class":3528,"line":13228},54,[3526,13230,13231],{"class":3982},"        statusLabel",[3526,13233,3166],{"class":3950},[3526,13235,13236],{"class":3967},"setText",[3526,13238,3996],{"class":3950},[3526,13240,13241],{"class":3999},"\"Add button clicked\"",[3526,13243,4003],{"class":3950},[3526,13245,13247],{"class":3528,"line":13246},55,[3526,13248,13249],{"class":3976},"        \u002F\u002F Тут буде відкриття діалогу додавання\n",[3526,13251,13253],{"class":3528,"line":13252},56,[3526,13254,4449],{"class":3950},[3526,13256,13258],{"class":3528,"line":13257},57,[3526,13259,4008],{"class":3950},[3526,13261,13263,13265],{"class":3528,"line":13262},58,[3526,13264,4458],{"class":3950},[3526,13266,11263],{"class":3954},[3526,13268,13270,13272,13274,13277],{"class":3528,"line":13269},59,[3526,13271,11268],{"class":3960},[3526,13273,3964],{"class":3954},[3526,13275,13276],{"class":3967}," onEditClicked",[3526,13278,11369],{"class":3950},[3526,13280,13282,13285,13287,13289,13291,13293,13295,13297,13300],{"class":3528,"line":13281},60,[3526,13283,13284],{"class":3954},"        AudiobookData",[3526,13286,8981],{"class":3982},[3526,13288,4026],{"class":3950},[3526,13290,9376],{"class":3982},[3526,13292,3166],{"class":3950},[3526,13294,9096],{"class":3967},[3526,13296,4163],{"class":3950},[3526,13298,13299],{"class":3967},"getSelectedItem",[3526,13301,4150],{"class":3950},[3526,13303,13305,13307,13310,13312],{"class":3528,"line":13304},61,[3526,13306,9272],{"class":4039},[3526,13308,13309],{"class":3950}," (selected != ",[3526,13311,8690],{"class":3960},[3526,13313,4433],{"class":3950},[3526,13315,13317,13320,13322,13324,13326,13329,13331,13333,13335,13337],{"class":3528,"line":13316},62,[3526,13318,13319],{"class":3982},"            statusLabel",[3526,13321,3166],{"class":3950},[3526,13323,13236],{"class":3967},[3526,13325,3996],{"class":3950},[3526,13327,13328],{"class":3999},"\"Editing: \"",[3526,13330,9322],{"class":3950},[3526,13332,9006],{"class":3982},[3526,13334,3166],{"class":3950},[3526,13336,9313],{"class":3967},[3526,13338,4046],{"class":3950},[3526,13340,13342],{"class":3528,"line":13341},63,[3526,13343,9341],{"class":3950},[3526,13345,13347],{"class":3528,"line":13346},64,[3526,13348,4449],{"class":3950},[3526,13350,13352],{"class":3528,"line":13351},65,[3526,13353,4008],{"class":3950},[3526,13355,13357,13359],{"class":3528,"line":13356},66,[3526,13358,4458],{"class":3950},[3526,13360,11263],{"class":3954},[3526,13362,13364,13366,13368,13371],{"class":3528,"line":13363},67,[3526,13365,11268],{"class":3960},[3526,13367,3964],{"class":3954},[3526,13369,13370],{"class":3967}," onDeleteClicked",[3526,13372,11369],{"class":3950},[3526,13374,13376,13378,13380,13382,13384,13386,13388,13390,13392],{"class":3528,"line":13375},68,[3526,13377,13284],{"class":3954},[3526,13379,8981],{"class":3982},[3526,13381,4026],{"class":3950},[3526,13383,9376],{"class":3982},[3526,13385,3166],{"class":3950},[3526,13387,9096],{"class":3967},[3526,13389,4163],{"class":3950},[3526,13391,13299],{"class":3967},[3526,13393,4150],{"class":3950},[3526,13395,13397,13399,13401,13403],{"class":3528,"line":13396},69,[3526,13398,9272],{"class":4039},[3526,13400,13309],{"class":3950},[3526,13402,8690],{"class":3960},[3526,13404,4433],{"class":3950},[3526,13406,13408,13411,13413,13416],{"class":3528,"line":13407},70,[3526,13409,13410],{"class":3982},"            audiobooks",[3526,13412,3166],{"class":3950},[3526,13414,13415],{"class":3967},"remove",[3526,13417,13418],{"class":3950},"(selected);\n",[3526,13420,13422,13424,13426,13428,13430,13433,13435,13437,13439,13441],{"class":3528,"line":13421},71,[3526,13423,13319],{"class":3982},[3526,13425,3166],{"class":3950},[3526,13427,13236],{"class":3967},[3526,13429,3996],{"class":3950},[3526,13431,13432],{"class":3999},"\"Deleted: \"",[3526,13434,9322],{"class":3950},[3526,13436,9006],{"class":3982},[3526,13438,3166],{"class":3950},[3526,13440,9313],{"class":3967},[3526,13442,4046],{"class":3950},[3526,13444,13446],{"class":3528,"line":13445},72,[3526,13447,9341],{"class":3950},[3526,13449,13451],{"class":3528,"line":13450},73,[3526,13452,4449],{"class":3950},[3526,13454,13456],{"class":3528,"line":13455},74,[3526,13457,4060],{"class":3950},[3206,13459,13461],{"id":13460},"модель-даних-audiobookdatajava","Модель даних: AudiobookData.java",[3150,13463,13464],{},"Для роботи з TableView потрібен клас з JavaFX Properties (ми детально розглянемо Properties у наступній статті):",[3518,13466,13468],{"className":3941,"code":13467,"language":3943,"meta":3522,"style":3522},"package dev.kostyl.audiobook.controller;\n\nimport javafx.beans.property.*;\n\npublic class AudiobookData {\n    private final StringProperty title;\n    private final StringProperty author;\n    private final IntegerProperty duration;\n    private final StringProperty genre;\n    \n    public AudiobookData(String title, String author, int duration, String genre) {\n        this.title = new SimpleStringProperty(title);\n        this.author = new SimpleStringProperty(author);\n        this.duration = new SimpleIntegerProperty(duration);\n        this.genre = new SimpleStringProperty(genre);\n    }\n    \n    \u002F\u002F Properties (для TableView)\n    public StringProperty titleProperty() { return title; }\n    public StringProperty authorProperty() { return author; }\n    public IntegerProperty durationProperty() { return duration; }\n    public StringProperty genreProperty() { return genre; }\n    \n    \u002F\u002F Getters\n    public String getTitle() { return title.get(); }\n    public String getAuthor() { return author.get(); }\n    public int getDuration() { return duration.get(); }\n    public String getGenre() { return genre.get(); }\n}\n",[3154,13469,13470,13476,13480,13487,13491,13501,13515,13527,13540,13553,13557,13589,13607,13625,13644,13662,13666,13670,13675,13693,13709,13725,13741,13745,13750,13774,13795,13817,13838],{"__ignoreMap":3522},[3526,13471,13472,13474],{"class":3528,"line":3529},[3526,13473,5315],{"class":3960},[3526,13475,11219],{"class":3950},[3526,13477,13478],{"class":3528,"line":3535},[3526,13479,4837],{"emptyLinePlaceholder":4836},[3526,13481,13482,13484],{"class":3528,"line":3541},[3526,13483,5327],{"class":3960},[3526,13485,13486],{"class":3950}," javafx.beans.property.*;\n",[3526,13488,13489],{"class":3528,"line":3547},[3526,13490,4837],{"emptyLinePlaceholder":4836},[3526,13492,13493,13495,13497,13499],{"class":3528,"line":3553},[3526,13494,3961],{"class":3960},[3526,13496,4390],{"class":3960},[3526,13498,12984],{"class":3954},[3526,13500,4402],{"class":3950},[3526,13502,13503,13505,13508,13511,13513],{"class":3528,"line":3559},[3526,13504,11268],{"class":3960},[3526,13506,13507],{"class":3960}," final",[3526,13509,13510],{"class":3954}," StringProperty",[3526,13512,8301],{"class":3982},[3526,13514,10379],{"class":3950},[3526,13516,13517,13519,13521,13523,13525],{"class":3528,"line":3565},[3526,13518,11268],{"class":3960},[3526,13520,13507],{"class":3960},[3526,13522,13510],{"class":3954},[3526,13524,11487],{"class":3982},[3526,13526,10379],{"class":3950},[3526,13528,13529,13531,13533,13536,13538],{"class":3528,"line":3571},[3526,13530,11268],{"class":3960},[3526,13532,13507],{"class":3960},[3526,13534,13535],{"class":3954}," IntegerProperty",[3526,13537,9826],{"class":3982},[3526,13539,10379],{"class":3950},[3526,13541,13542,13544,13546,13548,13551],{"class":3528,"line":3577},[3526,13543,11268],{"class":3960},[3526,13545,13507],{"class":3960},[3526,13547,13510],{"class":3954},[3526,13549,13550],{"class":3982}," genre",[3526,13552,10379],{"class":3950},[3526,13554,13555],{"class":3528,"line":3583},[3526,13556,4008],{"class":3950},[3526,13558,13559,13561,13563,13565,13567,13569,13571,13573,13575,13577,13579,13581,13583,13585,13587],{"class":3528,"line":3589},[3526,13560,4411],{"class":3960},[3526,13562,12984],{"class":3967},[3526,13564,3996],{"class":3950},[3526,13566,4424],{"class":3954},[3526,13568,8301],{"class":3982},[3526,13570,3173],{"class":3950},[3526,13572,4424],{"class":3954},[3526,13574,11487],{"class":3982},[3526,13576,3173],{"class":3950},[3526,13578,9823],{"class":3954},[3526,13580,9826],{"class":3982},[3526,13582,3173],{"class":3950},[3526,13584,4424],{"class":3954},[3526,13586,13550],{"class":3982},[3526,13588,4433],{"class":3950},[3526,13590,13591,13594,13596,13598,13600,13602,13604],{"class":3528,"line":3595},[3526,13592,13593],{"class":3960},"        this",[3526,13595,3166],{"class":3950},[3526,13597,3666],{"class":3982},[3526,13599,4026],{"class":3950},[3526,13601,4040],{"class":4039},[3526,13603,9512],{"class":3967},[3526,13605,13606],{"class":3950},"(title);\n",[3526,13608,13609,13611,13613,13616,13618,13620,13622],{"class":3528,"line":3601},[3526,13610,13593],{"class":3960},[3526,13612,3166],{"class":3950},[3526,13614,13615],{"class":3982},"author",[3526,13617,4026],{"class":3950},[3526,13619,4040],{"class":4039},[3526,13621,9512],{"class":3967},[3526,13623,13624],{"class":3950},"(author);\n",[3526,13626,13627,13629,13631,13634,13636,13638,13641],{"class":3528,"line":3607},[3526,13628,13593],{"class":3960},[3526,13630,3166],{"class":3950},[3526,13632,13633],{"class":3982},"duration",[3526,13635,4026],{"class":3950},[3526,13637,4040],{"class":4039},[3526,13639,13640],{"class":3967}," SimpleIntegerProperty",[3526,13642,13643],{"class":3950},"(duration);\n",[3526,13645,13646,13648,13650,13653,13655,13657,13659],{"class":3528,"line":3613},[3526,13647,13593],{"class":3960},[3526,13649,3166],{"class":3950},[3526,13651,13652],{"class":3982},"genre",[3526,13654,4026],{"class":3950},[3526,13656,4040],{"class":4039},[3526,13658,9512],{"class":3967},[3526,13660,13661],{"class":3950},"(genre);\n",[3526,13663,13664],{"class":3528,"line":3619},[3526,13665,4449],{"class":3950},[3526,13667,13668],{"class":3528,"line":4612},[3526,13669,4008],{"class":3950},[3526,13671,13672],{"class":3528,"line":4618},[3526,13673,13674],{"class":3976},"    \u002F\u002F Properties (для TableView)\n",[3526,13676,13677,13679,13681,13684,13687,13690],{"class":3528,"line":4624},[3526,13678,4411],{"class":3960},[3526,13680,13510],{"class":3954},[3526,13682,13683],{"class":3967}," titleProperty",[3526,13685,13686],{"class":3950},"() { ",[3526,13688,13689],{"class":4039},"return",[3526,13691,13692],{"class":3950}," title; }\n",[3526,13694,13695,13697,13699,13702,13704,13706],{"class":3528,"line":4630},[3526,13696,4411],{"class":3960},[3526,13698,13510],{"class":3954},[3526,13700,13701],{"class":3967}," authorProperty",[3526,13703,13686],{"class":3950},[3526,13705,13689],{"class":4039},[3526,13707,13708],{"class":3950}," author; }\n",[3526,13710,13711,13713,13715,13718,13720,13722],{"class":3528,"line":4918},[3526,13712,4411],{"class":3960},[3526,13714,13535],{"class":3954},[3526,13716,13717],{"class":3967}," durationProperty",[3526,13719,13686],{"class":3950},[3526,13721,13689],{"class":4039},[3526,13723,13724],{"class":3950}," duration; }\n",[3526,13726,13727,13729,13731,13734,13736,13738],{"class":3528,"line":4928},[3526,13728,4411],{"class":3960},[3526,13730,13510],{"class":3954},[3526,13732,13733],{"class":3967}," genreProperty",[3526,13735,13686],{"class":3950},[3526,13737,13689],{"class":4039},[3526,13739,13740],{"class":3950}," genre; }\n",[3526,13742,13743],{"class":3528,"line":4948},[3526,13744,4008],{"class":3950},[3526,13746,13747],{"class":3528,"line":4958},[3526,13748,13749],{"class":3976},"    \u002F\u002F Getters\n",[3526,13751,13752,13754,13757,13760,13762,13764,13766,13768,13771],{"class":3528,"line":4968},[3526,13753,4411],{"class":3960},[3526,13755,13756],{"class":3954}," String",[3526,13758,13759],{"class":3967}," getTitle",[3526,13761,13686],{"class":3950},[3526,13763,13689],{"class":4039},[3526,13765,8301],{"class":3982},[3526,13767,3166],{"class":3950},[3526,13769,13770],{"class":3967},"get",[3526,13772,13773],{"class":3950},"(); }\n",[3526,13775,13776,13778,13780,13783,13785,13787,13789,13791,13793],{"class":3528,"line":4977},[3526,13777,4411],{"class":3960},[3526,13779,13756],{"class":3954},[3526,13781,13782],{"class":3967}," getAuthor",[3526,13784,13686],{"class":3950},[3526,13786,13689],{"class":4039},[3526,13788,11487],{"class":3982},[3526,13790,3166],{"class":3950},[3526,13792,13770],{"class":3967},[3526,13794,13773],{"class":3950},[3526,13796,13797,13799,13802,13805,13807,13809,13811,13813,13815],{"class":3528,"line":5584},[3526,13798,4411],{"class":3960},[3526,13800,13801],{"class":3954}," int",[3526,13803,13804],{"class":3967}," getDuration",[3526,13806,13686],{"class":3950},[3526,13808,13689],{"class":4039},[3526,13810,9826],{"class":3982},[3526,13812,3166],{"class":3950},[3526,13814,13770],{"class":3967},[3526,13816,13773],{"class":3950},[3526,13818,13819,13821,13823,13826,13828,13830,13832,13834,13836],{"class":3528,"line":5589},[3526,13820,4411],{"class":3960},[3526,13822,13756],{"class":3954},[3526,13824,13825],{"class":3967}," getGenre",[3526,13827,13686],{"class":3950},[3526,13829,13689],{"class":4039},[3526,13831,13550],{"class":3982},[3526,13833,3166],{"class":3950},[3526,13835,13770],{"class":3967},[3526,13837,13773],{"class":3950},[3526,13839,13840],{"class":3528,"line":5595},[3526,13841,4060],{"class":3950},[3206,13843,13845],{"id":13844},"запуск-додатку","Запуск додатку",[3518,13847,13849],{"className":3941,"code":13848,"language":3943,"meta":3522,"style":3522},"public class AudiobookApp extends Application {\n    \n    @Override\n    public void start(Stage primaryStage) throws Exception {\n        Parent root = FXMLLoader.load(getClass().getResource(\"\u002Ffxml\u002Faudiobook-list-view.fxml\"));\n        \n        Scene scene = new Scene(root);\n        primaryStage.setTitle(\"Audiobook Platform\");\n        primaryStage.setScene(scene);\n        primaryStage.show();\n    }\n    \n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n",[3154,13850,13851,13865,13869,13875,13897,13926,13930,13945,13959,13969,13979,13983,13987,14007,14013,14017],{"__ignoreMap":3522},[3526,13852,13853,13855,13857,13859,13861,13863],{"class":3528,"line":3529},[3526,13854,3961],{"class":3960},[3526,13856,4390],{"class":3960},[3526,13858,4393],{"class":3954},[3526,13860,4396],{"class":3960},[3526,13862,4399],{"class":3954},[3526,13864,4402],{"class":3950},[3526,13866,13867],{"class":3528,"line":3535},[3526,13868,4008],{"class":3950},[3526,13870,13871,13873],{"class":3528,"line":3541},[3526,13872,4458],{"class":3950},[3526,13874,3955],{"class":3954},[3526,13876,13877,13879,13881,13883,13885,13887,13889,13891,13893,13895],{"class":3528,"line":3547},[3526,13878,4411],{"class":3960},[3526,13880,3964],{"class":3954},[3526,13882,4094],{"class":3967},[3526,13884,3996],{"class":3950},[3526,13886,3632],{"class":3954},[3526,13888,4475],{"class":3982},[3526,13890,11662],{"class":3950},[3526,13892,11665],{"class":3960},[3526,13894,11668],{"class":3954},[3526,13896,4402],{"class":3950},[3526,13898,13899,13901,13903,13905,13907,13909,13911,13913,13915,13917,13919,13921,13924],{"class":3528,"line":3553},[3526,13900,11723],{"class":3954},[3526,13902,4140],{"class":3982},[3526,13904,4026],{"class":3950},[3526,13906,11616],{"class":3982},[3526,13908,3166],{"class":3950},[3526,13910,11735],{"class":3967},[3526,13912,3996],{"class":3950},[3526,13914,11702],{"class":3967},[3526,13916,4163],{"class":3950},[3526,13918,11707],{"class":3967},[3526,13920,3996],{"class":3950},[3526,13922,13923],{"class":3999},"\"\u002Ffxml\u002Faudiobook-list-view.fxml\"",[3526,13925,4181],{"class":3950},[3526,13927,13928],{"class":3528,"line":3559},[3526,13929,5471],{"class":3950},[3526,13931,13932,13934,13936,13938,13940,13942],{"class":3528,"line":3565},[3526,13933,5626],{"class":3954},[3526,13935,4193],{"class":3982},[3526,13937,4026],{"class":3950},[3526,13939,4040],{"class":4039},[3526,13941,4200],{"class":3967},[3526,13943,13944],{"class":3950},"(root);\n",[3526,13946,13947,13949,13951,13953,13955,13957],{"class":3528,"line":3571},[3526,13948,5661],{"class":3982},[3526,13950,3166],{"class":3950},[3526,13952,4116],{"class":3967},[3526,13954,3996],{"class":3950},[3526,13956,4121],{"class":3999},[3526,13958,4003],{"class":3950},[3526,13960,13961,13963,13965,13967],{"class":3528,"line":3577},[3526,13962,5661],{"class":3982},[3526,13964,3166],{"class":3950},[3526,13966,4223],{"class":3967},[3526,13968,4226],{"class":3950},[3526,13970,13971,13973,13975,13977],{"class":3528,"line":3583},[3526,13972,5661],{"class":3982},[3526,13974,3166],{"class":3950},[3526,13976,4235],{"class":3967},[3526,13978,4150],{"class":3950},[3526,13980,13981],{"class":3528,"line":3589},[3526,13982,4449],{"class":3950},[3526,13984,13985],{"class":3528,"line":3595},[3526,13986,4008],{"class":3950},[3526,13988,13989,13991,13993,13995,13997,13999,14001,14003,14005],{"class":3528,"line":3601},[3526,13990,4411],{"class":3960},[3526,13992,4414],{"class":3960},[3526,13994,3964],{"class":3954},[3526,13996,4419],{"class":3967},[3526,13998,3996],{"class":3950},[3526,14000,4424],{"class":3954},[3526,14002,4427],{"class":3950},[3526,14004,4430],{"class":3982},[3526,14006,4433],{"class":3950},[3526,14008,14009,14011],{"class":3528,"line":3607},[3526,14010,4438],{"class":3967},[3526,14012,5733],{"class":3950},[3526,14014,14015],{"class":3528,"line":3613},[3526,14016,4449],{"class":3950},[3526,14018,14019],{"class":3528,"line":3619},[3526,14020,4060],{"class":3950},[3150,14022,14023,14025],{},[3163,14024,6615],{}," Повноцінний додаток з таблицею аудіокниг, панеллю інструментів та статус-баром. Кнопки \"Edit\" та \"Delete\" автоматично вмикаються\u002Fвимикаються залежно від вибору у таблиці.",[3189,14027,14028,14036,14037,14040,14041,14043],{},[3163,14029,5944,14030,14032,14033,14035],{},[3154,14031,12616],{},", а не ",[3154,14034,9170],{},"?"," У цьому прикладі ми використовуємо спрощений клас з JavaFX Properties для демонстрації роботи TableView. У реальному додатку з MVVM-архітектурою ми створимо ",[3154,14038,14039],{},"AudiobookViewModel",", що обгортає Domain-об'єкт ",[3154,14042,9170],{}," та експонує Properties для UI. Це буде детально розглянуто у статті 28.",[3375,14045],{},[3145,14047,14049],{"id":14048},"практичні-завдання","Практичні завдання",[3150,14051,14052],{},"Настав час закріпити вивчений матеріал через практику. Кожне завдання розраховане на поступове ускладнення: від базових операцій до побудови повноцінних екранів.",[3206,14054,14056],{"id":14055},"рівень-1-базові-операції-виправлення-та-розуміння","Рівень 1: Базові операції (Виправлення та розуміння)",[3150,14058,14059],{},[3163,14060,14061],{},"Завдання 1.1: Виправити помилки у коді",[3150,14063,14064],{},"Наступний код містить кілька помилок. Знайдіть та виправте їх:",[3518,14066,14068],{"className":3941,"code":14067,"language":3943,"meta":3522,"style":3522},"public class BrokenApp extends Application {\n    \n    public void start(Stage stage) {\n        VBox root = new VBox();\n        Button button = new Button(\"Click me\");\n        \n        button.setOnAction(event -> {\n            Label label = new Label(\"Button clicked!\");\n            root.getChildren().add(label);\n        });\n        \n        Scene scene = new Scene(root, 300, 200);\n        stage.setScene(scene);\n    }\n    \n    public static void main(String[] args) {\n        launch(args);\n    }\n}\n",[3154,14069,14070,14085,14089,14106,14120,14140,14144,14159,14179,14195,14199,14203,14225,14236,14240,14244,14264,14270,14274],{"__ignoreMap":3522},[3526,14071,14072,14074,14076,14079,14081,14083],{"class":3528,"line":3529},[3526,14073,3961],{"class":3960},[3526,14075,4390],{"class":3960},[3526,14077,14078],{"class":3954}," BrokenApp",[3526,14080,4396],{"class":3960},[3526,14082,4399],{"class":3954},[3526,14084,4402],{"class":3950},[3526,14086,14087],{"class":3528,"line":3535},[3526,14088,4008],{"class":3950},[3526,14090,14091,14093,14095,14097,14099,14101,14104],{"class":3528,"line":3541},[3526,14092,4411],{"class":3960},[3526,14094,3964],{"class":3954},[3526,14096,4094],{"class":3967},[3526,14098,3996],{"class":3950},[3526,14100,3632],{"class":3954},[3526,14102,14103],{"class":3982}," stage",[3526,14105,4433],{"class":3950},[3526,14107,14108,14110,14112,14114,14116,14118],{"class":3528,"line":3547},[3526,14109,5419],{"class":3954},[3526,14111,4140],{"class":3982},[3526,14113,4026],{"class":3950},[3526,14115,4040],{"class":4039},[3526,14117,4147],{"class":3967},[3526,14119,4150],{"class":3950},[3526,14121,14122,14124,14127,14129,14131,14133,14135,14138],{"class":3528,"line":3553},[3526,14123,5521],{"class":3954},[3526,14125,14126],{"class":3982}," button",[3526,14128,4026],{"class":3950},[3526,14130,4040],{"class":4039},[3526,14132,5531],{"class":3967},[3526,14134,3996],{"class":3950},[3526,14136,14137],{"class":3999},"\"Click me\"",[3526,14139,4003],{"class":3950},[3526,14141,14142],{"class":3528,"line":3559},[3526,14143,5471],{"class":3950},[3526,14145,14146,14149,14151,14153,14155,14157],{"class":3528,"line":3565},[3526,14147,14148],{"class":3982},"        button",[3526,14150,3166],{"class":3950},[3526,14152,5548],{"class":3967},[3526,14154,5551],{"class":3950},[3526,14156,5554],{"class":3960},[3526,14158,4402],{"class":3950},[3526,14160,14161,14164,14167,14169,14171,14173,14175,14177],{"class":3528,"line":3571},[3526,14162,14163],{"class":3954},"            Label",[3526,14165,14166],{"class":3982}," label",[3526,14168,4026],{"class":3950},[3526,14170,4040],{"class":4039},[3526,14172,4173],{"class":3967},[3526,14174,3996],{"class":3950},[3526,14176,6019],{"class":3999},[3526,14178,4003],{"class":3950},[3526,14180,14181,14184,14186,14188,14190,14192],{"class":3528,"line":3577},[3526,14182,14183],{"class":3982},"            root",[3526,14185,3166],{"class":3950},[3526,14187,4160],{"class":3967},[3526,14189,4163],{"class":3950},[3526,14191,4166],{"class":3967},[3526,14193,14194],{"class":3950},"(label);\n",[3526,14196,14197],{"class":3528,"line":3583},[3526,14198,5581],{"class":3950},[3526,14200,14201],{"class":3528,"line":3589},[3526,14202,5471],{"class":3950},[3526,14204,14205,14207,14209,14211,14213,14215,14217,14219,14221,14223],{"class":3528,"line":3595},[3526,14206,5626],{"class":3954},[3526,14208,4193],{"class":3982},[3526,14210,4026],{"class":3950},[3526,14212,4040],{"class":4039},[3526,14214,4200],{"class":3967},[3526,14216,4203],{"class":3950},[3526,14218,3859],{"class":4206},[3526,14220,3173],{"class":3950},[3526,14222,7651],{"class":4206},[3526,14224,4003],{"class":3950},[3526,14226,14227,14230,14232,14234],{"class":3528,"line":3601},[3526,14228,14229],{"class":3982},"        stage",[3526,14231,3166],{"class":3950},[3526,14233,4223],{"class":3967},[3526,14235,4226],{"class":3950},[3526,14237,14238],{"class":3528,"line":3607},[3526,14239,4449],{"class":3950},[3526,14241,14242],{"class":3528,"line":3613},[3526,14243,4008],{"class":3950},[3526,14245,14246,14248,14250,14252,14254,14256,14258,14260,14262],{"class":3528,"line":3619},[3526,14247,4411],{"class":3960},[3526,14249,4414],{"class":3960},[3526,14251,3964],{"class":3954},[3526,14253,4419],{"class":3967},[3526,14255,3996],{"class":3950},[3526,14257,4424],{"class":3954},[3526,14259,4427],{"class":3950},[3526,14261,4430],{"class":3982},[3526,14263,4433],{"class":3950},[3526,14265,14266,14268],{"class":3528,"line":4612},[3526,14267,4438],{"class":3967},[3526,14269,5733],{"class":3950},[3526,14271,14272],{"class":3528,"line":4618},[3526,14273,4449],{"class":3950},[3526,14275,14276],{"class":3528,"line":4624},[3526,14277,4060],{"class":3950},[3150,14279,14280,14283,14284,14286,14287,3166],{},[3163,14281,14282],{},"Підказка:"," Перевірте сигнатуру методу ",[3154,14285,3658],{},", чи додано кнопку до контейнера, чи викликано ",[3154,14288,14289],{},"stage.show()",[3150,14291,14292],{},[3163,14293,14294],{},"Завдання 1.2: Створити форму з валідацією",[3150,14296,14297],{},"Створіть форму реєстрації користувача з полями:",[6171,14299,14300,14303,14306,14309,14312],{},[4501,14301,14302],{},"Username (TextField)",[4501,14304,14305],{},"Email (TextField)",[4501,14307,14308],{},"Password (PasswordField)",[4501,14310,14311],{},"Confirm Password (PasswordField)",[4501,14313,14314],{},"Кнопка \"Register\"",[3150,14316,14317],{},"Додайте просту валідацію: кнопка \"Register\" має бути неактивною, якщо будь-яке поле порожнє.",[3150,14319,14320],{},[3163,14321,14322],{},"Завдання 1.3: Список з можливістю видалення",[3150,14324,14325,14326,14328],{},"Створіть ",[3154,14327,9021],{}," з 5 елементами (назви книг). Додайте кнопку \"Remove Selected\", що видаляє обраний елемент зі списку.",[3206,14330,14332],{"id":14331},"рівень-2-логіка-та-колекції-робота-з-даними","Рівень 2: Логіка та колекції (Робота з даними)",[3150,14334,14335],{},[3163,14336,14337],{},"Завдання 2.1: TableView з фільтрацією",[3150,14339,14325,14340,14342,14343,14345],{},[3154,14341,3512],{}," для відображення списку авторів (ім'я, прізвище, кількість книг). Додайте ",[3154,14344,3506],{}," для пошуку: при введенні тексту таблиця має показувати лише тих авторів, чиє ім'я або прізвище містить введений текст.",[3150,14347,14348,14350,14351,14354],{},[3163,14349,14282],{}," Використовуйте ",[3154,14352,14353],{},"FilteredList"," з JavaFX Collections.",[3150,14356,14357],{},[3163,14358,14359],{},"Завдання 2.2: Калькулятор тривалості",[3150,14361,14362],{},"Створіть додаток, що обчислює загальну тривалість аудіокниг у колекції:",[6171,14364,14365,14370,14375],{},[4501,14366,14367,14369],{},[3154,14368,9021],{}," з аудіокнигами (назва + тривалість у хвилинах).",[4501,14371,14372,14374],{},[3154,14373,3509],{},", що показує загальну тривалість у форматі \"X годин Y хвилин\".",[4501,14376,14377],{},"Кнопка \"Add Audiobook\" для додавання нових записів через діалог.",[3150,14379,14380],{},[3163,14381,14382],{},"Завдання 2.3: Динамічна форма",[3150,14384,14385,14386,14388],{},"Створіть форму додавання аудіокниги з ",[3154,14387,8442],{}," для вибору жанру. При виборі жанру \"Podcast\" має з'являтися додаткове поле \"Episode Number\". При виборі інших жанрів це поле має ховатися.",[3150,14390,14391,14350,14393,3282,14396,10139,14399,3166],{},[3163,14392,14282],{},[3154,14394,14395],{},"comboBox.valueProperty().addListener()",[3154,14397,14398],{},"node.setVisible()",[3154,14400,14401],{},"node.setManaged()",[3206,14403,14405],{"id":14404},"рівень-3-архітектура-та-створення-повноцінні-екрани","Рівень 3: Архітектура та створення (Повноцінні екрани)",[3150,14407,14408],{},[3163,14409,14410],{},"Завдання 3.1: Master-Detail інтерфейс",[3150,14412,14413,14414,4378],{},"Створіть додаток з ",[3154,14415,3481],{},[6171,14417,14418,14426,14433],{},[4501,14419,14420,3689,14423,14425],{},[3163,14421,14422],{},"Left:",[3154,14424,9021],{}," зі списком жанрів (Fiction, Non-Fiction, Science, History).",[4501,14427,14428,3689,14430,14432],{},[3163,14429,11987],{},[3154,14431,3512],{}," з аудіокнигами обраного жанру.",[4501,14434,14435,14438],{},[3163,14436,14437],{},"Right:"," Панель деталей обраної аудіокниги (назва, автор, опис, тривалість).",[3150,14440,14441],{},"При виборі жанру зліва — оновлюється таблиця у центрі. При виборі аудіокниги у таблиці — оновлюється панель деталей справа.",[3150,14443,14444],{},[3163,14445,14446],{},"Завдання 3.2: Діалог з поверненням результату",[3150,14448,14449],{},"Створіть головний екран зі списком авторів та кнопкою \"Add Author\". При натисканні кнопки має відкриватися модальний діалог з формою (First Name, Last Name, Bio). Після натискання \"Save\" у діалозі — новий автор має з'являтися у списку на головному екрані.",[3150,14451,14452,14350,14454,14456,14457,3282,14460,3166],{},[3163,14453,14282],{},[3154,14455,3632],{}," з ",[3154,14458,14459],{},"initModality(Modality.APPLICATION_MODAL)",[3154,14461,14462],{},"showAndWait()",[3150,14464,14465],{},[3163,14466,14467],{},"Завдання 3.3: Повноцінний CRUD-додаток",[3150,14469,14470],{},"Створіть додаток для управління колекцією аудіокниг з наступним функціоналом:",[6171,14472,14473,14479,14482,14485,14491,14494],{},[4501,14474,14475,14476,14478],{},"Перегляд списку аудіокниг у ",[3154,14477,3512],{}," (Title, Author, Genre, Duration).",[4501,14480,14481],{},"Додавання нової аудіокниги через діалог з валідацією всіх полів.",[4501,14483,14484],{},"Редагування обраної аудіокниги (завантаження даних у форму).",[4501,14486,14487,14488,3166],{},"Видалення обраної аудіокниги з підтвердженням через ",[3154,14489,14490],{},"Alert",[4501,14492,14493],{},"Пошук аудіокниг за назвою або автором.",[4501,14495,14496],{},"Збереження даних у файл (JSON або CSV) та завантаження при старті додатку.",[3150,14498,14499],{},[3163,14500,14501],{},"Вимоги:",[6171,14503,14504,14507,14510],{},[4501,14505,14506],{},"Використовуйте FXML для всіх екранів.",[4501,14508,14509],{},"Розділіть код на Controller (UI-логіка) та Service (бізнес-логіка).",[4501,14511,14512],{},"Додайте обробку помилок (наприклад, при читанні файлу).",[3375,14514],{},[3145,14516,14518],{"id":14517},"підсумок","Підсумок",[3150,14520,14521],{},"У цій статті ми пройшли шлях від базових концепцій JavaFX до побудови повноцінних інтерактивних додатків. Ось що ми вивчили:",[3150,14523,14524,14527],{},[3163,14525,14526],{},"Архітектура JavaFX:"," Ми зрозуміли, що JavaFX — це не просто набір класів для малювання кнопок, а цілісна архітектура з Scene Graph (ієрархічне дерево вузлів), життєвим циклом Application (init → start → stop) та спеціалізованим JavaFX Application Thread для всіх UI-операцій.",[3150,14529,14530,14533,14534,14536,14537,14536,14539,14536,14541,14543],{},[3163,14531,14532],{},"Scene Graph та компоненти:"," Ми дізналися про ієрархію класів: ",[3154,14535,3412],{}," → ",[3154,14538,3446],{},[3154,14540,3465],{},[3154,14542,3497],{},", та про тріаду Stage → Scene → Root Node, що формує структуру кожного JavaFX-додатку.",[3150,14545,14546,14549,14550,3173,14552,3173,14554,3173,14556,3282,14558,14560],{},[3163,14547,14548],{},"Layout-контейнери:"," Ми вивчили, як організовувати простір через ",[3154,14551,3475],{},[3154,14553,3478],{},[3154,14555,3481],{},[3154,14557,3484],{},[3154,14559,7593],{},", замість абсолютного позиціонування. Це робить інтерфейси адаптивними та легкими у підтримці.",[3150,14562,14563,14566,14567,3173,14569,3173,14571,3173,14573,3173,14575,3173,14577,14579],{},[3163,14564,14565],{},"Контроли:"," Ми познайомилися з основними UI-компонентами: ",[3154,14568,3503],{},[3154,14570,3509],{},[3154,14572,3506],{},[3154,14574,8442],{},[3154,14576,9021],{},[3154,14578,3512],{}," — будівельними блоками будь-якого desktop-додатку.",[3150,14581,14582,14585,14586,14589],{},[3163,14583,14584],{},"Event Handling:"," Ми навчилися обробляти події користувача через ",[3154,14587,14588],{},"EventHandler"," та лямбда-вирази, розуміючи важливість делегування логіки до ViewModel замість розміщення всього коду у Controller.",[3150,14591,14592,14595],{},[3163,14593,14594],{},"FXML:"," Ми відкрили декларативний підхід до побудови UI через XML-розмітку, що розділяє структуру інтерфейсу та логіку, робить код читабельнішим та дозволяє використовувати візуальні редактори як Scene Builder.",[3150,14597,14598,14601,14602,14604],{},[3163,14599,14600],{},"Практичний досвід:"," Ми створили кілька робочих додатків: від простого \"Hello World\" до повноцінного списку аудіокниг з ",[3154,14603,3512],{},", панеллю інструментів та статус-баром.",[3150,14606,14607,14608,14611,14612,14614,14615,14617],{},"Але це лише початок. У наступній статті ми заглибимося у ",[3163,14609,14610],{},"JavaFX Properties та Bindings"," — реактивну систему, що дозволяє автоматично синхронізувати стан UI з даними. Ми дізнаємося, як зробити так, щоб зміна значення у ",[3154,14613,3506],{}," автоматично оновлювала ",[3154,14616,3509],{},", а кнопка \"Save\" ставала активною лише при валідних даних — без написання жодного рядка коду для ручного оновлення.",[3150,14619,14620],{},"Після цього ми перейдемо до архітектурних патернів (MVC, MVP, MVVM) та побудуємо повноцінний додаток з розділенням відповідальностей, тестованою логікою та інтеграцією з Google Guice для впровадження залежностей.",[3150,14622,14623],{},"JavaFX — це потужний інструмент, але його справжня сила розкривається лише у поєднанні з правильною архітектурою. І саме цьому присвячені наступні статті нашого курсу.",[3368,14625,14626,14631],{},[3150,14627,14628],{},[3163,14629,14630],{},"Корисні ресурси для подальшого вивчення:",[6171,14632,14633,14640,14648,14656,14664],{},[4501,14634,14635,14639],{},[3199,14636,14638],{"href":3201,"rel":14637},[3203],"Офіційна документація JavaFX"," — API reference та гайди.",[4501,14641,14642,14647],{},[3199,14643,14646],{"href":14644,"rel":14645},"https:\u002F\u002Fopenjfx.io\u002Fjavadoc\u002F21\u002Fjavafx.graphics\u002Fjavafx\u002Fscene\u002Fdoc-files\u002Fcssref.html",[3203],"JavaFX CSS Reference Guide"," — повний список CSS-властивостей.",[4501,14649,14650,14655],{},[3199,14651,14654],{"href":14652,"rel":14653},"https:\u002F\u002Fcontrolsfx.github.io\u002F",[3203],"ControlsFX"," — бібліотека додаткових контролів (Rating, Notifications, PropertySheet).",[4501,14657,14658,14663],{},[3199,14659,14662],{"href":14660,"rel":14661},"http:\u002F\u002Fwww.jfoenix.com\u002F",[3203],"JFoenix"," — Material Design компоненти для JavaFX.",[4501,14665,14666,14669],{},[3199,14667,11856],{"href":11860,"rel":14668},[3203]," — візуальний редактор FXML.",[14671,14672,14673],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .s0P7L, html code.shiki .s0P7L{--shiki-light:#800000;--shiki-default:#808080;--shiki-dark:#808080}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sa4r_, html code.shiki .sa4r_{--shiki-light:#E50000;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}",{"title":3522,"searchDepth":3535,"depth":3535,"links":14675},[14676,14680,14685,14692,14700,14711,14716,14722,14729,14734],{"id":3147,"depth":3535,"text":3148,"children":14677},[14678,14679],{"id":3208,"depth":3541,"text":3209},{"id":3314,"depth":3541,"text":3315},{"id":3379,"depth":3535,"text":3380,"children":14681},[14682,14683,14684],{"id":3390,"depth":3541,"text":3391},{"id":3625,"depth":3541,"text":3626},{"id":3900,"depth":3541,"text":3901},{"id":4638,"depth":3535,"text":4639,"children":14686},[14687,14688,14689,14690,14691],{"id":4645,"depth":3541,"text":4646},{"id":5084,"depth":3541,"text":5085},{"id":5294,"depth":3541,"text":5295},{"id":5746,"depth":3541,"text":5747},{"id":6040,"depth":3541,"text":6041},{"id":6155,"depth":3535,"text":6156,"children":14693},[14694,14695,14696,14697,14698,14699],{"id":6165,"depth":3541,"text":6166},{"id":6210,"depth":3541,"text":6211},{"id":6754,"depth":3541,"text":6755},{"id":7152,"depth":3541,"text":7153},{"id":7587,"depth":3541,"text":7588},{"id":7757,"depth":3541,"text":7758},{"id":7885,"depth":3535,"text":7886,"children":14701},[14702,14703,14704,14705,14706,14707,14708,14709,14710],{"id":7899,"depth":3541,"text":7900},{"id":8119,"depth":3541,"text":8120},{"id":8176,"depth":3541,"text":8177},{"id":8436,"depth":3541,"text":8437},{"id":8748,"depth":3541,"text":8749},{"id":9015,"depth":3541,"text":9016},{"id":9352,"depth":3541,"text":9353},{"id":9757,"depth":3541,"text":9758},{"id":9839,"depth":3541,"text":9840},{"id":9913,"depth":3535,"text":9914,"children":14712},[14713,14714,14715],{"id":9924,"depth":3541,"text":9925},{"id":10104,"depth":3541,"text":10105},{"id":10295,"depth":3541,"text":10296},{"id":10611,"depth":3535,"text":10612,"children":14717},[14718,14719,14720,14721],{"id":10663,"depth":3541,"text":10664},{"id":11201,"depth":3541,"text":11202},{"id":11609,"depth":3541,"text":11610},{"id":11865,"depth":3541,"text":11866},{"id":11966,"depth":3535,"text":11967,"children":14723},[14724,14725,14726,14727,14728],{"id":11973,"depth":3541,"text":11974},{"id":11997,"depth":3541,"text":11998},{"id":12539,"depth":3541,"text":12540},{"id":13460,"depth":3541,"text":13461},{"id":13844,"depth":3541,"text":13845},{"id":14048,"depth":3535,"text":14049,"children":14730},[14731,14732,14733],{"id":14055,"depth":3541,"text":14056},{"id":14331,"depth":3541,"text":14332},{"id":14404,"depth":3541,"text":14405},{"id":14517,"depth":3535,"text":14518},"Від консольних додатків до вікон з кнопками: архітектура JavaFX, Scene Graph, життєвий цикл Application, layout-контейнери та FXML як декларативний опис UI.","md",null,{},{"title":2414,"description":14735},"PWDugi1jC6mJy5kD2g5R9gOxinN0jSmlav5ISeNdTfY",[14742,14744],{"title":2410,"path":2411,"stem":2412,"description":14743,"children":-1},"Від ручного зв'язування об'єктів до повноцінного IoC-контейнера: архітектура Google Guice, анотаційне впровадження, модулі, скопи та інтеграція з JavaFX через ControllerFactory.",{"title":2418,"path":2419,"stem":2420,"description":14745,"children":-1},"Від ручного оновлення UI до автоматичної синхронізації: JavaFX Properties, Change Listeners, Bindings, ObservableList та реактивна парадигма для побудови відгукливих інтерфейсів.",1778998388377]