65.9K
CodeProject 正在变化。 阅读更多。
Home

BootBrander - 一个 Bootstrap .less 生成器 UI (第一部分 / 设置 UI)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (2投票s)

2015年3月1日

CPOL

8分钟阅读

viewsIcon

18007

downloadIcon

167

创建一个 MVC 站点,用户可以通过输入更改 bootstrap 变量,并生成自定义品牌的 bootstrap.css。

引言

我在大部分工作中都使用 bootstrap。我非常喜欢它。我发现我不需要编写太多 CSS,因为大部分东西都已存在。

如果我需要不同的外观,我做的第一件事就是修改 variables.less 文件中的 .less 变量。

但是客户应该对颜色有一定的控制权。这个项目的想法是创建一个 UI,从中我们可以更改 bootstrap variables.less 文件中的 .less 变量。

这些更改应该实时反映在站点中。

本文将是一个系列的一部分。您可以在 http://bootbrander.azurewebsites.net/ 找到我完成这个系列后制作的演示。在完成这个系列文章的末尾,您不会得到完全一样的效果,因为我还在不断改进它。

文章索引

必备组件

我正在使用 Visual Studio 2013。我在这里创建了一个标准的 MVC 网站。如果您这样做,您将获得一个包含 bootstrap 的标准设置。

在此之后,我安装了以下 NuGet 包

  • Bootstrap Less Source
  • less.js
  • knockoutjs

bootstrap.less 的工作方式是,bootstrap 中的所有组件都有自己的 .less 文件。但是所有主要的颜色都通过一个名为 variables.less 的文件进行设置。在那里更改一种颜色,它将反映在从 bootstrap.less 生成的整个 boostrap.css 文件中。

Bootstrap.less (部分)

//
// Variables
// --------------------------------------------------


//== Colors
//
//## Gray and brand colors for use across Bootstrap.

@gray-base:              #000;
@gray-darker:            lighten(@gray-base, 13.5%); // #222
@gray-dark:              lighten(@gray-base, 20%);   // #333
@gray:                   lighten(@gray-base, 33.5%); // #555
@gray-light:             lighten(@gray-base, 46.7%); // #777
@gray-lighter:           lighten(@gray-base, 93.5%); // #eee

//@brand-primary:         darken(#428bca, 6.5%); // #337ab7
@brand-primary:         #337ab7;
@brand-success:         #5cb85c;
@brand-info:            #5bc0de;
@brand-warning:         #f0ad4e;
@brand-danger:          #d9534f;

正如您所见,所有颜色都是变量。这只是文件的一部分。但关键在于,这些是我们希望能够访问的颜色。

Less.js

Less.js 允许您将实际的 less 文件直接集成到您的站点中。这样,您就可以实时更改 less 变量。Bootstrap Less Source 包创建了一个名为 \Content\Bootstrap 的文件夹,其中包含 bootstrap.less 文件。

为了将所有内容连接起来,我们需要修改 _Layout.cshtml 文件。我们首先删除这一行

@Styles.Render("~/Content/css")

然后我们添加这些行

<link href="~/Content/bootstrap/bootstrap.less" 
rel="stylesheet/less" type="text/css" />
<script src="~/Scripts/less-1.5.1.min.js"></script>  

您可能会遇到麻烦,因为您的 IISExpress 无法识别 .less 扩展名。如果这样,您将在 web.config 中收到 bootstrap.less 文件的 404 错误。要更改此设置,您必须在 web.config 中声明其 mimeType

将此放在 system.webServer 标签中。

<staticContent>
    <mimeMap fileExtension=".less" mimeType="text/css" />
</staticContent>    

测试

如果一切顺利,您应该能够通过 less.modifyVars 方法实时更改 less 变量。

  1. 启动您的网站
    • 它应该显示 ASP.NET 欢迎页面
  2. 从控制台运行以下命令
    less.modifyVars({ "@body-bg": "#FF0000" });
    • 页面的背景现在应该是红色的;

设置用户界面

正如我们在小测试中看到的,有一个名为 @body-bg 的变量控制着背景颜色。我们先用它来设置我们的界面。

但首先,我们需要一个源文件来存放我们的 JavaScript 代码。我称之为“main.js”,并将其放在根文件夹中。

接下来,我们需要将其添加到 _Layout.cshtml 文件的底部。就在 body 标签之前,并且在其他 bundle 的下方。它应该看起来像这样

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/bundles/knockout")
    @Scripts.Render("~/main.js")
    @RenderSection("scripts", required: false)
</body>

现在,我们将创建一个 knockout viewModel ,并在其中放入一个变量 @body-bg 。并将其绑定到 HTML。我们的 main.js 应该看起来像这样

/* globals ko, less */
(function () {

    var viewModel = window.viewModel = {
        "@body-bg": ko.observable('#ffffff')
    };

    $(document).ready(function () {

        ko.applyBindings(viewModel);

    });

})();

制作变量面板

我决定将包含变量的面板作为左侧的列。我可以通过创建一个部分视图并将它插入到其他每个视图中来实现这一点,但由于我将在每个页面上都需要它,所以我将直接将其放在 _Layout.cshtml 中。

找到这部分

    <div class="container body-content">
        @RenderBody()
    </div>

我们要改变一些 bootstrap 的视图。首先,我想使用页面的全部宽度,所以我将把“container”类改为“container-fluid”。

在其旁边,我需要创建一个“row”,其中包含 2 个列“col-xs-4”和“col-xs-8”。然后,将 @RenderBody() 放在最后一个中。

<div class="container body-content">
   <div class="row">
      <div class="col-xs-2" id="toolbar-container">
      </div>
      <div class="col-xs-10">
          @RenderBody()
      </div>
   </div>
</div> 

所以请记住,我们的 viewModel 有一个字段 @body-bg ,它是一个颜色。我希望可以通过在文本字段中输入确切的十六进制代码来编辑它,但我也想利用新的 HTML5 输入控件 type="color"

我希望设置 knockout 绑定。因此,对于第一个列“toolbar-container”,我将添加

<div class="form-group">
    <label>@@body-bg</label>
    <div class="row">
        <div class="col-xs-9" style="padding-right: 0;">
            <input type="text" class="form-control" 
            data-bind="value: $data['@@body-bg']" />
        </div>
        <div class="col-xs-3" style="padding-left: 0;">
            <input type="color" class="form-control" 
            data-bind="value: $data['@@body-bg']" />
        </div>
    </div>
</div>

注意双 @ 符号。'真正的'变量名只有一个 @ 符号,但这个符号对 Razor 引擎有意义。要转义它,您需要添加一个双 @。

哦。是的,这是一个内联样式。因为我很懒;)

订阅更改

现在,我们将有一个通过 UI 更新的变量。但我们仍然需要调用 less.modifyVars 方法。要做到这一点,我们必须订阅我们变量的更改。

    var viewModel = window.viewModel = {
        "@body-bg": ko.observable('#ffffff')
    }

    viewModel["@body-bg"].subscribe(function () {

        less.modifyVars({
            "@body-bg": viewModel["@body-bg"]()
        });
    });

测试

现在启动网站并更改 color 字段,网站应该会改变颜色,看起来像这样

添加值

现在,您可以决定客户应该被允许更改哪些变量。并将它们添加到视图模型中。

所以让我们添加 @text-color ,这是主要的文本颜色,以及 @brand-primary ,它有所有以“-primary”结尾的类,例如“btn-primary”。

main.js

    var viewModel = window.viewModel = {
        "@body-bg": ko.observable('#ffffff'),
        "@text-color": ko.observable('#777777'),
        "@brand-primary": ko.observable('#337ab7')
    };

    viewModel["@body-bg"].subscribe(function () {

        less.modifyVars({
            "@body-bg": viewModel["@body-bg"]()
        });

    });

    viewModel["@text-color"].subscribe(function () {

        less.modifyVars({
            "@text-color": viewModel["@text-color"]()
        });
    });

    viewModel["@brand-primary"].subscribe(function () {

        less.modifyVars({
            "@brand-primary": viewModel["@brand-primary"]()
        });
    });

HTML

将此放在 @body-bg 定义下方

<div class="form-group">
    <label>@@text-color</label>
    <div class="row">
        <div class="col-xs-9" style="padding-right: 0;">
            <input type="text" class="form-control" 
            data-bind="value: $data['@@text-color']" />
        </div>
        <div class="col-xs-3" style="padding-left: 0;">
            <input type="color" class="form-control" 
            data-bind="value: $data['@@text-color']" />
        </div>
    </div>
</div>

<div class="form-group">
    <label>@@brand-primary</label>
    <div class="row">
        <div class="col-xs-9" style="padding-right: 0;">
            <input type="text" class="form-control" 
            data-bind="value: $data['@@brand-primary']" />
        </div>
        <div class="col-xs-3" style="padding-left: 0;">
            <input type="color" class="form-control" 
            data-bind="value: $data['@@brand-primary']" />
        </div>
    </div>
</div>

测试

启动您的项目并执行以下测试

  • 通过在文本字段中键入“red”来更改 @body-bg 变量
    • 您的页面背景应变为红色
  • 通过颜色选择器更改 @body-bg 变量
    • 您的页面背景应变为选定的颜色
  • 通过在文本字段中键入“white”来更改 @text-color 变量
    • 您的页面文本颜色应变为白色
  • 通过颜色选择器更改 @text-color 变量
    • 您的页面文本颜色应变为选定的颜色
  • 通过在文本字段中键入“red”来更改 @brand-primary 变量
    • 主按钮应变为“blue
  • 通过颜色选择器更改 @brand-primary 变量
    • 主按钮应变为选定的颜色

问题

UI 通过了测试。但我看到了一些我没打算到的情况。每次我更改一个变量时,UI 都没有保留先前设置的变量的值。换句话说,它一直在重置我所做的一切。

为什么?

但是等等,在我们深入探讨原因之前,让我们先更改一下测试计划

  • 通过在文本字段中键入“red”来更改 @body-bg 变量
    • 您的页面背景应变为红色
  • 通过颜色选择器更改 @body-bg 变量
    • 您的页面背景应变为选定的颜色
  • 通过在文本字段中键入“white”来更改 @text-color 变量
    • 您的页面文本颜色应变为白色
    • UI 应保留先前设置的 @body-bg
  • 通过颜色选择器更改 @text-color 变量
    • 您的页面文本颜色应变为选定的颜色
    • UI 应保留先前设置的 @body-bg
  • 通过在文本字段中键入“red”来更改 @brand-primary 变量
    • 主按钮应变为“blue
    • UI 应保留先前设置的 @body-bg
    • UI 应保留先前设置的 @text-color
  • 通过颜色选择器更改 @brand-primary 变量
    • 主按钮应变为选定的颜色
    • UI 应保留先前设置的 @body-bg
    • UI 应保留先前设置的 @text-color

再次执行测试,在第 1 步之后就失败了。

修复问题

问题在于 less.modifyVars 的工作方式。每次调用它时,它都会获取原始的所有变量,并应用您传入的变量对象。所以我们需要在每次调用 less.modifyVars 时都传入所有颜色。

所以让我们找到所有对 viewModel 的订阅,其中调用了 less.modifyVars 。我只展示其中一个

    viewModel["@text-color"].subscribe(function () {
        //there's your problem! you need the whole viewModel!
        less.modifyVars({
            "@text-color": viewModel["@text-color"]()
        });
    });

正如您所见,我们只发布了 @text-color ,而它需要我们所有的参数。

为了解决这个问题,我们将使用一个名为 toJS 的 knockout 功能,将我们的 viewModel 反序列化为一个普通的 JavaScript 对象,并将其传入。

   viewModel["@body-bg"].subscribe(function () {

        less.modifyVars(ko.toJS(viewModel));
    });

    viewModel["@text-color"].subscribe(function () {

        less.modifyVars(ko.toJS(viewModel));
    });

    viewModel["@brand-primary"].subscribe(function () {

        less.modifyVars(ko.toJS(viewModel));
    });

现在,如果我们执行我们最后的测试计划,它应该会通过。

优化

我还是不满意。我认为目前我定义了每个我想暴露给用户的变量是可以的,但我不想为每个变量重复这段订阅代码。

所以让我们将其更改为对 viewModel 的迭代,整个代码现在看起来像这样

/* globals ko, less */
(function () {

    var viewModel = window.viewModel = {
        "@body-bg": ko.observable('#ffffff'),
        "@text-color": ko.observable('#777777'),
        "@brand-primary": ko.observable('#337ab7')
    };

    for (var prop in viewModel) {
        if (viewModel.hasOwnProperty(prop)) {
            viewModel[prop].subscribe(function () {
                less.modifyVars(ko.toJS(viewModel));
            });
        }
    }

    $(document).ready(function () {
        ko.applyBindings(viewModel);
    });
})();

测试

  • 执行之前的测试计划
  • 从顶部导航栏选择不同的页面

问题

又一次,我之前所做的一切都被丢弃了!

这是由 .NET.MVC 的工作方式引起的,它不是一个 Ajax 站点。整个页面将从服务器重新获取。实际上将更改保存到服务器将是另一篇文章的主题。但现在,我们需要将一些内容保存到 localStorage ,并从那里重新创建我们的 viewModel

让我们使用我们学到的许多知识,为代码添加最后一个优化。

/* globals ko, less */
(function () {

    var viewModel = window.viewModel = {
        "@body-bg": ko.observable('#ffffff'),
        "@text-color": ko.observable('#777777'),
        "@brand-primary": ko.observable('#337ab7')
    },
        storedViewData =
            localStorage.getItem("viewData") !== null
            ? JSON.parse(localStorage.getItem("viewData"))
            : {};

    function onViewModelChanged() {

        var viewData = ko.toJS(viewModel);

        less.modifyVars(viewData);

        localStorage.setItem("viewData", JSON.stringify(viewData));
    };

    for (var prop in viewModel) {

        if (viewModel.hasOwnProperty(prop)) {

            viewModel[prop].subscribe(onViewModelChanged);

            if (storedViewData.hasOwnProperty(prop)) {
                viewModel[prop](storedViewData[prop]);
            };
        }
    }

    $(document).ready(function () {

        ko.applyBindings(viewModel);
    });
})();

测试

现在执行所有测试计划,它们都通过了(至少在我这里;)。

结论

我暂时就到这里。您可以添加其他要暴露的颜色变量。

生成和下载实际的 .css 文件将在另一篇文章中讨论。我可能在此之前会修复一个其他根本性的缺陷。你能发现吗?

另外,在极小的屏幕上将 UI 设置为 2 列并不是很好。所以也许它可以更具响应性。

但现在,这样就可以了。

历史

我已经在这里添加了这篇主题的下一篇文章:这里

© . All rights reserved.