回顧
在上一章中使用了angular實現了ajax form和樹形結構,經過以上兩章對於angular的大致使用,對於angular也有了初步的認識,接下來的內容只會對angular的一些用法做簡單的說明,如果有不清楚的可以自己查看angular API或者留言給我。
剛開始接觸angular的時候,我以為會拋棄諸如jQueryUI、easyui這樣的ui組件,但是隨着我學習后才發現,其實是我被自己的想法給誤導、局限了。mvvm通過數據與ui的綁定,實現雙向的同步,使用其他ui的組件我們一樣可以通過數據的變化來實現ui組件的狀態變化,通過ui組件的一些變化來變更綁定的數據也是行的通的。
問題
1、input類型控件
2、列表類型控件
3、C#擴展
input類型控件
由於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的值設置為默認值,代碼就不寫了。
列表類型控件
像numberbox、validatebox、datebox等input類型控件,都可以通過ngModel加上一些代碼來實現數據的雙向綁定,相對來說還是很簡單的,但是像combo、combobox、combotree等就沒辦法直接使用ngModel進行綁定了,因為這些控件會生成額外的html代碼,這是NG無法控制到的,因為這些控件的一些自身的事件機制並不能在ng內發揮作用,相反還會影響ng的正常運行,因此只能根據自身的業務來對它們進行一些擴展,這里以combobox為例子,實現思路大致如下:
1、自定義指令生成下拉單html
2、手動初始化combobox並將綁定字段的值設置到combobox上
3、當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者結合在一起的方法,因此就有了此篇文章,希望此次分享能給其他人帶來幫助,如果存在問題或者錯誤的話,請告訴我,謝謝。
