Migrace z verze 0.2.x
Verze 1.x a 2.x jsou z velké části API kompatibilní s verzí 0.2.x, ale existují některé zásadní změny. Migrace na verzi 2.x je téměř identická, takže níže uvedené poznámky platí převážně pro obě verze.
Pokud migrujete, zvažte použití nástroje mithril-codemods, který vám může pomoci automatizovat nejjednodušší migrace.
m.prop
odstraněno
Ve verzi 2.x byl m.prop()
nahrazen výkonnější streamovou mikroknihovnou, ale již není součástí jádra. Informace o používání volitelného modulu Streams naleznete v dokumentaci.
v0.2.x
var m = require('mithril');
var num = m.prop(1);
v2.x
var m = require('mithril');
var prop = require('mithril/stream');
var num = prop(1);
var doubled = num.map(function (n) {
return n * 2;
});
m.component
odstraněno
Ve verzi 0.2.x bylo možné komponenty vytvářet pomocí m(Component)
nebo m.component(Component)
. Verze 2.x podporuje pouze m(Component)
.
v0.2.x
// Tyto zápisy jsou ekvivalentní
m.component(Component);
m(Component);
v2.x
m(Component);
m.withAttr
odstraněno
Ve verzi 0.2.x mohli posluchači událostí používat oninput: m.withAttr("value", func)
a podobně. Ve verzi 2.x je stačí číst přímo z cíle události. Funkce dobře spolupracovala s m.prop
, ale protože to bylo odstraněno ve prospěch řešení mimo jádro a verze 1.x nezaznamenala podobné široké, idiomatické využití streamů, m.withAttr
ztratil většinu své užitečnosti.
v0.2.x
var value = m.prop('');
// Ve vašem view
m('input[type=text]', {
value: value(),
oninput: m.withAttr('value', value),
});
v2.x
var value = '';
// Ve vašem view
m('input[type=text]', {
value: value,
oninput: function (ev) {
value = ev.target.value;
},
});
m.version
odstraněno
Obecně to mělo malé využití a vždy si to můžete přidat sami. Pro zjištění, jaké funkce jsou k dispozici, byste měli upřednostňovat detekci funkcí a API verze 2.x je navrženo tak, aby to lépe umožňovalo.
Funkce config
Ve verzi 0.2.x Mithril.js poskytoval jedinou metodu životního cyklu, config
. Verze 2.x poskytuje mnohem jemnější kontrolu nad životním cyklem vnode.
v0.2.x
m('div', {
config: function (element, isInitialized) {
// běží při každém překreslení
// isInitialized je boolean reprezentující, zda byl uzel přidán do DOM
},
});
v2.x
Více informací o těchto nových metodách je k dispozici v lifecycle-methods.md.
m('div', {
// Voláno před vytvořením DOM uzlu
oninit: function (vnode) {
/*...*/
},
// Voláno po vytvoření DOM uzlu
oncreate: function (vnode) {
/*...*/
},
// Voláno před aktualizací uzlu, vrací false pro zrušení
onbeforeupdate: function (vnode, old) {
/*...*/
},
// Voláno po aktualizaci uzlu
onupdate: function (vnode) {
/*...*/
},
// Voláno před odebráním uzlu, vrací Promise, která se vyřeší, až
// bude uzel připraven k odebrání z DOM
onbeforeremove: function (vnode) {
/*...*/
},
// Voláno před odebráním uzlu, ale po volání onbeforeremove done()
onremove: function (vnode) {
/*...*/
},
});
Pokud je k dispozici, lze k DOM-Elementu vnode přistupovat pomocí vnode.dom
.
Změny v chování překreslování
Renderovací engine Mithril.js stále funguje na základě globálních překreslení, která jsou zčásti automatizovaná, ale některé API a chování se liší:
Žádné další zámky překreslování
Ve verzi 0.2.x Mithril.js umožňoval "zámky překreslování", které dočasně zabraňovaly blokované logice kreslení: ve výchozím nastavení m.request
uzamkne smyčku kreslení při spuštění a odemkne ji, když se všechny čekající požadavky vyřeší - stejné chování lze vyvolat ručně pomocí m.startComputation()
a m.endComputation()
. Tyto API a související chování byly odstraněny ve verzi 2.x bez náhrady. Zamykání překreslování může vést k chybovým uživatelským rozhraním: nemělo by být dovoleno, aby zájmy jedné části aplikace bránily ostatním částem zobrazení v aktualizaci, aby odrážely změny.
Zrušení překreslení z obslužných rutin událostí
m.mount()
a m.route()
stále automaticky překreslují po spuštění obslužné rutiny události DOM. Zrušení těchto překreslení z obslužných rutin událostí se nyní provádí nastavením vlastnosti redraw
na předaném objektu události na false
.
v0.2.x
m('div', {
onclick: function (e) {
m.redraw.strategy('none');
},
});
v2.x
m('div', {
onclick: function (e) {
e.redraw = false;
},
});
Synchronní překreslení změněno
Ve verzi 0.2.x bylo možné vynutit, aby Mithril.js okamžitě překreslil, předáním hodnoty truthy do m.redraw()
. Ve verzi 2.x byla tato funkce rozdělena do dvou různých metod pro větší přehlednost.
v0.2.x
m.redraw(true); // okamžitě a synchronně překreslí
v2.x
m.redraw(); // naplánuje překreslení na další requestAnimationFrame tick
m.redraw.sync(); // vyvolá překreslení okamžitě a čeká na jeho dokončení
m.startComputation
/m.endComputation
odstraněno
Jsou považovány za antipatterny a mají řadu problematických okrajových případů, takže byly odstraněny bez náhrady ve verzi 2.x.
Funkce controller
komponenty
Ve verzi 2.x již neexistuje vlastnost controller
v komponentách - místo toho použijte oninit
.
v0.2.x
m.mount(document.body, {
controller: function () {
var ctrl = this;
ctrl.fooga = 1;
},
view: function (ctrl) {
return m('p', ctrl.fooga);
},
});
v2.x
m.mount(document.body, {
oninit: function (vnode) {
vnode.state.fooga = 1;
},
view: function (vnode) {
return m('p', vnode.state.fooga);
},
});
// NEBO
m.mount(document.body, {
// *this* je ve výchozím nastavení vázáno na vnode.state
oninit: function (vnode) {
this.fooga = 1;
},
view: function (vnode) {
return m('p', this.fooga);
},
});
Argumenty komponenty
Argumenty pro komponentu ve verzi 2.x musí být objekt, jednoduché hodnoty jako String
/Number
/Boolean
budou považovány za textové potomky. K argumentům se přistupuje v rámci komponenty čtením z objektu vnode.attrs
.
v0.2.x
var Component = {
controller: function (options) {
// options.fooga === 1
},
view: function (ctrl, options) {
// options.fooga === 1
},
};
m('div', m.component(Component, { fooga: 1 }));
v2.x
var Component = {
oninit: function (vnode) {
// vnode.attrs.fooga === 1
},
view: function (vnode) {
// vnode.attrs.fooga === 1
},
};
m('div', m(Component, { fooga: 1 }));
Potomci vnode komponenty
Ve verzi 0.2.x nebyli potomci vnode komponenty normalizováni, pouze předáváni jako další argumenty a nebyli ani zploštěni. (Interně to jen vracelo částečně aplikovanou komponentu, která byla porovnávána na základě částečné aplikace komponenty.) Ve verzi 2.x jsou potomci vnode komponenty předáváni prostřednictvím vnode.children
jako vyřešené pole potomků, ale stejně jako ve verzi 0.2.x nejsou jednotliví potomci normalizováni, ani není pole potomků zploštěno.
v0.2.x
var Component = {
controller: function (value, renderProp) {
// value === "value"
// typeof renderProp === "function"
},
view: function (ctrl, value, renderProp) {
// value === "value"
// typeof renderProp === "function"
},
};
m(
'div',
m.component(Component, 'value', function (key) {
return 'child';
})
);
v2.x
var Component = {
oninit: function (vnode) {
// vnode.children[0] === "value"
// typeof vnode.children[1] === "function"
},
view: function (vnode) {
// vnode.children[0] === "value"
// typeof vnode.children[1] === "function"
},
};
m(
'div',
m(Component, 'value', function (key) {
return 'child';
})
);
Potomci DOM vnode
Ve verzi 0.2.x byli potomci uzlů DOM reprezentováni doslovně bez normalizace, kromě přímého použití potomků, pokud je přítomno pouze jedno pole potomků. Vrátilo to strukturu spíše takovou, s řetězci reprezentovanými doslovně.
m("div", "value", ["nested"])
// Stane se:
{
tag: "div",
attrs: {},
children: [
"value",
["nested"],
]
}
Ve verzi 2.x jsou potomci uzlů DOM normalizováni na objekty s jednotnou strukturou.
m("div", "value", ["nested"])
// Stane se zhruba:
{
tag: "div",
attrs: null,
children: [
{tag: "#", children: "value"},
{tag: "[", children: [
{tag: "#", children: "nested"},
]},
]
}
Pokud je na DOM vnode přítomen pouze jeden textový potomek, místo toho nastaví text
na tuto hodnotu.
m("div", "value")
// Stane se zhruba:
{
tag: "div",
attrs: null,
text: "",
children: undefined,
}
Další podrobnosti o struktuře vnode ve verzi 2.x a o tom, jak jsou věci normalizovány, naleznete v dokumentech vnode.
Většina vlastností vnode ve verzi 2.x je zde vynechána pro stručnost.
Klíče
Ve verzi 0.2.x jste mohli libovolně kombinovat klíčované a neklíčované vnodes.
Ve verzi 2.x musí být seznamy potomků fragmentů i prvků buď všechny klíčované, nebo všechny neklíčované. Prázdné hodnoty jsou pro účely této kontroly také považovány za neklíčované - již je neignoruje.
Pokud to potřebujete obejít, použijte idiom fragmentu obsahujícího jeden vnode, jako například [m("div", {key: whatever})]
.
Parametry view()
Ve verzi 0.2.x jsou funkcím view předávány odkazy na instanci controller
a (volitelně) všechny možnosti předané komponentě. Ve verzi 2.x je jim předáván pouze vnode
, stejně jako funkci controller
.
v0.2.x
m.mount(document.body, {
controller: function () {},
view: function (ctrl, options) {
// ...
},
});
v2.x
m.mount(document.body, {
oninit: function (vnode) {
// ...
},
view: function (vnode) {
// Použijte vnode.state místo ctrl
// Použijte vnode.attrs místo options
},
});
Předávání komponent do m()
Ve verzi 0.2.x jste mohli předávat komponenty jako druhý argument m()
bez nutnosti jakéhokoli obalování. Pro zajištění konzistence ve verzi 2.x musí být vždy obaleny voláním m()
.
v0.2.x
m('div', Component);
v2.x
m('div', m(Component));
Předávání vnodes do m.mount()
a m.route()
Ve verzi 0.2.x m.mount(element, component)
toleroval vnodes jako druhý argument místo komponent (i když to nebylo zdokumentováno). Stejně tak m.route(element, defaultRoute, routes)
umožňoval používat vnodes jako hodnoty v objektu routes
.
Ve verzi 2.x je třeba v obou případech použít komponenty.
v0.2.x
m.mount(element, m('i', 'hello'));
m.mount(element, m(Component, attrs));
m.route(element, '/', {
'/': m('b', 'bye'),
});
v2.x
m.mount(element, {
view: function () {
return m('i', 'hello');
},
});
m.mount(element, {
view: function () {
return m(Component, attrs);
},
});
m.route(element, '/', {
'/': {
view: function () {
return m('b', 'bye');
},
},
});
m.route.mode
Ve verzi 0.2.x bylo možné nastavit režim směrování přiřazením řetězce "pathname"
, "hash"
nebo "search"
do m.route.mode
. Ve verzi v.2.x
je toto nahrazeno m.route.prefix = prefix
, kde prefix
může být jakákoli předpona. Pokud prefix
začíná #
, funguje se v režimu „hash“, ?
pro režim „search“ a jakýkoli jiný znak (nebo prázdný řetězec) pro režim „pathname“. Podporovány jsou i kombinace, například m.route.prefix = "/path/#!"
nebo ?#
.
Výchozí hodnota byla změněna tak, aby používala předponu #!
(hashbang) namísto pouhého #
. Pokud jste používali výchozí chování a chcete zachovat stávající URL adresy, nastavte m.route.prefix = "#"
před inicializací tras.
v0.2.x
m.route.mode = 'hash';
m.route.mode = 'pathname';
m.route.mode = 'search';
v2.x
// Přímé ekvivalenty
m.route.prefix = '#';
m.route.prefix = '';
m.route.prefix = '?';
m.route()
a kotvy (anchor tags)
Zpracování směrovatelných odkazů nyní používá speciální vestavěnou komponentu místo atributu. Pokud jste toto používali na prvcích jako <button>
, můžete zadat název značky pomocí atributu selector: "button"
.
v0.2.x
// Po kliknutí tento odkaz načte trasu "/path" namísto navigace
m('a', {
href: '/path',
config: m.route,
});
v2.x
// Po kliknutí tento odkaz načte trasu "/path" namísto navigace
m(m.route.Link, {
href: '/path',
});
Šablony cest
Ve verzi 1.x existovaly tři samostatné syntaxe šablon cest, které, ačkoli byly podobné, měly 2 samostatně navržené syntaxe a 3 různé implementace. Bylo to definováno poměrně ad-hoc způsobem a parametry obecně nebyly escapovány. Nyní je vše buď zakódováno, pokud je to :key
, nebo ponecháno v surovém stavu, pokud je to :key...
. Pokud jsou data neočekávaně zakódována, použijte :path...
. Je to tak jednoduché.
Konkrétně, jak to ovlivňuje jednotlivé metody:
URL adresy m.request
Segmenty cest ve verzi 2.x jsou automaticky escapovány při interpolaci a čtou své hodnoty z params
. Ve verzi 0.2.x by m.request({url: "/user/:name/photos/:id", data: {name: "a/b", id: "c/d"}})
odeslal požadavek s URL adresou /user/a%2Fb/photos/c/d
. Ve verzi 2.x by odpovídající m.request({url: "/user/:name/photos/:id", params: {name: "a/b", id: "c/d"}})
odeslal požadavek na /user/a%2Fb/photos/c%2Fd
. Pokud záměrně chcete interpolovat klíč bez escapování, použijte místo toho :key...
.
Interpolace ve vložených řetězcích dotazů, jako například v /api/search?q=:query
, se ve verzi 2.x neprovádí. Místo toho je předejte přes params
s příslušnými názvy klíčů, aniž byste je specifikovali v řetězci dotazu.
Vezměte prosím na vědomí, že se to týká i m.jsonp
. Při migraci z m.request
+ dataType: "jsonp"
na m.jsonp
si toho musíte být také vědomi.
Cesty m.route(route, params, shouldReplaceHistoryEntry)
Tyto nyní umožňují interpolace a fungují identicky s interpolacemi v m.request
.
Vzory tras m.route
Klíče cest ve tvaru :key...
vrací ve verzi 1.x URL dekódované, ale ve verzi 2.x vrací surové URL.
Dříve byly chybně akceptovány výrazy jako :key.md
, přičemž výsledná hodnota parametru byla nastavena na keymd: "..."
. To již neplatí - .md
je nyní součástí vzoru, nikoli názvu.
Čtení/zápis aktuální trasy
Ve verzi 0.2.x veškerá interakce s aktuální trasou probíhala prostřednictvím m.route()
. Ve verzi 2.x to bylo rozděleno do dvou funkcí.
v0.2.x
// Získání aktuální trasy
m.route();
// Nastavení nové trasy
m.route('/other/route');
v2.x
// Získání aktuální trasy
m.route.get();
// Nastavení nové trasy
m.route.set('/other/route');
Přístup k parametrům trasy
Ve verzi 0.2.x bylo čtení parametrů trasy kompletně řešeno prostřednictvím m.route.param()
. Toto API je stále k dispozici ve verzi 2.x a navíc jsou všechny parametry trasy předávány jako vlastnosti v objektu attrs
na vnode.
v0.2.x
m.route(document.body, '/booga', {
'/:attr': {
controller: function () {
m.route.param('attr'); // "booga"
},
view: function () {
m.route.param('attr'); // "booga"
},
},
});
v2.x
m.route(document.body, '/booga', {
'/:attr': {
oninit: function (vnode) {
vnode.attrs.attr; // "booga"
m.route.param('attr'); // "booga"
},
view: function (vnode) {
vnode.attrs.attr; // "booga"
m.route.param('attr'); // "booga"
},
},
});
Sestavování/parsování řetězců dotazu
v0.2.x používal metody zavěšené na m.route
, m.route.buildQueryString()
a m.route.parseQueryString()
. Ve verzi 2.x byly tyto rozděleny a přesunuty do kořenového m
.
v0.2.x
var qs = m.route.buildQueryString({ a: 1 });
var obj = m.route.parseQueryString('a=1');
v2.x
var qs = m.buildQueryString({ a: 1 });
var obj = m.parseQueryString('a=1');
Také ve verzi 2.x je {key: undefined}
serializováno jako key=undefined
pomocí m.buildQueryString
a metod, které jej používají, jako m.request
. Ve verzi 0.2.x byl klíč vynechán a to se přeneslo do m.request
. Pokud jste se na to dříve spoléhali, změňte svůj kód tak, aby klíče z objektu zcela vynechal. Může stát za to použít jednoduchý nástroj k odstranění všech klíčů z objektu, jejichž hodnoty jsou undefined
, pokud to nemůžete snadno udělat a potřebujete zachovat chování verze 0.2.x.
// Volejte vždy, když potřebujete vynechat parametry `undefined` z objektu.
function omitUndefineds(object) {
var result = {};
for (var key in object) {
if ({}.hasOwnProperty.call(object, key)) {
var value = object[key];
if (Array.isArray(value)) {
result[key] = value.map(omitUndefineds);
} else if (value != null && typeof value === 'object') {
result[key] = omitUndefineds(value);
} else if (value !== undefined) {
result[key] = value;
}
}
}
return result;
}
Zabránění odpojení (unmounting)
Již nelze zabránit odpojení pomocí e.preventDefault()
v onunload
. Místo toho byste měli explicitně volat m.route.set
, když jsou splněny očekávané podmínky.
v0.2.x
var Component = {
controller: function () {
this.onunload = function (e) {
if (condition) e.preventDefault();
};
},
view: function () {
return m('a[href=/]', { config: m.route });
},
};
v2.x
var Component = {
view: function () {
return m('a', {
onclick: function () {
if (!condition) m.route.set('/');
},
});
},
};
Spuštění kódu při odebrání komponenty
Komponenty již nevolají this.onunload
, když se odebírají. Nyní používají standardizovaný lifecycle hook (hák životního cyklu) onremove
.
v0.2.x
var Component = {
controller: function () {
this.onunload = function (e) {
// ...
};
},
view: function () {
// ...
},
};
v2.x
var Component = {
onremove: function() {
// ...
}
view: function() {
// ...
}
}
m.request
Promises vrácené funkcí m.request již nejsou getter-settery m.prop
. Kromě toho již nejsou podporovány možnosti initialValue
, unwrapSuccess
a unwrapError
.
Kromě toho požadavky již nemají význam m.startComputation
/m.endComputation
. Místo toho jsou překreslení vždy spuštěna, když se dokončí řetězec promise požadavku (pokud není nastaveno background: true
).
Parametr data
byl nyní rozdělen na params
, což je část s parametry dotazu interpolovanými do URL a připojenými k požadavku, a body
, což je část s tělem, které se má odeslat v podkladovém XHR.
Ve verzi 0.2.x byste použili dataType: "jsonp"
k zahájení požadavku JSONP. Ve verzi 2.x nyní používáte m.jsonp
, který má většinou stejné API jako m.request
bez částí souvisejících s XHR.
v0.2.x
var data = m.request({
method: 'GET',
url: 'https://api.github.com/',
initialValue: [],
});
setTimeout(function () {
console.log(data());
}, 1000);
m.request({
method: 'POST',
url: 'https://api.github.com/',
data: someJson,
});
v2.x
var data = [];
m.request({
method: 'GET',
url: 'https://api.github.com/',
}).then(function (responseBody) {
data = responseBody;
});
setTimeout(function () {
console.log(data); // poznámka: není to getter-setter
}, 1000);
m.request({
method: 'POST',
url: 'https://api.github.com/',
body: someJson,
});
// NEBO
var data = [];
m.request('https://api.github.com/').then(function (responseBody) {
data = responseBody;
});
setTimeout(function () {
console.log(data); // poznámka: není to getter-setter
}, 1000);
m.request('https://api.github.com/', {
method: 'POST',
body: someJson,
});
Kromě toho, pokud je možnost extract
předána do m.request
, bude návratová hodnota poskytnuté funkce použita přímo k vyřešení promise požadavku a zpětné volání deserialize
je ignorováno.
Hlavičky m.request
Ve verzi 0.2.x Mithril.js ve výchozím nastavení nenastavoval žádné hlavičky pro požadavky. Nyní nastavuje až 2 hlavičky:
Content-Type: application/json; charset=utf-8
pro požadavky s těly JSON, které jsou!= null
Accept: application/json, text/*
pro požadavky očekávající odpovědi JSON
První ze dvou hlaviček, Content-Type
, spouští CORS prefetch, protože není hlavičkou požadavku CORS-safelisted kvůli zadanému typu obsahu, a to by mohlo způsobit nové chyby v závislosti na tom, jak je CORS nakonfigurován na vašem serveru. Pokud narazíte na problémy s tímto, možná budete muset přepsat tuto hlavičku předáním headers: {"Content-Type": "text/plain"}
. (Hlavička Accept
nic nespouští, takže ji nemusíte přepisovat.)
Jediné typy obsahu, které specifikace Fetch umožňuje, aby se zabránilo kontrolám CORS prefetch, jsou application/x-www-form-urlencoded
, multipart/form-data
a text/plain
. Nepovoluje nic jiného a záměrně zakazuje JSON.
m.deferred
odstraněno
v0.2.x používal svůj vlastní objekt asynchronní smlouvy, označený jako m.deferred
, který byl použit jako základ pro m.request
. v2.x místo toho používá Promises a implementuje polyfill v nepodporujících prostředích. V situacích, kdy byste použili m.deferred
, byste měli místo toho použít Promises.
v0.2.x
var greetAsync = function () {
var deferred = m.deferred();
setTimeout(function () {
deferred.resolve('hello');
}, 1000);
return deferred.promise;
};
greetAsync()
.then(function (value) {
return value + ' world';
})
.then(function (value) {
console.log(value);
}); //zaznamená "hello world" po 1 sekundě
v2.x
var greetAsync = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve('hello');
}, 1000);
});
};
greetAsync()
.then(function (value) {
return value + ' world';
})
.then(function (value) {
console.log(value);
}); //zaznamená "hello world" po 1 sekundě
m.sync
odstraněno
Protože v2.x používá standardům vyhovující Promises, m.sync
je nadbytečný. Místo toho použijte Promise.all
.
v0.2.x
m.sync([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({
method: 'GET',
url: 'https://api.github.com/users/dead-claudia',
}),
]).then(function (users) {
console.log('Contributors:', users[0].name, 'and', users[1].name);
});
v2.x
Promise.all([
m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }),
m.request({
method: 'GET',
url: 'https://api.github.com/users/dead-claudia',
}),
]).then(function (users) {
console.log('Contributors:', users[0].name, 'and', users[1].name);
});
Vyžadován jmenný prostor xlink
Ve verzi 0.2.x byl jmenný prostor xlink
jediný podporovaný jmenný prostor atributů a byl podporován prostřednictvím speciálního chování. Nyní je analýza jmenných prostorů plně podporována a atributy s jmenným prostorem by měly explicitně deklarovat svůj jmenný prostor.
v0.2.x
m(
'svg',
// atribut `href` je automaticky v jmenném prostoru
m("image[href='image.gif']")
);
v2.x
m(
'svg',
// Uživatelem zadaný jmenný prostor na atributu `href`
m("image[xlink:href='image.gif']")
);
Vnořená pole v pohledech
Pole nyní reprezentují fragmenty, které jsou strukturálně významné ve virtuálním DOM verze 2.x. Zatímco vnořená pole ve verzi 0.2.x by byla zploštěna do jednoho souvislého seznamu virtuálních uzlů pro účely zjišťování změn, verze 2.x zachovává strukturu pole - potomci jakéhokoli daného pole nejsou považováni za sourozence potomků sousedních polí.
Kontroly rovnosti vnode
Pokud se vnode striktně rovná vnode, který zaujímá jeho místo v posledním vykreslení, v2.x přeskočí tuto část stromu, aniž by kontroloval mutace nebo spouštěl jakékoli metody životního cyklu v podstromu. Dokumentace ke komponentám obsahuje podrobnější informace o tomto problému.