簡單乾淨的C#方法設計案例:SFCUI.AjaxValue()之三

來源:互聯網
上載者:User

之一,之二,之三

 

之前講到,方法聲明為:

    @SFCUI.AjaxValue(story.ID, effortValue.ToString(), Effort.EffortPlannedValues, "/SFC/Efforts/AjaxSetEffortPlanned?itemID=" + story.ID + "&value={0}", ajaxOnSuccess: "refreshLeftPad");

調用的例子:

    @SFCUI.AjaxValue(story.ID, effortValue.ToString(), Effort.EffortPlannedValues, "/SFC/Efforts/AjaxSetEffortPlanned?itemID={0}&value={1}", ajaxOnSuccess: "refreshLeftPad");

感覺上toggle、快顯功能表、UpdateTargetID什麼的都沒有啊,怎麼能實現?

這就是設計的一種重要原則:把所有技術細節都隱藏在實現的內部。

函數內部的設計

下面的代碼僅作樣本,與我自己用的不太一樣,簡化了一下,只為說明問題。

        public static MvcHtmlString AjaxValue(int id, string value, string[] values, string urlFormat, string ajaxOnSuccess = null)        {            string ajaxUpdateTargetID = "ValueOf" + id;            string innerHtml = null;            //Build the div to show the final value.            TagBuilder theValue = new TagBuilder("div"); //這個是放置原始數值的DIV,日後更新的也是它,ID在前面產生好了。            theValue.InnerHtml = value;            ...            //Build the ajaxLink to popup the menu of values to be selected.            TagBuilder ajaxLink = new TagBuilder("div"); //這個是在Value後面放一個可以點的小圖片,點一下就會快顯功能表            ...            ajaxLink.AddCssClass("clickshow"); //這個是我自己定義的Class,說明點擊它,會彈出一樣東西,就是是$("#clickshowBodyOfAjaxLinkOf" + $(this).attr(id)).show()。            innerHtml += ajaxLink.ToString();            //Build the div to show the popup menu; and a "clickshowbody" container to hide the menu when mouse left.            TagBuilder content = new TagBuilder("div"); //這個是平時隱藏,點擊後會彈出的那個菜單。            ... //這裡隱藏的比較多,因為我調用了很多我自己的函數比如ImageLink()等,寫出來也看不到細節。            ... //大致就是:在菜單裡邊foreach(var v in  values),用id 和每個v,string.Format(urlFormat, id, v)出一個AjaxLink來。            ... //如果OnSuccess不是null,記得AjaxLink裡邊要放上OnSuccess。             ... //關鍵代碼:imglink(是個tagbuilder).MergeAttribute("onclick", "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: '" + ajaxUpdateTargetID + "', onSuccess: Function.createDelegate(this, " + ajaxOnSuccess + ") });");            innerHtml += clickshowbody.ToString();            return new MvcHtmlString(innerHtml);        }

這樣,函數裡邊就把放置Value的DIV(也就是日後會被更新的那個)、快顯功能表、快顯功能表上應該寫的東西、點擊後該幹什麼、彈出後該幹什麼等等都處理了,扔到哪裡都工作。
下面是點擊後彈出的效果:

更複雜的介面

上面的例子基本上解決了簡單的列表式快顯視窗的情況,如果彈出來的東西比較複雜怎麼辦?比如我想把一個任務分配給我負責的組或我參與的組的某個組員,而且不希望這些組員直接按姓名排列(假設我記不住這些人),希望能呈現出一個下面這樣的介面來:

圖中點一下人名,就能把這個任務分配給這個人。

這個時候千萬別先想技術實現手段,而是先要想一下:到底有幾個商務資訊要處理?其實就4個:

1. 哪個任務?任務的ID

2. 什麼資訊要改?“當前負責人”

3. 可選人員有哪些?“我的組(我負責管理的和我參與的)”

4. 設定好了要做點什嗎?“重新整理一下螢幕”(主要是為了讓更新後的內容接受一下Document.Ready裡邊的JS重新刷一下)

所以函式宣告是:

public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null)

item是那個任務

sdcTypeValue是之前定義好的自訂欄位的值(不是為了這個AjaxValue定義的,SDC=System Defined Column系統自訂欄位)

AjaxValue.ValuesKey是個enum稍微複雜一點。之前總是直接輸入string[]來代表未來的值,現在有可能是User[]……等等,所以乾脆傳遞一個enum由函數內部判斷這次是找什麼,裡邊再詳細處理。

urlFormat哪去了?由於AjaxSDCValue是我們專門用來處理系統自訂欄位的,所以urlFormat是統一的,請看函數內部:

        public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null)        {            string value = item.ReadSDCValueOf(sdcTypeValue);            string urlFormat = "/SFC/Items/AjaxSetSDCValue?<itemID={0}&sdcTypeValue=" + SDCType.USER_CURRENT_OWNER+ "&value={0}";                     return AjaxValue(item.ID, value, valuesKey, urlFormat, ajaxOnSuccess);        }

實際上完全使用原來的函數AjaxValue也能實現,但是向使用者暴露了過多的技術細節(尤其是形成urlFormat的式子太複雜),完全是使用者不應該關心的。

/SFC/items/AjaxSetSDCValue會把Item的USER_CURRENT_OWNER欄位設定為value的值。

彈出的複雜菜單哪來的?我們總結了一下,發現AjaxValue大致可能彈出以下幾種東西:

1. 最基本的一串字串(原來那個string[],可以處理可用的預估值、剩餘時間值、花費時間值……)

2. 我的組的人員(分配任務、分配Bug、安排值班、安排休假……)

3. ……

urlFormat不同點擊後的功能就不同,但上述菜單的結構和外觀每次都相同。

所以就做了一個AjaxController.PopupAjaxValuesMenu,會根據傳進來的AjaxValue.ValuesKey這個enum來判斷彈出哪個菜單(包括準備菜單中的資料),順便把urlFormat也傳進去。

回顧

設計和調試這一堆東西還是花了2天時間的,但是從此再也不用見到大約每次都要50行左右的C#/Html代碼了。

當未來的維護者看到這段代碼時:

        @SFCUI.AjaxSDCValue(Story, SDCType.USER_CURRENT_OWNER, AjaxValue.ValuesKeys.UsersMyTeams, ajaxOnSuccess: "function() { refreshStoryPad(" + Story.ID + "); }", imageUrl: "/SFC/Users/Details16.png")

他會想:“哦,這裡要把Story的CurrentOwner調整為……我的Team中的一個人,如果成功了會來重新整理這條故事。而且,前面似乎還顯示了個頭像表徵圖!”

這比面對50行漫天飛的代碼要舒服多了。

寫到最後,突然想起來10年前總結過的另一句話:

封裝的最高境界,就是用和自然語言一樣多的文字寫完調用代碼,剩下的全部隱匿起來。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.