Optis modelu danych bazuje na standardzie [JSONSchema](https://json-schema.org/specification.html) w wesji `draft-07` # Użycie `schema.json` ```JSON { "type": "object", "properties": { "imie": { // jeśli nie ma typu to przyjmuje domyślnie string "title": "Imię", "fieldRequired": true, "minLength": 3 }, "nazwisko": { "title": "Naziwsko", "type": "string", "fieldRequired": true, "minLength": 3 }, "nazwa": { "calc": "$methods.trim(this.imie + ' ' + this.naziwsko)" } } } ``` `App.vue` ```HTML ``` # Rozwinięcia ## `$ref` Pobranie schematu z definicji ```JSON "faktury": { "type": "array", "items": { "$ref" : "#/definitions/faktura" } }, "definitions": { "faktura": { "type": "object", "properties": { "netto": { "type": "money" }, "brutto": { "type": "money" } } } } ``` można używać jako schematu bazowego z połączeniem z `allOf` ```JSON "faktury": { "type": "array", "items": { "allOf": [ { "$ref" : "#/definitions/faktura" }, { "vat": { "type": "money" } } ] } } ``` można adres referencji wyliczyć na podstawie dostarczonego modelu: ```JSON { "properties": { "typorganizacji": { "type": "integer" }, "opis": { "allOf": [ { "type": "string", "default": "Opis" }, { "$ref": "./definitions/typ{{typorganizacji}}", "definitions": { "typ1": { "title": "Tytuł 1" }, "typ2": { "title": "Tytuł 2" } } } ] }, "address": { "$ref": "#/definitions/address{{typorganizacji}}" } }, "definitions": { "address1": { "type": "object", "properties": { "street": { "type": "string", "default": "Sienkiewicza" } } }, "address2": { "type": "object", "properties": { "city": { "type": "string", "default": "Slupsk" } } } } } ``` ## `aggregate` Wartośc pola jest wyliczana na podstawie przeliczania tabeli ```JSON "aggregate": { "array": "kosztorys", // = this.kosztorys gdy nazwa jest prosta, dodawane jest this. na początku "property": "wartosc", // = $item.wartosc gdy występuje proste property to jest dodawane $item. na początku, jeśli jest pominięta agreguje po prostu elementy tablicy, np [1, 4, 67] "func": "sum", "when": "$item.cena>0" //tutaj trzeba pisać pełną nazwę pola, pola z bieżącego wiersza dostępne są poprzez $item, lub $array[$index] } ``` Dostępne funkcje: - `sum` - suma, domyślna funkcja gdy jej nie podano - `max` - wartość maksymalna - `min` - wartość minimalna - `avg` - średnia - `count` - liczba elementów, ma sens przy podaniu `when`, w innym przypadku lepiej użyć `calc` i `array.length` - `mul` - iloczyn - `first` - zwraca pierwszy element - `last` - zwraca ostatni element - `map` - zwraca talicę elementów - `distinct` - tablica unikalnych wartości zdefiniowanych w `prop` - `distinctitem` - tablica wszystkich elementów z unikalnymi `prop`, w odróżnieniu od `distinct` zwraca całe wiersze tablicy Dostępne właściwości (do uzycia w `array`, `property` i `when`): - `this` - bieżący kontekst modelu, wlasciwosci która jest wyliczana - `$array` - aktualna tablica na której odbywa się agregacja - `$index` - aktualny indeks w tablicy - `$item` - aktualny element w tablicy, odpowiednik `$array[$index]` - `$root` - gałąź główna modelu - `$parent` - gałąz nadrzedna względem `this` - `$parent.$parent`, `$parent2` - gałąź modelu nadrzędna do `$parent` - `$parent.$parent.$parent`, `$parent3` - gałąź modelu nadrzędna do `$parent2` - `$parent4` - gałąź modelu nadrzędna do `$parent3` - `$parent5` - gałąź modelu nadrzędna do `$parent4` - `$obj` - bieżący obiekt schematu, dostepne pola typu `$schema`, `$key` i inne - `$methods` - metody matematyczne, lista poniżej Funkcja `when` jest wykonywana dla kadego elementu tablicy, spowalnia to znacznie wykonanie funkcji, uywac tylko w razie konieczności W `array` mozna skorzystać z `$root`, `$parent` i zewnętrznych danych z gałęzi `$external`. `array` moze byc tablica nazw, wtedy agregacji odbedzie sie po wielu talicach: ```JSON "aggregate": { "array": ["kosztorys1", "kosztorys2"], "property": "brutto" } ``` jeśli nazwa tablicy zawiera znak `/` to jest przeprowadzane sumowanie pod podtablicy, czyli np. `kosztorys[0...].fakturydodatowe[]` ```JSON "aggregate": { "array": "kosztorys/fakturydodatowe", "property": "brutto" } ``` W `property` równiez mozna skorzystac ze złozonych kalkulacji typu `$item.ilosc * $item.cena` ale znacznie spowalnia to wykonanie pętli wyliczającej, uzywac tylko w ramach koniecznosci. Lepiej wykonac dwa proste sumowania i pozniej policzyc iloraz. ## `allOf` Służy do łączenia wielu schematów, wg kolejności ich podania ```JSON "allOf": [ { "faktury": { "properties": { "netto": { "type": "money" } } } }, { "faktury": { "properties": { "brutto": { // dołożenie do faktur pola brutto "type": "money" } } } }, ] ``` ## `calc` Zawiera informacje o tym że wartośc pola jest wyliczana na podstawie innych pól, przykład: ```JSON "calc" : "this.ilosc * this.cena" ``` Dostępne właściwości `$root` `$parent` oraz wszystkie własciwosci z gałęzi `$external`, patrz `aggregate` `this` jest wymagane w odwołaniach do pól `calc` może być podana jako obiekt: ```JSON "calc": { "func": "this.ilosc * this.cena", "when": "this.ilosc>0" } ``` gdy warunek `when` nie jest spełniony to model nie jest uzupełniany, czyli trzeba ręcznie wypełnić `calc` lub `func` może być podane jako tablica, trzeba ustawić zmienną `$result`, zwracanie wartości przez `return` nie uruchamia zaokrągleń wynikających z typu danych (`money` `integer`) ```JSON "calc": [ "var ile = this.ilosc;", "if (ile>10) {", " $result = ile * this.cena", "} else {", " $result = this.cena", "}" ] ``` ## `computed` Ustawienie tej właściwości na `true` spowoduje, że pole to będzie tworzonena potrzeby interfejsu, a przy zapisie będzie to pole usuwane ```JSON "computed": true ``` ## `disabled` Ustawienie tej właściwości na `true` spowoduje, że pole to będzie wyłączone, przy zapisie będzie ustawiania wartośc `default` lub `undefined`. Wyłaczone są również walidacje i wyliczenia ```JSON "disabled": true ``` ```JSON "disabled": "!this.innyAdresZamieszkania" ``` ## `debug` _Tylko w trybie development_ Ustawienie tej wartości (w połączeniu z `calc` lub `aggregate`) powoduje, że Chrome zatrzyma się przed obliczeniem wartości tego pola w celu sprawdzenia poprawności obliczeń ## `enum` Pole musi przybrać jedną z podanych wartości ```JSON "enum": [1, 2, 3] //musi przyjąc wartośc 1 lub 2 lub 3 ``` ```JSON "enum": [true] // musi być true ``` ```JSON "enum": "[this.Koszt]" //musi być równe polu Koszt ``` w przypadku gdy walidowane pole jest elementem tablicy, to w przypadku nie spełniania `enum` element jest automatycznie usuwany z tablicy ```JSON { "properties": { "sfery": { "type": "array", "items": { "type": "number", "enum": [1, 2, 3] } }, "sfery2": { "type": "array", "items": { "type": "number", "enum": "$root.sfery" } } } }; ``` ## `format` Dodatkowe formaty walidacji / formatowania tekstu - `email` - mail - `nip` - sprawdza prawidłowość numeru NIP - `pesel` - poprawność PESEL - `regon` - poprawność REGON - `iban` / `nrb` - numer konta - `dowod` - numer dowodu - `data-time` - data i czas `yyyy-MM-dd HH:mm:ss` - `date` - sama data `yyyy-MM-dd` - `kod` - kod pocztowy - `kw` - symbol księgi wieczystej `SL1S/12345678/9` - `numeric` - tylko cyfry - `url` - sprawdza poprawność adresu URL - `passport` - numer paszportu - `uppercase` - zamienia na wielkie litery - `lowercase` - zamienia na małe litery - `capitalize` - zamienia pierwszą litere wyrazu na wielką, resztę na małe ## `length` Pilnuje długości tablicy, można być wyliczane na podstawie innych pól ## `minimum` Minimalna wartość, `number` ## ~~`summary`~~ - wycofane, patrz `aggregate` Wartośc pola jest wyliczana na podstawie podsumowania tabeli ```JSON "summary": { "array": "kosztorys", "property": "wartosc" } ``` W `array` mozna skorzystac z `$root`, `$parent` i zewnętrznych danych z gałęzi `external` ## `type` - `string` - tekst - `integer` - całkowita - `number` - zmiennoprzecinkowa - `money` - automatycznie zaokrągla do 2 miejsc po przecinku - `boolean` - `true`/`false` ## `validationMessages` Komunikaty walidacji, według klucza, przykład: ```JSON "validationMessages": { "required": "Pole XXX jest wymagane", "type": "Nieprawidłowy typ danych", "minimum": "Wartość musi być większa niż 10", "validFunc": "Wartośc musi być większa niz w konkursie" } ``` można używać zmiennych w komuniakcie, poza tymi z walidacji dostępne jest `value` ```JSON "validationMessages": { "minimum": "Wartość musi być większa niż {{minimum}}, a wynosi {{value}}" } ``` ## `validFunc` Dowolna funkcja do walidacji ```JSON "validFunc": "$value>=$external.$konkurs.minValue" ``` Mozliwość koszystania z właściwości `$root` `$parent` oraz wszystkich własciwosci z gałęzi `$external`. `$value` oznacza bierzącą wartość pola, pozostałe wartości identyczne jak w `aggregate` Bardziej złozne funkcje muszą być otoczone klamrą: ```JSON "validFunc": "{if ($value>0) return \"za mało\"}" ``` Można również zdefiniować jako tablicę, zamiast `return` można użyć zmiennej wyjściowej `$result` ```JSON "validFunc": [ "if ($value>0) {", " $result = \"za mało\"", "}" ] ``` - `true` - wszystko ok, zwraca `null` - `false` - zwraca `validFunc` - `null` - wszystko ok - niepusty tekst - zwraca ten tekst jako komunikat błędu ## `$methods` Metody których można używać w funkcjach wyliczeniowych, agregacyjnych i walidacyjnych, np. `$methods.mround(this.Kwota)` - `mround`: funkcja zaokrąglenia do określonej liczny miejsc po przecinku, domyślnie do 2 ```TypeScript mround(value: number, precision: number = 2): number ``` - `fmoney`: formatowanie wartości liczbowych, `forceZeros` oznacza że zawsze będzie określona liczba miejsc po przecinku ```TypeScript fmoney(value: number, forcezeros?: boolean, precision: number = 2): string ``` - `fmoneySpace`: formatowanie wartości liczbowych, `forceZeros` oznacza że zawsze będzie określona liczba miejsc po przecinku ```TypeScript fmoneySpace(value: number, forcezeros?: boolean, precision: number = 2): string ``` - `fdate`: formatowanie daty do formatu `yyyy-MM-dd` ```TypeScript fdate(dt: Date): string ``` - `ftime`: formatowanie czasu do formatu `HH:mm:ss` ```TypeScript ftime(dt: Date): string ``` - `fdatetime`: formatowanie daty i czasu do formatu `yyyy-MM-dd HH:mm:ss` ```TypeScript fdatetime(dt: Date) : string ``` - `dateFromPesel`: wyciągnięcie daty z numeru PESEL w formacie `yyyy-MM-dd` ```TypeScript dateFromPesel(pesel: string) : string ``` - `includesAll`: sprawdza czy pierwsza tablica zawiera wszystkie elementy z drugiej tablicy ```TypeScript includesAll(arr: number[], requiredArr: number[]): boolean ``` - `padNumber`: dodanie brakujących zer przed liczbą, domyślnie wyrównuje do 2 znaków ```TypeScript padNumber(value: number, minLength: number = 2) : string ``` - `dateDiff`: oblicznenie różnicy dat, zwraca obiekt `TimeSpan` określający różnicę w datach, np. `dateDiff(this.DateEnd,this.DateStart).days` ```TypeScript dateDiff(lateDate: string, earlyDate: string): TimeSpan class TimeSpan { totalMiliseconds: number; //całkowita liczba milisekund miliseconds: number; //liczba milisekund po odjęciu sekund, minut, godzin i dni totalSeconds: number; //całkowita liczba sekund = totalMiliseconds/1000 seconds: number; //liczba sekund po odjęciu minut, godzin i dni totalMinutes: number; //całkowita liczba minut = totalSceconds/60 minutes: number; //liczba minut po odjęciu godzin i dni totalHours: number; //całkowita liczba godzin = totalMinutes/60 hours: number; //liczba godzn po odjęciu liczby dni totalDays: number; //całkowita liczba dni = totalHours/24 days: number; //całkowita liczba dni, zaokrąglona w dół do płenych dni months: number; //liczba miesięcy, wartośc całkowita years: number; //liczba lat, wartośc całkowita toString(): string; //różnica w postaci HH:mm:ss } ``` - `today`: dziś, w formacie `yyyy-MM-dd` ```TypeScript today() : string ``` - `now`: teraz, w formacie `yyyy-MM-dd HH:mm:ss` ```TypeScript now() : string ``` - `generateGuid`: oblicza nowy guid ```TypeScript generateGuid() : string ``` - `check`: sprawdza format tekstu, np. `check('nip',this.Nip)` ```TypeScript check(format: string, value: string) : boolean ``` - `datePart`: zwraca określone pole z daty. np. `datePart(this.DateStart,'d')` ```TypeScript datePart(date: string, part: string): number wartości part: 'd' - dzień 'M' - miesiąc 'y' - rok 'h' - godzina 'm' - minuta 's' - sekunda ``` - `dateAdd`: dodaje do daty określoną liczbę jednostek, np. `dateAdd(this.DateStart,'d',7)` ```TypeScript dateAdd(date: string, part: string, value: number) : string // wartości part identyczne jak w datePart() ``` - `dateSet`: ustawia w dacie odpowiednią część, np. `dateSet(this.DateStart,'d',7)` ```TypeScript dateSet(date: string, part: string, value: number) : string // wartości part identyczne jak w datePart() ``` - `dateParse`: zamienia datę w formacie `yyyy-MM-dd` lub `yyyy-MM-dd HH:mm:ss` ```TypeScript dateParse(date: string) : Date ``` - `dateHoliday`: zwraca nazwę świeta (tylko dni wolne od pracy) na podstawie daty lub pusty tekst gdy świeta nie ma ```TypeScript dateHoliday(date: string) : string; ``` * `listMonths`: lista miesięcy pomiędzy datami ```TypeScript listMonths(startDate: string, endDate: string, format?: string/* = 'MMMM yyyy'*/): string[] ``` * `listYears`: lista lat pomiędzy datami ```TypeScript listYears(startDate: string, endDate: string): number[] ``` * `trim`: obcina spacje w tekscie ```TypeScript trim(text: string): string ``` * `capitalize`: zamienia pierwszą literę w wyrazie na wielką resztę na małe "jacek wrycz-rekowski" -> "Jacek Wrycz-Rekowski" ```TypeScript capitalize(text: string): string ``` * `percent`: obliczanie wartości procentowej, zabezpieczenie przed dzieleniem przez 0 i różnymi znakami ```TypeScript percent(value: number, totalValue: number): number ``` * `max`: oblicza maksymalną wartośc z tablicy, np. `max(['2019-01-01','2019-05-01'])` ```TypeScript max(values: any[]) : any ``` * `min` oblicza minimalną wartośc z tablicy, np. `min(['2019-01-01','2019-05-01'])` ```TypeScript min(values: any[]) : any ``` * `sum` sumuje wartości w tablicy, np. `sum([1,2])` ```TypeScript sum(array: any[], prop?: string): any ``` * `avg` średnia wartości w tablicy, np. `avg([1,2])` ```TypeScript avg(array: any[], prop?: string): any ``` * `distinct` unikalne wartości z tablicy ```TypeScript distinct(array: any[], prop?: string): any[] ``` * `distinctItem` unikalne wiersze z tablicy ```TypeScript distinctItem(array: any[], prop?: string): any[] ``` * `toWords` liczba slownie ```TypeScript toWords(value: number): string ``` * `toWordsMoney` kwota slownie ```TypeScript toWordsMoney(value: number, currency: string = '', forceShowgr: boolean = false) ``` * `listNumberFormat` formatuje liczbę do tekstu wyświetlanego na liście: "1.", "a)" itp. ```TypeScript listNumberFormat(value: number, type: FormatType = '1.'): string ``` * `nextListNumber` następny numer na liście numerowanej ```TypeScript nextListNumber(levels: number[], level: number = 0, type?: ExtendedFormatType, value?: number): string ``` # funkcje na \$schema obj (czyli na dowolnym polu obudowanego schematu) ## `$dirtyList()` - zwaraca listę pól ze statusem `$dirty === true` ## `$setDefault()` - ustawia domyślną wartośc pola na podstawie schemau, w przypadku tablicy uwzględnia również uztwienie `length` ## `$title: string` - tytuł pola ze schematu (pole `title` ze schema) ## `$shortTitle: string` - skrócony tytuł pola ze schematu (pole `shortTitle` lub `title` ze scheamtu) ## `$decription: string` - opis, (pole `description` ze scheamtu) ## `$enum: any[]` - lista dostępnych wartości pola (pole `enum` ze schematu) ## `$definitions: any` - definicje pól (pole `definitions` ze schematu) ## `$setData(value: any, path?: string, fixArraySize?: boolean)` - ustawienie wartości pola - `path` - ustawia pole wg podenej ścieżki nazwy - `fixArraySize` - w przypadku tabel zmienia ich rozmiar na ten podany w `value` ## `$reset()` - czyściu status `$dirty` i `$error`, na elemencie oraz wszystkich dzieci ## `$validate()` - waliduje pole, ustawia status `$dirty` na true - `$validate(value,true)` - nie ustawia `$dirty` na `true`, jednak wtedy bład należy odczytać przez `$validateResult` ## `$validateAll()` - waliduje pole i wszystkie pola podrzędne ustawia status `$dirty` na true - `$validateAll(true)` - nie ustawia `$dirty` na `true`, jednak wtedy bład należy odczytać przez `$validateResult` ## `$error` - zwraca komunikat błędu, lub `null` jeśłi nie występuje - działą tylko w przypadku ustawionego `$dirty` na `true` ## `$validateResult` - bład ustawiany przez funkcję `$validate()` i `$validateAll()`, niezależnie od stanu `$dirty` ## `$check()` - sprawdza pole używając `warnings`, zwraca tablicę warningów, jeśli nie ma to tablica jest pusta - `$check(value,true)` - nie ustawia `$dirty` na `true`, jednak wtedy bład należy odczytać przez `$checkResult` ## `$checkAll()` - sprawdza pole i wszystkie pola podrzędne używając `warnings` - `$checkAll(true)` - nie ustawia `$dirty` na `true`, jednak wtedy bład należy odczytać przez `$checkResult` ## `$fixDateFormat()` - naprawia format daty w polach `date` i `date-time` # funkcje na \$schema - tylko tablice ## `$addRow(nvalue: any, count: number = 1)` - dodaje wiersza do tabeli, `nvalue` to wartośc nowo dodanego wiersza - `count` można dodać więcej wierszy niż 1 ## `$removeRow(index)` - usuwa wiersz o indeksie `index` ## `$clearRow(index)` - czyści wiersz o indeksie `index` - wstawia domyślne wartości ## `$exchangeRow(index1: number, index2: number)` - zamienia wiersz miejscami ## `$moveUp(index: number)` - przesuwa wiersz w górę ## `$moveDown(index: number)` - przesuwa wiersz w dół ## `$sort(field: string)` - sortuje tablicę wg podanego pola ## `$filter(func: (value: any, index: number, array: any[]) => boolean)` - zwraca elementy tablicy spełniające funkcję ## `$getItems(start?: number, count?: number)` - zwraca wycinek tablicy - `start` - domyślnie 0 - `count` - domyślnie `pageSize`, domyslnie ## `$getPage(page: number, pageSize: number = 25)` - pobiera kolejną stronę ## `pageNumber` - bierząca strona ## `pageSize` - rozmiar strony ## `pageCount` - liczba stron ## `order` - aktualne sortowanie # dodatkowe funkcje dostępne w pakiecie ## `validate()` Walidacja zgodności danych ze schematem ```JavaScript import {validate} from '@witkac/jschema'; const res = validate(schema, model, options); ``` wynikiem obiekt typu `ValidationResult`: ```JavaScript ValidationResult { valid: false, errors: [ { validation: 'format', message: 'Nieprawidłowy format', expected: 'phone9', value: '60409883', path: 'phone' } ] } ``` dostępne opcje: ```JavaScript { external: {}, // dane zewnętrzne validateCalculations: false, // czy sprawdzać poprawnośc wyliczeń disableMessages: false, // czy pomijać komunikat walidacji validationMessages: undefined, // włąsne komunikaty walidacji breakOnFirstError: true, // czy przerywać sprawdzanie pola po pierszym błędzie (tylko jeden błąd na pole) } ``` ## `flatPropsList()` zwraca wszystkie włąsciwości schematy w postaci płaskiej tablicy ```JavaScript import {flatPropsList} from '@witkac/jschema'; flatPropsList(schema); // zwraca płaską listę wszystkiech właściwości ze schematu flatPropsList(schema,true); // na liście będą tylko typy proste (z pominięciem `object` i `array`) ``` # opcje schematu `schemaOptions` ## lista opcji i domyślne wartości (`defaultOptions`) ```TypeScript export const defaultOptions: IOptions = { resetOnFocus: true, // czyszczenie błędów przy focus na polu dirtyOnInput: false, // ustawienie zmian po pierwszej zmianie wartości dirtyOnBlur: true, // ustawienie informacji, że pole zostało dotknięte po wyjściu z pola disableCalculations: false, // wyłaczenie wszelkich kalkulacji disableValidations: false, // wyłaczenie walidacji startDirty: false, // ustawienie wszystkich pól jako zmienione wrapAll: false, // obudowanie od razu wszystkich pól schematu fixData: true, // wypełnianie brakujących pól w modelu przy starcie fixFormat: true, // naprawianie formatu pól tekstowych (np. uppercase) przy starcie validationMessages, // komunikaty walidacji warningMessages: validationMessages, // komunikaty ostrzeżeń }; ``` ## kolejność odczytu - JSchema\defaultOptions - schemat, pole `$options` ```JSON { "$options": { "fixFormat": true, "validationMessages": { "required": "Pole jest wymagane" } } } ``` - instancja Vue, pole `schemaOptions` ```JavaScript { mixins: [schemaMixin], schema, schemaOptions: { fixFormat: true, validationMessages: { required: 'Pole jest wymagane' } } } ``` # dodawanie metod do `$methods` Można dodać swoje metody które można wykorzystać w `$methods` w kalkulacjach ## dodatkowe metody definiowane w schemacie ```JSON { "$methods": { "getone": "return 1", "plusone": { "args": "value", "body": ["return value+1"] }, "plustwo": ["return $0+2"] }, "properties": { ... } } ``` - treść metody może być stringiem lub tablicą stringów - domyślnie argumenty mają nazwy `$0` ... `$9` - jeśli chcemy nazwać argumenty funkcję można podać jako obiekt zawierający właściwości `props` oraz `body` ## dodatkowe metody definiowane w instancji ```JavaScript export default { mixins: [schemaMixin], schema: { properties: { test1: { type: 'number' }, test2: { type: 'number', calc: '$methods.plusone(this.test1)' } } }, data() { return { model: null, plusvalue: 1 } }, $methods: { plusone(value) { return value+this.plusvalue; } } } ``` - `this` jest mapowane na instancję Metody w schemacie przeciążają domyślne metody, metody w instancji przeciążają zarówno domyślne metody jak i te ze schematu