How to add a stylesheet to a page after the layout has already been built?

The normal way to add a stylesheet to a page is to call \Magento\Framework\View\Page\Config::addPageAsset()
Here is an example: How to load JavaScript or stylesheet conditionally depends on backend config settings?

But sometimes the normal way is not available because the layout has already been built (the \Magento\Framework\View\Layout\Builder::build() has already been finished).
I have found 2 solutions of the problem.

Solution 1

If the layout is already built but HTML document is not rendered yet then we can generate <link> tag manually and insert it to the HTML document.
A <link> tag is valid inside the <body> tag in HTML 5.

We can use the \Magento\Framework\View\Asset\File::getUrl() method to get an asset url:

/**
 * @param string $asset
 * @return string
 */
function getAssetUrl($asset) {
	/** @var \Magento\Framework\ObjectManagerInterface $om */
	$om = \Magento\Framework\App\ObjectManager::getInstance();
	/** @var \Magento\Framework\View\Asset\Repository $assetRepository */
	$assetRepository = $om->get('Magento\Framework\View\Asset\Repository');
	return $assetRepository->createAsset($asset)->getUrl();
}

For example:

getAssetUrl('Dfe_Markdown::simple-mde/main.css');

Solution 2

If the decision whether to load the asset need to be made interactively on the browser side then we can get the asset url using the Solution 1 and then pass the asset url to a JavaScript component as a parameter.
Then the JavaScript component can generate a <link> tag interactively to load the asset:

var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = url;
document.getElementsByTagName("head")[0].appendChild(link);

It is not a simple way but it works. Waiting for a standard and more simplier way:

https://github.com/magento/magento2/issues/2194

See also: