2017年12月20日 星期三

Kendo ui Combobox 在 blur 時觸發change 事件 | Kendo ui Combobox blur trigger change event

同事遇到的BUG,
在Kendo ui Combobox選完值,blur時會觸發change事件,
經過實驗(還有我一直google)同時發現,
如果在text field使用number類型欄位,就會這樣,
而且官方好像沒有要改的意思

ref: https://github.com/telerik/kendo-angular/issues/1137

2017年12月19日 星期二

Telerik Report Nullable Date Parameter



背景

有個報表,搜尋條件是兩種日期選一種,日期欄位就是區間開始與區間結束,
在報表檔案設定四個日期變數:A起始、A結束、B起始、B結束,都可以為空值,
然後還要做民國年轉換,
結果在表頭顯示輸入變數的地方就調了很久,終於成功...

解法


原來要做以下處理:

  1. 判斷變數是否為null
Parameters.P_InitDateStart is null



  1. 調整UTC時區問題,沒有自動+8,要手動+8
AddHours(Parameters.P_InitDateStart,8)



  1. 加上小時前,還要做IsNull判斷讓他一定要回傳一個非空的日期值
AddHours(IsNull(Parameters.P_InitDateStart, Date(1,1,1)),8)



  1. 把年份減去1911
(AddHours(IsNull(Parameters.P_InitDateStart, Date(1,1,1)),8).Year-1911)



5.最後拼起來這些處理就變成這樣:

= IIF(Parameters.P_InitDateStart is null,
(AddHours(IsNull(Parameters.P_ArriveDateStart, Date(1,1,1)),8).Year-1911)+"/"
+AddHours(IsNull(Parameters.P_ArriveDateStart, Date(1,1,1)),8).Month+"/"
+AddHours(IsNull(Parameters.P_ArriveDateStart, Date(1,1,1)),8).Day,
(AddHours(IsNull(Parameters.P_InitDateStart, Date(1,1,1)),8).Year-1911)+"/"
+AddHours(IsNull(Parameters.P_InitDateStart, Date(1,1,1)),8).Month+"/"
+AddHours(IsNull(Parameters.P_InitDateStart, Date(1,1,1)),8).Day)



 暈...

2017年12月8日 星期五

Kendo UI Date Picker 民國年格式 | Kendo UI Date Picker Taiwan calander format



背景

一個Grid,在編輯時用popup,內容是template,
有個日期欄位,
使用者想要輸入1060101或者10601011735這種日期或日期時間格式
不想要選取也不想要打「-」、空白或「:」

研究了一下也提問在官方討論區了,官方是說不支援民國年這種格式,
實際上你輸入1900年之前的日期,也會無法set,
所以也不能用民國106年直接套上DatePicker/DateTimePicker,而且萬年曆也會不對,

這裡就用textbox自幹一個字串轉日期的方法
(如果想要讓後端收到的值仍然有「-」、空白或「:」,是可以搭配mask)

首先在原本的template的時間輸入框前面加入一個input textbox

<input class="k-textbox " data-bind="value:myDateTW" id="myDateTW" name="myDateTW" placeholder="民國年yyyMMdd" style="width: 200px;" tabindex="110">
<span class="k-invalid-msg" data-for="myDateTW"></span>
<input data-bind="value:myDate" id="myDate" name="myDate" style="display: none; width: 250px;" tabindex="110">
<span class="k-invalid-msg" data-for="myDate"></span>


然後準備好字串轉日期、日期轉字串的處理,
這裡列的方法是我很直觀的去寫,沒有做什麼效能分析

//傳入值與格式,分析與轉換成JS日期物件
function parseTWDate(val, format) {
  var result = { result: true, msg: "", DateObj: null };

  if (typeof val === "undefined") {
    result.result = false;
    return result;
  }

  //只接受數字與字串型態
  if (typeof val !== "number" && typeof val !== "string") {
    result.result = false;
    return result;
  }
  //檢查格式字串
  if (
    format !== "yyyMMdd" &&
    format !== "yyyMMddHHmm" &&
    format !== "yyyMMddHHmmss"
  ) {
    result.result = false;
    result.msg = "不支援的日期格式設定";
    return result;
  }
  //只接受數字不接受符號
  if (/^\d+$/.test(val) === false) {
    result.result = false;
    return result;
  }
  var str = "";

  //數字型態轉文字
  if (typeof val === "number") {
    str = val.toString();
  } else {
    str = val;
  }

  //從後往前兩位兩位取出字串,轉數字後做驗證
  var sec = 0;
  var min = 0;
  var hr = 0;
  var day = 0;
  var mon = 0;
  var year = 0;
  var movingCount = 0;
  if (format === "yyyMMddHHmmss") {
    if (str.length < 11) {
      result.result = false;
      return result;
    }
    sec = parseInt(
      str.substring(str.length - movingCount - 2, str.length - movingCount),
      10
    );
    if (sec === NaN || sec > 59 || sec < 0) {
      result.result = false;
      return result;
    }
    movingCount += 2;
  }
  if (format === "yyyMMddHHmm" || format === "yyyMMddHHmmss") {
    if (str.length < 9) {
      result.result = false;
      return result;
    }
    min = parseInt(
      str.substring(str.length - movingCount - 2, str.length - movingCount),
      10
    );
    if (min === NaN || min > 59 || min < 0) {
      result.result = false;
      return result;
    }
    movingCount += 2;

    hr = parseInt(
      str.substring(str.length - movingCount - 2, str.length - movingCount),
      10
    );
    if (hr === NaN || hr > 23 || hr < 0) {
      result.result = false;
      return result;
    }
    movingCount += 2;
  }
  if (str.length < 5) {
    result.result = false;
    return result;
  }
  day = parseInt(
    str.substring(str.length - movingCount - 2, str.length - movingCount),
    10
  );
  if (day === NaN || day > 31 || day < 1) {
    result.result = false;
    return result;
  }
  movingCount += 2;

  mon = parseInt(
    str.substring(str.length - movingCount - 2, str.length - movingCount),
    10
  );
  if (mon === NaN || mon > 12 || mon < 1) {
    result.result = false;
    return result;
  }
  movingCount += 2;

  year = parseInt(str.substring(0, str.length - movingCount), 10);
  if (year === NaN || year < 1) {
    result.result = false;
    return result;
  }
  //JS的日期的月份是0 base
  result.DateObj = new Date(year + 1911, mon - 1, day, hr, min, sec);

  return result;
}
//將日期選擇轉成民國年
function getTWDate(val, format) {
  var rawYear = val.getFullYear();
  var twYear = rawYear - 1911;
  var rawMonth = val.getMonth() + 1;
  var twMonth = rawMonth.toString().padStart(2, "0");
  var rawDay = val.getDate();
  var twDay = rawDay.toString().padStart(2, "0");
  var strTWDate = twYear + twMonth + twDay;
  if (format === "yyyMMddHHmm" || format === "yyyMMddHHmmss") {
    var hr = val
      .getHours()
      .toString()
      .padStart(2, "0");
    var min = val
      .getMinutes()
      .toString()
      .padStart(2, "0");
    strTWDate = strTWDate + hr + min;
    if (format === "yyyMMddHHmmss") {
      var sec = val
        .getSeconds()
        .toString()
        .padStart(2, "0");
      strTWDate = strTWDate + sec;
    }
  }
  return strTWDate;
}


然後在原本的datasource的schema中加入一個自訂的民國年欄位,類型是字串
這個欄位不會回傳值給API,只是用來data bind,
裡面做自定義驗證方法,在民國年字串輸入框跳開時,去跑日期解析,
成功就把日期物件帶回model,失敗就給予錯誤訊息並回傳false;
原本的欄位也給他一個驗證是如果跑到了驗證就檢查他的值是否可以解析成日期,
這裡弄了很久是不知如何避免偶爾會出現原欄位跳出必填的問題,
還有編輯時無法透過model.set讓他自己data bind去改變前端DOM的值,
set下去model的值竟然不會改,只能手動去塞值&改DOM,
改值的時候也要先改DOM在做SET,不然也會有怪現象

var dataSource = {
  schema: {
    model: {
      fields: {
        myDate: {
          type: "date",
          validation: {
            required: true,
            dateFormat: function(input, option) {
              if (input.is("[name='myDate']")) {
                if (Date.parse(input.val()) !== NaN) {
                  $("#myDateTW").removeAttr("data-TWdate-msg");
                  $("#myDate").removeAttr("data-required-msg");
                  return true;
                } else {
                  $("#myDateTW").attr(
                    "data-TWdate-msg",
                    "格式民國年yyyMMdd"
                  );
                  return false;
                }
              } else {
                return true;
              }
            }
          }
        },
        myDateTW: {
          type: "string",
          validation: {
            required: true,
            TWdate: function(input, option) {
              if (input.is("[name='myDateTW']")) {
                var val = input.val();
                var format = "yyyMMdd";
                var result = parseTWDate(val, format);
                if (result.result) {
                  input.removeAttr("data-TWdate-msg");
                  //帶回model
                  var source = $("#myDateTW")[0].kendoBindingTarget
                    .source;
                  $("#myDate").val(result.DateObj);
                  source.set("myDate", result.DateObj);
                  return true;
                } else {
                  input.attr("data-TWdate-msg", "格式民國年" + format);
                  //帶回model
                  var source = $("#myDateTW")[0].kendoBindingTarget
                    .source;
                  $("#myDate").val(val);
                  source.set("myDate", val);
                  return false;
                }
              } else {
                return true;
              }
            }
          },
        }
      }
    }
  },
};
};



再來是grid新增或編輯時需要把model的date欄位清空與轉換

edit: function(e) {
  var arg = e;
  if (e.model.isNew()) {
    //日期型態先清空
    e.model.set("myDate", null);
  } else {
    //民國年要先轉好
    var myDate = e.model.get("ㄗㄩDate");
    var strMyDateTW = getTWDate(myDate, "yyyMMdd");
    e.model.myDateTW = strMyDateTW;
    e.model.set("myDateTW", strMyDateTW);
    $("#myDateTW").val(strMyDateTW);
  }
}



這樣就可以輸入民國年日期轉成西元年日期,編輯時也可以把西元年日期轉回民國年日期了

2017年12月4日 星期一

Kendo UI Grid + datasource 錯誤處理 | Kendo UI Grid datasource error handle



(我的Kendo版本是2017.3.913)

最常用的CRUD應用元件,KendoUI Grid + Datasource transport,
可以簡單串起Restful API,
但錯誤處理上有一些地方要注意

使用transport時API如果不拋出500或者404之類的錯誤,
而是用處理完成,但處理結果有誤,錯誤訊息放在一個字串屬性裡面的這種模式,
datasource無法去抓出那個物件秀出來,
這時就要用因為有錯誤所以要拋exception的概念,
讓API拋出HttpStatusCode.InternalServerError,
這樣前端ajax呼叫時就會抓到錯誤,
然後丟給datasource裡的error屬性事件處理,
就可以用e.xhr.responseJSON.Message抓出錯誤內容,
但也要加上undefined偵測,不是只有API邏輯錯的時候才會丟錯
所以error物件裡面也不是常常都有xhr


如果是自己寫的介面,例如另外寫kendo window,
並且自訂按鈕送出ajax,你就可以控制ajax的邏輯,
要用「有錯就是要拋錯」或者「你成功執行那就給我200,商業邏輯錯誤再另外包」
哪一種抓錯方式都可以處理了。

另外如果新增刪除修改的API用void類型,datasource的transport會拋一個JSON parsererror的錯,但若是給他回傳值,則不會有這個錯
我用的回傳物件格式:

public class OperationOutModel
{
    ///<summary>執行結果</summary>
    public bool result { get; set; }
    ///<summary>錯誤訊息</summary>
    public string msg { get; set; }
}



2017年11月30日 星期四

Postman 用Windows AD登入驗證 呼叫API | Postman call api with Windows AD authorization

在Authorization分頁
TYPE選擇NTLM Authentication[Beta]
帳密就打AD帳密
Domain有的話也要輸入
就好了

2017年11月23日 星期四

Kendo UI Grid 讀取array | Kendo UI Grid load array



情境

因為一些架構問題,不想要直接從URL讀取資料,而先做一些處理之後,再把陣列傳入GRID

作法

1.先在全域宣告datasource:

var datasource = new kendo.data.DataSource({...});<br>



2.在grid 初始的時候指定datasource為全域變數的那一個

$("#grid").kendoGrid({
//...
    dataSource: datasource,
//...
});



3.準備好陣列後用datasource的data餵入

$("#grid").data("kendoGrid").dataSource.data(array);

2017年11月16日 星期四

IIS 上用到oracle的web api出現 定義了重複的 'oracle.manageddataaccess.client' 區段 錯誤 | There is a duplicate 'oracle.manageddataaccess.client' section defined

API網站架上去後出現
定義了重複的 'oracle.manageddataaccess.client' 區段

根據黑暗執行序大的文章
http://blog.darkthread.net/post-2016-09-10-managed-odp-net-configsection-issue.aspx
了解原來預設是啟用32位元模式,且一查系統的machine.config,的確已定義過oracle.manageddataaccess.client,
然後64位元的machine.config也的確沒有定義,

但該伺服有別的站台在跑,不想動到系統的machine.config
可是又不想砍掉專案的config並改用環境變數,因為需要裡面一段custom colum type mapping設定,

所以反過來,把集區中啟用32位元模式取消,讓站台用64位元模式執行就OK了

2017年10月13日 星期五

開發 Kendo UI for JQuery 時遇到的問題


最近公司導入這個框架,盡量要用它來做功能,
第一眼看上去覺得不錯,簡單功能做都很快,
不過要加上細節時問題就出來了,用一篇文章來記錄。
版本:2017 R3

CRUD資料來源 datasource使用transport方法時,不可混用function/object,要一致


出現的bug現象為:
在local做的資料處理,刪除/更新之後loading動畫不會解除
ref:官網文件 https://docs.telerik.com/kendo-ui/framework/datasource/crud

前端驗證validation自訂驗證方法bug

前端驗證validation,寫在datasource的schema裡每個欄位field下,除了預設的方法如required外,自訂的方法會在每個欄位做驗證時被觸發,所以要加上

if (input.is("[name='該欄的名稱']")){
//驗證邏輯....
}


來確保進來的是該欄而不是其他欄,但這樣就變成每個欄位可能都要自己寫一個funciton,就會變得很亂。
而且若是多個欄位有相同的驗證類別,也不太能寫共用方法,
例如有三個自訂input格式的金額欄位,驗證規則都是數字且3位數內,
自訂是因為不要預設的小數點與上下箭頭,所以要用自訂,
但因為這個自訂驗證方法最後需要手動塞入錯誤訊息:

&nbsp;$(input).attr("data-自訂驗證名稱-msg", "最多10位數!");<br>


以這個例子,這三個欄位都得自己寫一個自訂驗證方法,把中間的邏輯抽出,
然後外面把function name傳入,如下:

//schema.model.fields<
fields: {
    Article: {
        type: "number",
        validation: {
            required: { message: "必填!" },
            ArticleMaxlength: function (input) {
                return vldRuleMaxLength(input,"ArticleMaxlength");
            }
        }
    },

//另外寫的方法
function vldRuleMaxLength(input, vldFunctionName) {
    if ((input.is("[name='Article']") || input.is("[name='Section']") || input.is("[name='Item']"))
        && input.val().length > 3) {
        $(input).attr("data-" + vldFunctionName + "-msg", "最多3位數!");
        return false;
    }
    return true;
}



ref:stackoverflow https://stackoverflow.com/questions/24671452/kendo-grid-column-field-validation

前端驗證validation,若在自訂editor的欄位使用required,需要在自訂editor的方法裡面把required加入元素TAG內,只用預設屬性沒有作用

ref:官網討論區 http://www.telerik.com/forums/validator-not-firing-for-custom-popup-editor

前端驗證的BUG

在grid popout editor做新增編輯時的驗證,先輸入符合驗證條件的值,再改成錯誤的值來觸發第一次錯誤訊息後,
再改回之前的值,會發現驗證方法不會被觸發,
目前的處理方法只想到按下存檔前再手動處理一次驗證,只是UX很怪
ref:http://www.telerik.com/forums/validation-in-grid's-schema-only-fire-once

前端驗證用remote類型(做AJAX)的處理

只能做同步ajax,不能做非同步,不然出錯時無法return false給validator,錯誤訊息就秀不出來,
目前也沒支援非同步

Grid中使用template做自訂的新增編輯窗版型很麻煩

在grid做popup類型的編輯新增layout時,若不想用預設的一欄一行排版,就要用獨立區塊的template來套在editor上,
ref:官網文件-editable.template那段 https://docs.telerik.com/kendo-ui/api/javascript/ui/grid#editable.template-string
但是官網很難找到你應該要用什麼class來做這些排版,雖然說有引用bootstrap css,
可是套用bootstrap的grid system類css上去,也跟原來單純用BS有差別,都要做調整,
這些k-xxx-class也沒有看到有像BS一樣有個官網區域做介紹,目前還是得用google找討論,然後挖出討論串中的code裡可用的部分拿來用,

grid的source用Kendo MVC的類別DataSourceRequest取進來值要用post

get抓不到東西會null

kendoWindow在close之後需要重bind,就要在失效(deactivate)事件做銷毀

ref: https://www.telerik.com/forums/destroy-window-on-close
情境:按下GRID上tool bar某custom按鈕後用$("

")的方式建立window,
以mvvm方式去kendo bind內容,key欄位有綁blur事件讓他做ajax帶值,
第一次可以work,但在按下右上角x關閉後再次打開就沒有blur事件了,
解決方式是在kendoWindow({})設定中加入deactivate事件處理,
把這個window給destory掉就可以了

Telerik Report的HTML5的report viewer串date picker的時區問題

kendo ui的datepicker接上REPORT VIEWER有個時區的問題,
在GRID系統裡抓API或送API的時間資料都是正常的,
但送到report viewer的API,會沒有處理時區,造成送去後端時變成GMT+0的時間,
C#後端可以用toLocalTime來轉回,
但報表上的變數沒辦法用toLocalTime,
暫時先在report design裡的變數expression裡面用AddHour(value,hour) +8來處理。

Kendo UI datasource的date類型資料再轉回asp.net web api2時要用符合格式的日期字串轉回

情境:一個grid配上獨立的datasource操作,不是用grid裡面的datasource給值,也沒有auto bind,
schema裡面有個欄位是date型別,USER不會去編輯他;
在call API後,此欄的值還是WEB API吐出的日期字串格式,yyyy-MM-ddThh24:mm:ss
但在編輯別的欄位後,就因為datasource的schema是date,而轉型為日期物件,
可是在做更新ajax時,並不能直接把日期物件放在data:

$.ajax({
            url: "your api url",
            type: "POST",
            dataType: "json",
            data: {date=new Date()},
            cache: false,
            async: true,
        });


後端API會抓不到,所以還是要把日期轉成日期字串,
轉日期字串的方法根據這篇建議是用 .toISOString() 來轉

2017年9月20日 星期三

SQLServer 抓表名與表註解 | SQLServer select table name and table comments



SELECT 
    c.name '欄位名稱',
    ep.value AS '欄位備註',
    t.Name '欄位型態',
    c.max_length '最大byte',
    c.precision '總位數',
    c.scale '小數位數',
    c.is_nullable '是否可為空',
    ISNULL(i.is_primary_key, 0) '是否是PK',
 '///<summary> ' + cast(ep.value as nvarchar(max)) +' </summary>'+'\n'
 + 'public ' 
 + (case when t.Name in ('char','varchar','nvarchar') then 'string'
 when t.Name in ('datetime','date','time') then 'DateTime'
 when t.Name = 'decimal' then 'decimal'
 when t.Name = 'float' then 'float'
 when t.Name = 'int' then 'int'
 when t.Name = 'bit' then 'bool'
 else 'object' end )
 +' '+ c.name + '{get;set;}'+'\n' as '類別屬性宣告'
FROM    
    sys.columns c
INNER JOIN 
    sys.types t ON c.user_type_id = t.user_type_id
INNER JOIN sys.objects ON sys.objects.object_id = c.object_id
LEFT OUTER JOIN 
    sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
LEFT OUTER JOIN 
    sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
OUTER APPLY fn_listextendedproperty(default,
                  'SCHEMA', schema_name(sys.objects.schema_id),
                  'TABLE', sys.objects.name, 'COLUMN', c.name) ep
WHERE
    c.object_id = OBJECT_ID('你的表名字')

20180713 更新加入C#類別屬性宣告字串 用法:

  1. 複製到class內
  2. 選取剛剛貼上的部分
  3. CTRL+H 打開取代
  4. 按下正則表達選項
  5. 搜尋\\n 取代為 \n
  6. CTRL+E+D 排版

2017年7月17日 星期一

Telerik 舊版本安裝包下載方式 | How to get old version Telerik install package

Telerik 舊版本安裝包下載方式
1.登入官網
2.右上頭像選單→Account OverView


3.Downloads


4.選擇你的安裝框架
5.Version下拉選單切換版本


6.選取Installation安裝檔下載即可

2017年7月13日 星期四

Oracle SQL Developer 使用者設定路徑&顏色設定路徑 | Oracle SQL Developer user setting path & color theme setting path

from
https://stackoverflow.com/questions/7954759/where-does-oracle-sql-developer-store-connections

connection string path

\Users[user]\AppData\Roaming\SQL Developer\system3.2.20.09.87\o.jdeveloper.db.connection.{version number}\connections.xml


----------

custome color theme setting path

\Users\[user]\AppData\Roaming\SQL Developer\system3.2.20.09.87\o.sqldeveloper.{version number}\product-preferences.xml

find below xml section:
┌  
│      
│      (copy all this section to your new setting file)
└      

           

2017年5月19日 星期五

windows 工作排程器出現"工作排程器太過忙碌,無法處理您的要求,請稍後再試(0x80041323)" | Windows scheduler got Error 0x80041323

主因:
一個CONSOLE程式,裡面塞了一行CONSOLE READLINE()
排程設定時選擇 以平行執行,頻率是兩分鐘一次
造成大量的執行完程式卡在背景
達到了排程器預設的同時執行程式上限
導致其他後面啟動的排程無法執行

所以要注意不要在定時執行的排程寫會讓它卡住的東西
或者不要用工作排程器,改寫AP,以多執行序執行

(
電腦裡排程器執行程式上限的設定檔參照:
https://support.microsoft.com/zh-tw/help/2696472/error-0x80041323-when-running-high-number-of-scheduled-tasks
More Information的部分
)


2017年3月13日 星期一

Visual Studio 加入現有資料夾 | Visual Studio add existing folder to project

問題發生的緣由:
我寫了一個測試程式,後來想說可以不用測試,開始正式寫了,
就用TFS復原功能回朔到初始狀態,因為那些檔案只有建立,還沒第一次commit,
但後來又要繼續測一些東西了,所以要把回朔的測試程式加回來,
結果發現加入現有項目只有加檔案,但是已經在方案資料夾內的資料夾不知道怎麼加入

後來找到解法:
在方案總管上方按鈕找到顯示所有檔案的按鈕,按下

然後在該資料夾路徑處對虛線資料夾按右鍵選加入至專案,
就可以把連同資料夾在內的檔案全部加入了