BootBrander:一个 Bootstrap .less 生成器 UI (第 4 部分 / 生成 custom-bootstrap.css)





0/5 (0投票)
创建一个 MVC 站点,允许用户输入来更改 bootstrap 变量并生成自定义品牌的 bootstrap.css。
引言
我们来到了 Bootstrap 颜色选择器 UI 系列的第四部分。在本系列中,我们将为用户提供一种下载他们创建的 CSS 文件的方式。
我们将利用一些本身也很有用的技术。首先,我们将使用 Dotless 库来转换我们的 less 文件。
如果您已经关注了之前的系列,您需要适应我们现在切换到 .NET C# 的事实。
文章索引
DotlessClientOnly nuget
我已经安装了 `DotlessClientOnly` nuget 包。这很重要!不要安装完整包,因为它会在每次请求时转换您的 `.less` 文件,从而阻止我们的 UI 解析它。
一个 FileContentResult 操作
在 Home 控制器中,我们将添加一个名为 `GetCss` 的操作,并将其类型设置为 `FileContentResult`。Home 控制器位于“Controllers/HomeController.cs”路径下。
public FileContentResult GetCss()
{
var contentType = "text/css";
var content = "body { background-color : #fff; }";
var bytes = Encoding.UTF8.GetBytes(content);
var result = new FileContentResult(bytes, contentType);
return result;
}
如果我们运行站点并访问 `Home/GetCss` 路径,我们应该会看到以下输出:
body { background-color : #fff; }
解析我们的 bootstrap.less 文件
首先,我们将忽略用户通过 UI 创建的变量。如果它能按原样解析默认的 `bootstrap.less` 文件,我们就会很高兴。
所以,让我们利用 `Dotless` 类。
public FileContentResult GetCss()
{
//Set the content type
var contentType = "text/css";
//Get the bootstrap.less contents
var bootstrapLessContent = System.IO.File.ReadAllText(
HostingEnvironment.MapPath("~/Content/bootstrap/bootstrap.less")
);
//Create a Dotless Configuration
var config = new DotlessConfiguration();
config.MinifyOutput = false;
config.ImportAllFilesAsLess = true;
config.CacheEnabled = false;
//Parse the .less file
var content = Less.Parse(
bootstrapLessContent
, config);
var bytes = Encoding.UTF8.GetBytes(content);
var result = new FileContentResult(bytes, contentType);
return result;
}
测试
如果您正确解决了所有引用,这应该可以编译。但如果我们运行,它会抛出一个异常。
这发生的原因是 `bootstrap.less` 文件中的其他文件的导入设置时没有路径。因此 `Dotless` 类不知道如何找到它们。
IFileReader
幸运的是,dotless 库为我们提供了一个解决方案。我们可以决定它实际如何找到文件。这可以通过实现 `IFileReader` 接口来完成。这很好,因为我们以后可以利用它来替换我们自己的变量。
但首先,我们将尝试只输出标准的 `.css`。
我们需要创建一个类。我在子文件夹 classes 中完成了这项工作,并将其命名为 `VirtualFileReader`。该类应该如下所示:
internal sealed class VirtualFileReader: IFileReader
{
public byte[] GetBinaryFileContents(string fileName)
{
fileName = GetFullPath(fileName);
return File.ReadAllBytes(fileName);
}
public string GetFileContents(string fileName)
{
fileName = GetFullPath(fileName);
return File.ReadAllText(fileName);
}
public bool DoesFileExist(string fileName)
{
fileName = GetFullPath(fileName);
return File.Exists(fileName);
}
private static string GetFullPath(string path)
{
return HostingEnvironment.MapPath("~/Content/bootstrap/" + path);
}
public bool UseCacheDependencies
{
get { return true; }
}
}
接下来,我们需要 `DotlessConfiguration` 来使用这个类。所以在我们的 `GetCss` 操作中,将此行添加到 `config` 中。
config.LessSource = typeof(VirtualFileReader);
测试
运行站点并访问路径
/home/getcss
我们现在应该会得到一个编译后的 CSS 文件。
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
...... and so on...
保存我们的变量
在本节中,我们将回到 JavaScript,将我们的变量发送到服务器。但首先,我们将在 Home 控制器中再创建一个操作,将它们存储在 `Session` 变量中。
[HttpPost]
public EmptyResult SendVariables()
{
Stream req = Request.InputStream;
req.Seek(0, System.IO.SeekOrigin.Begin);
string variables = new StreamReader(req).ReadToEnd();
HttpContext.Session["variables.less"] = variables;
return new EmptyResult();
}
现在,我们需要回到 JavaScript。基本上,我们将创建一个函数来重写 `viewModel` 中的 `variables.less` 文件。
所以,在我们的 `main.js` 中,我们将创建一个 `sendVariables` 函数,它将遍历类别、它们的变量并重写 `variables.less` 文件。
但首先,我们将添加一些其他东西。在 `_Layout.cshtml` 文件中,在主菜单栏中添加一个 ActionLink。如下所示:
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("Elements", "Elements", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
<li><a href="#"
data-bind="click: sendAndReturnVariablesFile">Get the css</a></li>
</ul>
@Html.Partial("_LoginPartial")
</div>
我们希望用户将此文件作为下载。这不可能通过 Ajax 实现,并且将他们发送到一个空白页也是不可接受的。所以我们将设置一个隐藏的 iframe。
<iframe id="file-download" class="hidden"></iframe>
这可以放在页面的任何位置。但在 `body` 结束标签之前的最底部似乎是最好的位置。
我们刚刚创建了 `sendAndReturnVariables` 绑定,所以我们需要将其添加到我们的 `viewModel` 中。
/**
* Recreates variables.less and sends it to the server
* @function sendVariablesFile
*/
sendAndReturnVariablesFile: function () {
var model = ko.toJS(viewModel),
lessFileString = "",
i, j, prop;
//Recreate the variables.less content
for (prop in model.categories) {
var category = model.categories[prop];
lessFileString += "//== " + prop + '\n';
for (i = 0, j = category.variables.length; i < j; i++) {
var varName = category.variables[i];
lessFileString += varName + ":" + model.variables[varName].value + ";\n";
}
}
//Send it to the server
$.ajax({
url: "/Home/SendVariables",
type: "POST",
data: lessFileString,
success: function () {
//Set the iframe src to the css
$("#file-download").attr("src", "/Home/GetCss");
}
});
现在,我们需要在 `homeController` 的 `GetCss` 操作中添加一行。
var bytes = Encoding.UTF8.GetBytes(content);
var result = new FileContentResult(bytes, contentType);
result.FileDownloadName = "bootstrap-custom.css";
return result;
替换我们的变量
所以现在,我们需要执行最后一步。还记得 `VirtualFileReader` 吗?它有一个 `GetFileContents` 函数,我们将从 `Session` 返回我们保存的变量。
public string GetFileContents(string fileName)
{
if (fileName == "variables.less")
{
var variables = HttpContext.Current.Session["variables.less"];
return (string)variables;
}
else
{
fileName = GetFullPath(fileName);
return File.ReadAllText(fileName);
}
}
结束 (暂时)
我写文章已经有一段时间了,但我后悔了。我非常喜欢创建这个系列,并学到了足够多的知识用于我的实际产品。我希望那里有人能欣赏我的工作。
我可能会写更多关于这个例子的文章。我可以想到一些我们可以做的其他事情。用户可以创建一个账户,让他们可以将多个版本存储在数据库中。
然后我们可以将整个东西存储在 Azure 中。或者可能将 `.css` 文件发布到 Azure CDN。
但在第四部分结束时,我觉得这是一个不错的成品。