angularjs + seajs構建Web Form前端(三) -- 兼容easyui

回顧css

  在上一章中使用了angular實現了ajax form和樹形結構,通過以上兩章對於angular的大體使用,對於angular也有了初步的認識,接下來的內容只會對angular的一些用法作簡單的說明,若是有不清楚的能夠本身查看angular API或者留言給我。
html

  剛開始接觸angular的時候,我覺得會拋棄諸如jQueryUI、easyui這樣的ui組件,可是隨着我學習後才發現,實際上是我被本身的想法給誤導、侷限了。mvvm經過數據與ui的綁定,實現雙向的同步,使用其餘ui的組件咱們同樣能夠經過數據的變化來實現ui組件的狀態變化,經過ui組件的一些變化來變動綁定的數據也是行的通的。ajax

問題bootstrap

  一、input類型控件app

  二、列表類型控件mvvm

  三、C#擴展學習

input類型控件ui

  因爲easyui大部分的控件均可以基於input,如validatebox、datebox、numberbox等,試着使用ngModel直接在這些控件上進行綁定,代碼以下:編碼

//html
<div id="main" ng-controller="MainController">
    名字:<input type="text" ng-model="editData.name" />
    <br />
    年齡:<input name="age" class="easyui-numberbox" ng-model="editData.age" />
    <br />
    <a href="#" class="easyui-linkbutton" ng-click="save()">保存</a>
</div>

//js
angular.module('test', []).controller('MainController', function ($scope) {
    var age = $('[numberboxname=age]');

    $scope.editData = {};

    $scope.save = function () {
        console.log($scope.editData);
        $scope.editData = {};
    };
});

  當點擊保存的時候,發現代碼是運行正常的,能夠在控制檯內看到名字和年齡的值,可是這裏有一個BUG,那就是當保存數據之後,要再次輸入年齡的時候,年齡仍是顯示上次輸入的值,所以須要在重置editData的時候將numberbox的值設置爲默認值,代碼就不寫了。spa

列表類型控件

  像numberbox、validatebox、datebox等input類型控件,均可以經過ngModel加上一些代碼來實現數據的雙向綁定,相對來講仍是很簡單的,可是像combo、combobox、combotree等就沒辦法直接使用ngModel進行綁定了,由於這些控件會生成額外的html代碼,這是NG沒法控制到的,由於這些控件的一些自身的事件機制並不能在ng內發揮做用,相反還會影響ng的正常運行,所以只能根據自身的業務來對它們進行一些擴展,這裏以combobox爲例子,實現思路大體以下:

  一、自定義指令生成下拉單html

  二、手動初始化combobox並將綁定字段的值設置到combobox上

  三、當combobox選擇值的時候將值更新到綁定的字段上

  根據以上思路,實現代碼以下:

//其餘的省略
.directive('esCombobox', function () {
    return {
        replace: true,
        restrict: 'E',
        template: '<select></select>',
        scope: {
            data: '=',
            value: '@'
        },
        link: function (scope, element, attrs) {
            var props = scope.value.split('.');
            var current = getBinder();

            element.combobox({
                data: scope.data,
                onSelect: function (r) {
                    var binder = getBinder();
                    binder.obj[binder.field] = r.value;
                }
            }).combobox('setValue', current.obj[current.field]);

            function getBinder() {
                return props.length == 1 ? {
                    obj: scope.$parent,
                    field: props[0]
                } : {
                    obj: scope.$parent[props[0]],
                    field: props[1]
                };
            };
        }
    };
});

  以上方法之因此要將getBinder獨立出來,是由於每次操做的綁定對象都是不一樣的(editData在保存以後會被從新賦值,引用的對象不一樣了)。

C#擴展

  有時候當咱們使用服務端腳原本生成html的時候,咱們可能會使用以下代碼來進行綁定:

<input type="text" ng-model="editData.name"  ng-init="editData.name='<%=Model.Name %>'"/>

  以上html代碼能夠發現,editData.name是綁定的字段,而ngInit內初始化賦值能夠直接從Model中獲取,咱們能夠建立一個NgModelAttribute的特性,而後該特性內提供一個屬性用來存儲綁定的字段,大體代碼以下:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class NgModelAttribute : Attribute, ITagBuilderWrapper
{
    private string m_BindName;
    public string BindName { get { return m_BindName; } }

    public NgModelAttribute(string bindName)
    {
        m_BindName = bindName;
    }
}

  而後使用服務端腳本生成Html的時候,獲取表達式指定的屬性包含的NgModelAttribute特性以及Model的值來生成以上的html,大體代碼以下:

public static string Control(string tag, Expression<Func<TModel, object>> exp, TModel model)
{
    var builder = new TagBuilder(tag);
    string propertyName = PropertyHelper.ResolveName(exp);
    var property = model.GetType().GetProperty(propertyName);
    var attrs = property.GetCustomAttributes(typeof(NgModelAttribute), false);
    if (attrs.Length > 0)
    {
        var ngModel = attrs[0] as NgModelAttribute;
        builder.AddAttribute("ng-model", ngModel.BindName);
        if (model != null)
        {
            var propertyValue = property.GetValue(model, null);
            string initValue;
            if (property.PropertyType == typeof(string) || property.PropertyType == typeof(Guid))
            {
                initValue = string.Format("'{0}'", propertyValue.ToString());
            }
            else if (property.PropertyType == typeof(DateTime))
            {
                try
                {
                    var date = Convert.ToDateTime(propertyValue);
                    initValue = string.Format(
                        "new Date(1970, 0, 1, 0, 0, {0})",
                        Convert.ToInt32((date - new DateTime(1970, 1, 1)).TotalSeconds));
                }
                catch
                {
                    initValue = "new Date()";
                }
            }
            else
            {
                initValue = propertyValue.ToString();
            }
            builder.AddAttribute("ng-init", string.Format("{0}={1}", ngModel.BindName, initValue));
        }
    }
    return builder.ToString();
}

  以上代碼僅做爲參考,其中大部分的代碼主要是用於判斷綁定ngInit的值,由於不一樣值綁定的方式不一樣。那麼接下來只要將最初的代碼改成以下:

//html
<%=HtmlHelper.Control<Person>("input", p => p.Name, Person)

//ViewModel
public class Person
{
    [NgModel("editData.name")]
    public string Name { get; set; }
}

  生成的結果跟原來的是同樣的,這樣就完成了對NG的擴展了。

結尾

  因爲這次是在學習angular的過程中,遇到了控件組的問題,因爲本人對於css實在是很爛,沒法實現漂亮的組件因而我就放棄了,便有了想要繼續使用easyui的念頭,可是引入easyui後發現它與bootstrap存在着樣式兼容性問題,所以移除了bootstrap,可是編碼當中發現了2個庫之間的各類原理上的衝突,通過幾回失敗以後,終於找到了能夠將2者結合在一塊兒的方法,所以就有了此篇文章,但願這次分享能給其餘人帶來幫助,若是存在問題或者錯誤的話,請告訴我,謝謝。

相關文章
相關標籤/搜索