Skripty
Vlastní JavaScript funkce rozšiřují formulář tam, kde pravidla nestačí. Skripty musí povolit správce tenantu.
Skript je JavaScript, který běží v prohlížeči každého uživatele formuláře s jeho oprávněními a není izolovaný. Kdo smí měnit konfiguraci formuláře, může tak spustit libovolný kód. Používejte skripty jen tam, kde pravidla a výrazy nestačí, a kód pečlivě kontrolujte.
Kdy použít skript
Skript je poslední možnost. Zkuste nejdřív tyto nástroje, protože jsou bezpečnější, nevyžadují povolení tenantem a návrhář je dokáže zkontrolovat:
- Pravidla: zobrazení, povinnost, zamčení, barvy, banner, blokování uložení.
- Výrazy: výpočty a skládání textu.
- Vlastní tlačítka: hromadné nastavení hodnot jedním kliknutím.
Skript se hodí pro složitou logiku (cykly, více podmínek, práce s poli a objekty), pro hlášky s vlastním textem podle stavu formuláře a pro kontroly, které nejdou zapsat podmínkami.
Politika skriptů (povolení tenantem)
Skripty jsou ve výchozím stavu vypnuté. Zapíná je správce tenantu jednou pro všechny weby (tenantová Storage Entity FormCraft365_ScriptsPolicy = allow; starší klíč SpfxFormCustomizer_ScriptPolicy = enabled se čte jen tehdy, když nový klíč neexistuje, a deny v novém klíči má vždy přednost). Chybějící nebo neznámá hodnota znamená vypnuto. Podrobně: Politika skriptů.
- Zapnutí: v návrháři v části Nastavení → Zabezpečení → Vlastní skripty přepínačem, pokud máte práva správce App Catalogu, jinak návrhář zobrazí příkaz
Set-PnPStorageEntitypro správce. Změna se projeví po novém načtení formuláře, nejpozději do 15 minut. - Chyba nebo chybějící hodnota znamená vypnuto.
- Při vypnutí se skript ani nekompiluje. Nespustí se
onFormLoad,onBeforeSaveani žádná funkce. Pravidla, výrazy a akce tlačítek, které nejsou skript, fungují dál. - Z toho plyne, že
onBeforeSaveneblokuje uložení. Chcete-li uložení blokovat spolehlivě, použijte pravidlo blockSave. - Návrhář (a jen on) vidí v horní části formuláře upozornění, že jsou skripty vypnuté. V editoru skriptů je stejné varování.
Editor skriptů
Bez licence Pro je editor jen pro čtení: nahoře je vysvětlení se štítkem Pro, existující skripty jdou zobrazit, ale ne měnit, a nejdou přidávat funkce. Skripty běží dál (rozhoduje jen politika skriptů). Viz Licencování.
V návrháři otevřete záložku Skripty. Obsahuje tři části a nápovědu s přehledem ctx:
- Skript při načtení (
onFormLoad): kód, který se spustí jednou po načtení výchozích hodnot formuláře. - Skript před uložením (
onBeforeSave): kód spuštěný před odesláním do SharePointu. - Vlastní pojmenované funkce (
functions): funkce volatelné z pravidel a tlačítek. Název funkce musí být platný JavaScript identifikátor (prepocitejDph) a v rámci formuláře jedinečný.
Tlačítkem Otestovat syntaxi zkontrolujete, že kód jde přeložit. Kód se píše jako tělo funkce, ne jako deklarace: do kódu nepište function nazev() {…} kolem něj. Tělo běží v přísném režimu a jako async, takže můžete použít await a return. Jediný parametr je ctx.
ctx s tlačítkem Otestovat syntaxiSkripty při načtení a před uložením
onFormLoad
Spustí se jednou po načtení formuláře (a znovu po změně režimu formuláře nebo konfigurace pravidel). Hodí se pro uvítací hlášku, předvyplnění hodnot nebo počáteční skrývání polí. Skript se nečeká, formulář se vykreslí hned. Návratová hodnota se ignoruje.
onBeforeSave
Spustí se při Uložit po validaci polí a po pravidlech onSave. Výsledek rozhoduje o uložení:
- Vrátí neprázdný text (
return "Doplňte cenu."): uložení se zastaví a text se zobrazí jako chybová hláška nahoře ve formuláři. - Vrátí
false: uložení se zastaví. Pokud skript předtím ohlásil chybu přesctx.notify(text, "error"), zůstane zobrazená jeho hláška. Jinak se zobrazí obecná hláška „Uložení formuláře bylo zastaveno validačním skriptem.“ v jazyce formuláře. - Cokoli jiného (
true,undefined, bezreturn): uložení pokračuje. - Chyba v kódu (výjimka, neplatná syntaxe, blokované
unsafe-eval) uložení zastaví a uživatel uvidí skutečnou chybu, například „Chyba skriptu: Cannot read properties of undefined“. Celý výpis včetně zásobníku je v konzole prohlížeče.
if (!ctx.getValue("Cena")) return "Před uložením vyplňte cenu.";
const celkem = Number(ctx.getValue("Cena")) * Number(ctx.getValue("Kusy") || 1);
if (celkem > 100000 && !ctx.getValue("Schvalovatel")) {
return "Objednávky nad 100 000 Kč musí mít schvalovatele.";
}
Pojmenované funkce
Funkce definujete v editoru a voláte je akcí callFunction v pravidle (cíl $script) nebo akcí callFunction tlačítka. Pravidlo v návrháři vybírá z názvů definovaných funkcí; odkaz na neexistující funkci kontrola konfigurace odmítne. Návratová hodnota funkce se ignoruje. Funkce volaná z pravidla always se spustí, když podmínky začnou platit nebo se změní jejich hodnoty, nejvýše 10krát (ochrana proti smyčce).
{
"scripts": {
"functions": [
{
"id": "fn-dph",
"name": "prepocitejDph",
"description": "Dopočítá cenu s DPH",
"code": "const bez = Number(ctx.getValue('CenaBezDph')) || 0;\nctx.setValue('CenaSDph', Math.round(bez * 1.21 * 100) / 100);"
}
]
}
}
Fáze init a user: kdy je formulář „změněný“
Skript běží ve dvou fázích a podle toho se chová ctx.setValue:
| Fáze | Kdy | Výchozí shouldDirty u ctx.setValue |
|---|---|---|
| init | onFormLoad, funkce volané z pravidel onLoad a první vyhodnocení pravidel při otevření | false: formulář se nepovažuje za změněný |
| user | onBeforeSave, funkce z pravidel onSave, tlačítka a pozdější změny polí | true: formulář je změněný |
- Díky tomu samotné otevření formuláře nespustí varování o neuložených změnách, i když
onFormLoadpředvyplní hodnoty. - Hodnotu nastavenou ve fázi init (bez „změněno“) formulář přesto uloží, i v režimu Úpravy, kde se jinak ukládají jen změněná pole. Platí jen pro hodnoty, které se liší od aktuální.
- Třetím argumentem
ctx.setValue(name, value, { shouldDirty: true })výchozí chování přepíšete. - V režimu zobrazení se formulář nikdy neoznačí za změněný a hodnoty se neukládají.
Volání z pravidel a tlačítek
- Z pravidla: podle spouštěče při změně (
always), po načtení (onLoad) nebo při uložení předonBeforeSave(onSave). - Z tlačítka: při kliknutí, v pořadí s ostatními akcemi.
- Při vypnutých skriptech se volání přeskočí a ostatní akce proběhnou.
Asynchronní kód, chyby a ladění
- Tělo je
async. Můžete použítawait(napříkladawait fetch(...)), formulář ale naonFormLoadnečeká.onBeforeSavea funkce z tlačítek a pravidlaonSavese čekají. - Chyby neshodí formulář. Zobrazí se jako červená hláška v horní části formuláře: Chyba při kompilaci skriptu, Chyba skriptu: … s textem výjimky, nebo hláška o zablokovaném
unsafe-eval(viz Politika skriptů). Podrobnosti jsou v konzole prohlížeče (F12). - Ladicí režim: přidejte k adrese
?fcdebug=1(nebo zapněte debugMode). V konzole se pak pro každý skript vypíše název (napříkladonFormLoadneborule 'X' callFunction 'Y'), doba běhu, návratová hodnota, zprávy zctx.notifya chyba, a také zdroj změněného pole („script:init (nedirty)“). Přeskočený skript při vypnuté politice je v logu označen. - Do konzole můžete ve skriptu psát i
console.log(...).
Ukázky skriptů
Uvítací hláška podle režimu (onFormLoad)
if (ctx.formMode === "new") {
ctx.notify("Vítejte, " + (ctx.currentUser?.title || "") + ". Vyplňte prosím všechna povinná pole.", "info");
}
Předvyplnění hodnoty při nové položce (onFormLoad)
if (ctx.formMode === "new" && !ctx.getValue("Oddeleni")) {
ctx.setValue("Oddeleni", "Obchod"); // fáze init: formulář zůstane nezměněný
}
Skrytí sekce podle skupiny (onFormLoad)
const groups = (ctx.currentUser?.groups || []).map(g => g.toLowerCase());
if (groups.indexOf("schvalovatele") < 0) {
ctx.setVisible("section:sec_schvaleni", false);
}
Přepočet po kliknutí na tlačítko (pojmenovaná funkce)
const bez = Number(ctx.getValue("CenaBezDph")) || 0;
ctx.setValue("CenaSDph", Math.round(bez * 1.21 * 100) / 100);
ctx.notify("Cena s DPH byla přepočítána.", "success");
Validace před uložením (onBeforeSave)
const od = ctx.getValue("Od");
const doo = ctx.getValue("Do");
if (od && doo && String(doo) < String(od)) {
return "Datum „Do“ nesmí být před datem „Od“.";
}
Bezpečnostní upozornění
Skript má stejná práva jako přihlášený uživatel, může číst a měnit data, volat SharePoint a další služby. Povolení skriptů platí pro celý tenant.
- Nevkládejte kód, kterému nerozumíte, ani kód z neověřených zdrojů.
- Konfiguraci smějí měnit jen vlastníci webu a případně skupina návrhářů. Před zamčením úložiště návrhář ukáže souhrn skriptů, které konfigurace obsahuje. Zkontrolujte je.
- Skript nespouští jen formulář, který otevřete, ale každý uživatel, který ho otevře. Chyba se tak týká všech.
- Zvažte, zda je nutné skripty na tenantu povolit. Většinu potřeb pokryjí pravidla a výrazy, které skripty nevyžadují.
API kontextu ctx
Každý skript dostane objekt ctx. Členy typu „jen pro čtení“ nelze měnit. Cíl target u funkcí setVisible a setReadOnly je interní název pole, nebo section:<id sekce>, nebo tab:<id záložky>. Stavy nastavené skriptem se vrátí do výchozího stavu po změně režimu formuláře nebo konfigurace pravidel.
ctx.formMode
Typ: "new" | "edit" | "display" (jen pro čtení). Aktuální režim formuláře: nová položka, úprava, zobrazení.
if (ctx.formMode === "display") return;
ctx.itemId
Typ: number | undefined (jen pro čtení). ID upravované položky. U nové položky (která ještě nebyla uložena) je undefined.
if (ctx.itemId) console.log("Upravuje se položka", ctx.itemId);
ctx.locale
Typ: string | undefined (jen pro čtení). Kód aktuálně zvoleného jazyka formuláře (cs, en, de, sk). Při přepnutí jazyka se hodnota aktualizuje.
ctx.notify(ctx.locale === "en" ? "Saved draft" : "Koncept uložen", "info");
ctx.currentUser
Typ: { id?, email?, loginName?, title?, groups? } | undefined (jen pro čtení). Přihlášený uživatel: ID, e-mail, přihlašovací jméno, zobrazované jméno a názvy SharePoint skupin, ve kterých je členem (groups).
const jeSchvalovatel = (ctx.currentUser?.groups || []).indexOf("Schvalovatelé") >= 0;
ctx.getValue
Typ: (fieldName: string) => unknown. Vrátí aktuální hodnotu pole podle interního názvu. Typ hodnoty závisí na poli: text, číslo, ano/ne, datum jako text, u osob, vyhledávání a spravovaných metadat objekt nebo pole objektů. Neexistující pole vrátí undefined.
const cena = Number(ctx.getValue("Cena")) || 0;
ctx.getAllValues
Typ: () => Record<string, unknown>. Vrátí kopii všech aktuálních hodnot formuláře (změna kopie formulář neovlivní).
const vse = ctx.getAllValues();
console.log(Object.keys(vse).length + " polí");
ctx.setValue
Typ: (fieldName: string, value: unknown, options?: { shouldDirty?: boolean; shouldValidate?: boolean }) => void. Nastaví hodnotu pole. shouldValidate je ve výchozím stavu true (pole se hned zvaliduje). shouldDirty závisí na fázi: v init false, v user true, v režimu zobrazení vždy false. Hodnotu zadejte v tvaru, který pole očekává (číslo, text, objekt s id a title pro osobu nebo vyhledávání).
ctx.setValue("Stav", "Schváleno");
ctx.setValue("Poznamka", "", { shouldDirty: true });
ctx.setVisible
Typ: (target: string, visible: boolean) => void. Zobrazí nebo skryje pole, sekci (section:id) či záložku (tab:id). Skript přepíše stav z pravidel pro daný cíl.
ctx.setVisible("tab:tab_priloha", false);
ctx.setRequired
Typ: (fieldName: string, required: boolean) => void. Nastaví povinnost pole (jen pole, ne sekci či záložku).
ctx.setRequired("DuvodZamitnuti", ctx.getValue("Stav") === "Zamítnuto");
ctx.setReadOnly
Typ: (target: string, readOnly: boolean) => void. Zamkne nebo odemkne pole, sekci (section:id) či záložku (tab:id). V režimu zobrazení zůstanou všechna pole zamčená.
ctx.setReadOnly("section:sec_cena", true);
ctx.setCollapsed
Typ: (sectionId: string, collapsed: boolean) => void. Sbalí nebo rozbalí sekci. Id můžete zadat s předponou section: i bez ní. Funguje u sekcí nastavených jako sbalitelné; uživatel ji může potom ručně přepnout.
ctx.setCollapsed("sec_prilohy", true);
ctx.notify
Typ: (message: string, intent?: "info" | "success" | "warning" | "error") => void. Zobrazí zprávu v horní části formuláře. Výchozí typ je info. Zpráva je jedna: nová nahradí předchozí a uživatel ji může zavřít.
ctx.notify("Pozor, termín je v minulosti.", "warning");