顯示具有 Html 標籤的文章。 顯示所有文章
顯示具有 Html 標籤的文章。 顯示所有文章

2015年8月14日 星期五

用filesaver.js 輕鬆將html table的資料匯出成Excel

過去有關Excel的匯出我都是用NPOI套件來撰寫,之前也有寫過類似的文章:

[鐵人30天]當ASP.NET MVC 遇見 Angular.js MVC 客戶管理系統(23) – NPOI匯出功能

但看到同事有使用filesaver.js這個套件來做匯出,個人覺得蠻方便的,就來分享一下

唯一有個限制,因為是用Html5的Blob來實現的,所以較舊的瀏覽器不支援

而有些版本只要再加載Blob.js也可正常運作

c85eee520a2e43589c3188a35b6ed0da

資訊來自 filesaver.js Github

官網已經有Demo示範匯出 png . txt . xhtml 的類型,以下就來實作實務上很常見的匯出table raw data

首頁Github整個clone下來會發現filesaver.min.js這個檔案,將這個檔案載入到你的網頁

e7a4119874a646ff9c98b135453ca671

接著我們寫個html假資料,並加個匯出按鈕,當然實際上你的資料來源可能是後端提供而來

ed9c7e3d3ce34582b205dcdc3784598e

22927ec38a9544ea871d38fcf1ce6978

接著我們用jQuery button click的時候,將tbData這個div底下的資料匯出

$(function () {
$('Button').click(function () {
var blob = new Blob([document.getElementById('tbData').innerHTML], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
});
var strFile = "Report.xls";
saveAs(blob, strFile);
return false;
});
});

這樣就可以很方便的匯出資料了!


780e1092f82642c59ffae31dead7beb5


Technorati 的標籤: ,,,

2014年10月8日 星期三

[鐵人30天]當ASP.NET MVC 遇見 Angular.js MVC 客戶管理系統(12)–繼續完善新增.修改.刪除功能-2

* 本篇目標

接續上一篇,繼續完成Angular.js的新增.編輯功能

* 上篇回顧

上篇我們依序從BLL.Web API,完成NG的Service call Web API,並在前端的Grid新增了編輯.刪除按鈕,此篇會接續來完成新增.編輯功能,以下是上一篇的最後一張圖。

56eb19ed168248f88a94ccf29bfb85c7

* 新增

因為是SPA頁面,我們會先增一個AddCustomer.html來定義template,藉由ng-model來指定我們的ViewModel欄位。這個html的內容會替換前幾篇我們所定義的ng-view div的內容。

5c5dc2e86b7d4301b5b8f1f1bbe5bd68

透過NG的Route,在最上層的app.js我們可以指定連結為/Customer/Add時指定為Contoller使用CustAddCtrl,html template使用為AddCustomer.html

036b6f056ecd4ae7aa4c86af65aa5285

畫面瀏覽:

4f8a9056dd054e2792b614e8aeaca3ca

新增一個customerAddController.js,基本上邏輯不會太複雜,故用註解來解釋

673569d9d1994e77914b31a731a6ced2

* 編輯

同樣,我們新增一個EditCustomer.html

539bc09bb3ce4091b3c3213b24568aa1

customerEditController.js,這邊又用到一個NG的新東西,因編輯我們透過QueryString來傳遞客戶的ID,$routeParams可以讓我們很方便的去取得參數

f224ffa881d843959f623796c0aaa1bf

* 後記

雖然jQuery程式很精簡好懂,NG則學習曲線較高,但jQuery跟html的耦合性太高,實務上我們常常會遇到一些鳥事,譬如設計師UI大改,客戶功能需求變更…等等,Angular.js將各"職責”分開,html跟srcipts完全不會有衝突,網頁設計師只要給他新的版,我們再套上NG所對應的ng-model即可,而需求變更我也會很清楚的知道要開哪個檔案去擴充。

我們基本上把改有的功能都帶過了,下面幾篇預計就活用前10幾篇的技術,繼續完成我們的需求!

本系列程式原始碼請至我的Github下載  https://github.com/kyleap

** 如內容有誤請告知,將進行修改,謝謝 **

2014年10月7日 星期二

[鐵人30天]當ASP.NET MVC 遇見 Angular.js MVC 客戶管理系統(10)–開始打造SPA網頁

* 本篇目標

Angular.js切割分層,並改寫成SPA網頁

* SPA

SPA ( Single Page Application),傳統一直POST,一直重新整理的網站在使用者體驗上是比較不好的設計,這也是為什麼Ajax目前已經變成是很普遍的事,而SPA顧名思義就會只有一個網頁,所有網站要做的事都利用Ajax的方式來處理,最顯見的意思就是Gmail,有沒有發現Gmail可以做很多事情,但是整個網頁都是沒有重新整理的。

延伸閱讀  SPA - Single Page Application

* 改寫Angular.js

先來看看目前已經完成的Code,我們逐步重構

489a18c3eb7b4f79aea18324c34a5f9b

1. angular.module模組每個頁面都要載入一次 -> 我們抽成一個.js檔,當成最上層

2. 與WebAPI溝通之程式也應有共用性 -> 抽成CustomerService.js

3. CustCtrl 再頁面上與html同時存在過於雜亂難維護 –> 我們抽成CustomerController

 

實務上我至少會區分以上的架構,接著來逐步改寫:

1 . 宣告一個MainAPP,並將ngRoute及ui.bootstrap模組載入,ngRoute是之後要改SPA的模組,這邊先載進來

17d4708197d2464689cc87d3ec344b7b

並且將ng-app往上層的<html>拉

674fe2335833461b8785fb33c59b6ed8

2.  我們將跟WebAPI溝通的改為一個Service

$q 參數,是一種JavaScript Promise的設計模式,Promise翻譯為承諾,我們將Web請求封裝起來,讓使用此API的程式只知道幾件事:  (1) : 成功了 ! 得到什麼資料 (2) 失敗了 ! 得到錯誤訊息 ,對於怎麼Call,使用這API的人不用太了解,這就是$q回傳的 resolve 及 reject

72fcb794f2024345a2809e4ccd941b40

3. 新增一個customerController.js 將Code複製近來,特別要注意的是,我們需載入CustService參數來使用,我們可以再.then裡面來接回Service成功回傳的值。


32818214c314493fbf20af07202b5842




接著再_Layout.cshtml載入這三個檔案


deb93e09366a4026b686e44c631370d1


這樣我們Customer Index.cshtml就會只剩下html的code了


* 改成SPA網站


但是這樣還不夠,這系列文章我們希望用SPA架構來開發。


先載入Route模組


faadb1636bcb46e8b2e385b5867b1e84


我們利用Angular.js的Route機制來修改,如當瀏覽器網址/Customer的時候,使用CustCtrl Controller,並載入List.html這個檔案


a51fddb298c94a8888e72f46f2260a1e


接著我們將原先的index.cshtml放入List.html檔案


58c6c1ff833f4589bedf738ebb896f21






而指定的List.html內容要載入到哪裡的,就是定義於ng-view


866ce3e9475447568bb05dd363b416e5


如此這樣就輕鬆完成SPA的網站了,且html與scripts與WebAPI Client邏輯都已拆開,問時就能有個明確方向。未來增加新增.修改時,也是透過NG的Route機制,讓我們將ng-view內容做個切換,不重新整理。


延伸閱讀


http://codewala.net/2014/06/16/learning-angularjs-with-asp-net-mvc-part-4/


http://www.infoq.com/cn/news/2011/09/js-promise


https://docs.angularjs.org/api/ng/service/$q


http://www.asp.net/web-api/tutorials/hands-on-labs/build-a-single-page-application-(spa)-with-aspnet-web-api-and-angularjs


 


 


本系列程式原始碼請至我的Github下載  https://github.com/kyleap

** 如內容有誤請告知,將進行修改,謝謝 **

2014年10月5日 星期日

[鐵人30天]當ASP.NET MVC 遇見 Angular.js MVC 客戶管理系統(8) - 美化網站工具介紹

* 本篇目標

利用一些 CSS Freamwrok 來快速美化我們的網站

* Bootstrap

開發Web目前Bootstrap已經是必備的技能之一了,甚至ASP.NET網站預設網站也已經全面改用Bootstrap, 如下:

5d07728c23444ba098a1aea2bdc038bb

我們用Nuget升級到目前的版本 3.2.0

139b2fbc829a47b49cedf0aeaf73b249

Bootstrap官網已經寫得很清楚,甚至只要看到順眼的樣式,只要指定個html class,就可把html copy下來,

如前兩天的範例,指定table class="table table-bordered" 就會變成很漂亮的帶框線table

dbd838df68e949c699719e0821b6ede7

f849fdcc41434921890cbf2913456177

其他像input textbox . Button …也只要套個class,就完成美化了,是不是很方便呢!

* Bootstrap主題 – Bootswatch

Bootwatch網站有提供一些免費的主題,讓我們很容易的輕易去更換網站配色,當然可以去買一些付費的template,如Wrapbootstrap,花幾百塊台幣就可以買到一個很完整的網站版型。接下來介紹一下怎麼使用Bootwatch的免費版型來更改樣網站配色。

點選bootstrap.min.css

bdd16d9182414c9cabdd0d4e7315b49d

點開後應該是一大串的css code,我們把這些貼回專案裡bootstrap.min.css

bdd16d9182414c9cabdd0d4e7315b49d

再將選單文字修改一下,這樣很輕鬆就更換配色了!

1033a6d9670648f19f5957faab6cdbc5

* Font Awesome

在以往一些圖示,我們都要設計師做一些ICON,但這個會有一些問題,如設計師死不給(有可能發生)造成時程Delay,或者是ICON大小要做修改,造成失真要重做。Font Awesome是一種以文字呈現圖形的服務,且他跟向量圖一樣是不會隨著大小而失真的,我們一樣使用html標籤 + class 就可以顯示ICON了,且他提供的圖片,我想是非常夠用的。

一樣用Nuget安裝 (有沒有覺得Nuget真的很強大...)

be8a320b748645dfa59ce4fe20424cdd

於Layout.cshtml載入.css

fcbcd99635ed484bb3cd9cfe05c3e769

接著去官網找合適的ICON

fdc0cd573189464f9900643eedb69d8a

套進class,並使用NG的ng-show來判斷是不是像WebAPI取得資料中,來顯示讀取中圖示

c3054489a4f249cdb5941ae21d643e57

完成圖

d36ee5bc7b3e444a9ad8338e4986d2d7

* 後記

現代化開發網站已經有很多工具能幫助我們快速開發了,Devopler通常對UI比較沒有概念,另一個就是某些網頁設計師對css html可能還不是那麼熟悉,用一些工具切出來的版會發生可怕(以前受過table包table網頁版的折磨),現在的話,我會用bootstrap來跟設計師討論,因為bootstrap Responsive Web Design (RWD)是做得非常好的,手機網頁看都會自動變成很好閱讀,故設計師主要會做客製化圖片的部分。接下來後面文章都會利用這些工具來製作我們的網站。

最後介紹一個工具 - placehold.it ,一個很方便的網站服務,當我們與設計師討論好某個區塊圖片向素大小為多少時,我們就可以用指定大小的方式來產生圖片,如:

http://placehold.it/350x150

http://placehold.it/50x50

這樣就可以指定於Img標籤,讓我們先有網站大概的版型樣子,不用等設計師設計出來

<img src=’http://placehold.it/350x150’/>

350x150

 

 

本系列程式原始碼請至我的Github下載  https://github.com/kyleap

** 如內容有誤請告知,將進行修改,謝謝 **

2014年1月28日 星期二

focus焦點於textbox在IE中失效之問題

這算是一個小記錄,起因是在Page Load的時候有的textbox要用JavaScript來做focus,但發現IE只要點選頁面其他元素時,在按F5(重新整理),則focus就會失效:

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js "></script>
</head>
<body>
<input id="txtName" type="text" />
</body>
<script type="text/javascript">
$('#txtName').focus();
</script>
</html>


以上程式在Firefox和Chome都沒問題,但在IE Reflash就會失效,看起來好像IE載入元素、JavaScirpt的順序跟其他瀏覽器不太一樣(待確認),導致focus()事件在html document完成之前就先載入了,所以發現主控台會有以下錯誤


image


最後解決的方式是加個timeout,延遲執行focus

<script type="text/javascript">
setTimeout(function () {
$('#txtName').focus();
}, 500);
</script>

2014年1月25日 星期六

Html5 Storage使用心得與應用

前言

大概半年前,開始接觸到Html5相關技術,但其實沒有太深入在實務上使用過,因為html5常常需要新的瀏覽器支援,故舊版IE支援度就差,瀏覽器的支援度可以到html5 Test網站去測試,而如果用IE8執行,除了看到46分被當掉的分數外,也許你會好奇這46分是哪來的,那時就發現了Storage這個東西,並一直找機會想在實務上用看看,故此篇主要會將一些實務範例做分享。

image
(圖為IE8 html的考試卷)



Html5 Storage的基本介紹

我試著用最簡化的方式來介紹html Storage,當然你想更了解html的機制,可以再延伸去Google或者去參考下面的延伸閱讀。
以往我們要暫存資料可能會選擇Session或者是Cookie,而這兩者其實都跟伺服器端有關聯,Storage是一個純客戶端的新機制,他能儲存的資料大小比Cookie還要大(試瀏覽器而定,Cookie大約4KB,而html5 Storage能到5MB)
Storage又分為兩種:Session Storage和Local Storage,主要差別在生命週期的不同
  • Session Storage : 只要該頁面關閉,則Session Storage的生命週期即結束
  • Local Storage : 相反的,Local Storage在使用者清除瀏覽器資料生命週期才會消失

 

一些範例與應用

以下就介紹我的實務上的應用,也許不是使用的很恰當,或者是有更好的解法,但這篇的目的是讓大家在想Solutions時能想到有html5 Storage能應用。

紀錄div顯示隱藏的狀態

有時表單程式在使用者點選上一頁後,會因為瀏覽器的特性將某些欄位回到初始值狀態,例如下面的例子,我們在下拉式選單選擇其他,會顯示出一個textarea讓他輸入,這時我們就可以寫入Storage來儲存這個div目前應該是隱藏還是顯示。
http://jsfiddle.net/kyleshen/H2pse/
$('#ddlSkills').change(function(){
    if($(this).val() == 'Others') {
        sessionStorage.setItem('ShowOthers', "Y");
        $('#divOthers').show();
    }
    else {
        sessionStorage.setItem('ShowOthers', "N");
        $('#divOthers').hide();
    }
});

$(function(){ 
    //如果SessionStorage為Y時,秀出textarea
    if (sessionStorage.getItem("ShowOthers") == "Y") {
        $('#divOthers').show();
    }
});

會這樣做的原因是之前公司的複雜表單,會針對不同的條件狀態下,顯示不同的區塊及帶入不同的下拉選項或Radio選項,在Submit過後,會顯示出再一次確認表單資訊的訊息,如發現有錯則點選上一頁,這時要保存他所輸入的資訊不讓他重新輸入,那時候就用Session Storage來解決了 !

將Json資料儲存於Session Storage


現在前端技術越來越火紅,有時我們常常會使用一些前端的Gird框架來實作,如JQueryUI、EasyUI、KendoUI…等等,而這些框架多半都使用JSON來接收及傳遞資料。

假設如果Grid我們要獲取目前所勾選的Row資料,這些框架也能很輕鬆的獲取這些項目的JSON string,但如果要將這些項目傳遞到另一頁做處理呢 ? 你也許可以用QueryString將JSON字串當參數傳遞過去,但QueryString是有長度限制的,或者你可以將這些Row的PK,用QueryString傳遞,到該頁時再去DataBase撈取資料也是一個方法,但這樣又多一個Connection。故之前我是將這串JSON String存到Session Storage,即使在操作上換頁也可將這筆資料擷取出來繼續使用。

http://jsfiddle.net/kyleshen/72Qkm/
$(function(){ 
    //新增資料到陣列
    var Skills = new Array();
    Skills.push('C#');
    Skills.push('ASP.NET');
    Skills.push('JQuery');
    
    var Json = JSON.stringify(Skills);
    sessionStorage.setItem('JsonStr', Json);
    $('#DivJson').html(Json); //秀出Json字串的Div
    
});

後記


基本上有了這個儲存機制,可以解決的事情還蠻多的,這邊只是簡單舉例一些小小的應用方式,特別要注意的是,儲存的資料勢必要考慮是不是敏感性資料,畢竟是儲存於客戶端,其實用瀏覽器工具就能看到值並且做竄改,以Chome為例,就是案F12,使用開發者工具視窗,到Resources就能看到key跟Value,點兩下就能改變他的值。Storage已經被很多網站所使用,下次逛網站時,不如打開開發者工具看看那些大網站怎麼應用,並儲存那些資訊吧!

image

延伸閱讀

2013年12月27日 星期五

KendoUI & ASP.NET MVC系列文章 - 處理CRUD-繼續完成新增、修改、刪除

前言

前一篇我們已經完成利用Ajax來讀取後端資料顯示於Grid,而此篇就會將新增、修改、刪除功能接續完成

CRUD的 Create - 新增

Controller

利用原本樣板產生的程式做一些修改,原本程式如下

image

將上面程式修改為以下,透過MVC的Model Bindder可以接收前端傳來的Customers物件,並依情況回傳是否新增成功

image

View

將dataSrc新增一個Create function,指向後端的CreateCustomer,並在完成後alert處理結果

 image

Schema部分新增model定義,讓KendoUI知道你的模組結構,並且可設定一些欄位驗證

image

接著在屬性部分新增toolbar及editable,editable模式可分為inline(直接在Gird新增一個)或者是popup(彈跳視窗)方式,此例我們希望新增時是跳出一個燈箱視窗

image

結果圖

這樣就完成,在以往我們可能自己要實做View的頁面,現在KendoUI都幫你做好好的,且表單也排得很整理 lol

image


CRUD的Update-編輯

Controller

基本上跟新增大同小異,我們在Controller新增EditCustomer的JsonResult

image 

View

新增一個Update function

image

在columns多一個command成員,並設定edit按鈕(這邊也可以自訂義Button標籤)

image

這樣就完成編輯燈箱視窗了!!

image

CRUD的Delete-刪除

Contoller (這邊就不解釋了)

image

View

image

image

後記

以上是一種實做CRUD的方式,但其實做法有很多種,譬如你也可自訂新增、編輯的View去實做,或者你也可以自訂一些查詢欄位POST到後端去,關於版面,如果要修改中文的顯示其實也可以做到,之後也會分享一些實做心得PO上來。而下一篇,預計完將後端改成用WebAPI + OData的方式實做 =V=

完成程式碼

Contoller

public class GridAjaxController : Controller
{
private NorthwindEntities db = new NorthwindEntities();

// GET: /GridAjax/
public ActionResult Index()
{
return View();
}

public JsonResult GetCustomer(KendoGridRequest request)
{
try
{
var result = db.Customers.ToList();
return Json(new KendoGrid<Customers>(request, result), JsonRequestBehavior.AllowGet);
}
catch (Exception)
{
Dictionary<string, object> error = new Dictionary<string, object>();
error.Add("ErrorCode", -1);
error.Add("ErrorMessage", "讀取資料失敗!");
return Json(error);
}
}

// GET: /GridAjax/Details/5
public ActionResult Details(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Customers customers = db.Customers.Find(id);
if (customers == null)
{
return HttpNotFound();
}
return View(customers);
}

// GET: /GridAjax/Create
public ActionResult Create()
{
return View();
}

[AcceptVerbs(HttpVerbs.Post)]
public JsonResult CreateCustomer(Customers customers)
{
if (ModelState.IsValid)
{
if (TryUpdateModel(customers))
{
db.Customers.Add(customers);
db.SaveChanges();
return Json("新增成功!!");
}
}
return Json("新增失敗!!");
}

public JsonResult EditCustomer(Customers customers)
{
if (ModelState.IsValid)
{
db.Entry(customers).State = EntityState.Modified;
if (TryUpdateModel(customers))
{
db.SaveChanges();
return Json("更新成功!!");
}
}
return Json("更新失敗!!");
}

[AcceptVerbs(HttpVerbs.Post)]
public JsonResult DeleteCustomer(string CustomerID)
{
Customers customers = db.Customers.Find(CustomerID);
db.Customers.Remove(customers);
if (TryUpdateModel(customers))
{
db.SaveChanges();
return Json("刪除成功!!");
}
return Json("刪除失敗!!");
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}

View

@model IEnumerable<KendoUI.Model.Customers>

@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/BaseLayoutPage.cshtml";
}

@section scripts
{
<script type="text/javascript">
$(function () {

//資料來源物件改用Ajax
var dataSrc = new kendo.data.DataSource({
transport: {
read: {
type: "POST",
url: "/GridAjax/GetCustomer",
dataType: "json",
complete: function (response, status) {
if (response.responseJSON.ErrorCode == -1) {
alert(response.responseJSON.ErrorMessage);
}
}

},
update: {
url: "/GridAjax/EditCustomer",
dataType: "json",
type: "POST",
complete: function (response, status) {
alert(response.responseJSON);
}
},
create: {
url: "/GridAjax/CreateCustomer",
dataType: "json",
type: "POST",
complete: function (response, status) {
alert(response.responseJSON);
}
},
destroy: {
url: "/GridAjax/DeleteCustomer",
dataType: "json",
type: "POST",
complete: function (response, status) {
alert(response.responseJSON);
}
},
},
schema: {
data: function (d) {
return d.data;
},
total: function (d) { return d.total; },
model: {
id: "CustomerID",
fields: {
product: { editable: false, nullable: false },
CompanyName: { validation: { required: true } },
ContactName: { validation: { required: true } },
City: { validation: { required: true } }
}
}
},
pageSize: 5,
serverPaging: true,
serverSorting: true
});

$("#gridData").kendoGrid({
dataSource: dataSrc,
columns: [
{ field: "CustomerID", title: "CustomerID" },
{ field: "CompanyName", title: "CompanyName" },
{ field: "ContactName", title: "ContactName" },
{ field: "City", title: "City" },
{ command: ["edit","destroy"], title: "&nbsp;", width: "160px" }
], //columns的field對應到customer資料表的欄位
sortable: true,
pageable: true,
filterable: true,
width: 800,
toolbar: ["create"],
editable: {
mode: "popup", //使用Popup的模式編輯資料
update: true, // 若設定為True,當按下Update才server資料
}
});
});
</script>
}
<div id="gridData"></div>


延伸閱讀