Posts match “ AngularJS ” tag:

fix($parse): deprecate promise unwrapping and make it an opt-in

http://goo.gl/BbPJMr

This commit disables promise unwrapping and adds
$parseProvider.unwrapPromises() getter/setter api that allows developers
to turn the feature back on if needed.

從開始寫 Angular.js 就知道這項讓 template 與 promise 深深結合的功能,實際在 project 中使用的時候還真的是意外的相當... 用也不是不用也不是

1.2.0-rc3 終於被改成 opt-in 了...
預設是關閉,想要啟用的話可以

.config(['$parseProvider', function ($parseProvider) {
  $parseProvider.unwrapPromise(true);
}]);

UI as route

ui-routerangular-ui 旗下一員大將,其 github README 介紹的主要特色如下:

  1. 使用 state 取代 angular 原生的 route
  2. 可巢狀的 state<ui-view> (view template directive)
  3. 支援多個 <ui-view>,可以給定名稱作為 reference 依據

特別點出來的原因在於今天要講的都不是這些,而是我打從一開始看到 ui-router 就想要使用它的真正原因

既不是巢狀 view ,也不是它的 state,而是 state 衍生出的 UI as route 能力。

Read on

Overriding $exceptionHandler

今天碰到結果因為神秘的 cross-domain 引用的 script error 沒有跳在 console 而卡了超久...

$exceptionHandler 的官方文件介紹:

Any uncaught exception in angular expressions is delegated to this service. The default implementation simply delegates to $log.error which logs it into the browser console.

在 v1.1.5 的 AngularJS source code 如下:

function $ExceptionHandlerProvider() {
  this.$get = ['$log', function($log) {
    return function(exception, cause) {
      $log.error.apply($log, arguments);
    };
  }];
}

比如說今天針對 angular uncaught exception 我們想要做一些額外的 log,那麼就必須 override 預設的 $exceptionHandler,在隨意寫了一個 override 用的 service 之後你很可能會碰壁...

angular.module('myApp')
.factory('$exceptionHandler', function ($timeout, $log) {
  return function(exception, cause) {
    var args = Array.prototype.slice.call(arguments);
    // delay $log.error by 100ms

    $timeout(function () {
      $log.error.apply($log, args);
    }, 100);
  };
});

錯誤訊息:

Circular dependency found: DepA <- DepB <- DepC

於因出在於其實有部分 AngularJS 原生的 service 都有 inject $exceptionHandler,所以這些 service 是不能再被 inject 到新生的 $exceptionHandler 中的,否則就會造成所謂的 circular dependancy (循環 dependancy?)

不能 inject 到 override 用的 $exceptionHandler 的原生 service 表列如下:

  • $rootScope
  • $timeout
  • $compile
  • $interpolate
  • $q

另外就是 試圖 inject 其它 module 的 service 進 $exceptionHandler 時候,那些 service 也不能夠包含以上的原生 service dependancy

angular-easyfb v0.2.0 released!

http://github.com/pc035860/angular-easyfb

  • AngularJS $q promise support
  • Fix minified code run-time error

0.2.0 加入了 $q promise 的支援,因此 增加了在 view 裡面使用的彈性

更新:AngularJS 1.2.0 之後將 promise 在 view 中自動 unwrap 的功能 deprecate 了,因此支援 promise 可能未來並不會增加在 view 裡面使用的彈性。

API demo (promise version)

jqLite Orz list (v1.1.5)

jqLite 是 AngularJS 為了方便進行 DOM 操作而又不希望讓 jQuery 成為 dependency 的解決方案,官方文件如是說:

jqLite is a tiny, API-compatible subset of jQuery that allows Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality within a very small footprint, so only a subset of the jQuery API - methods, arguments and invocation styles - are supported.

並且在文件下方給了一串相關 API 文件的連結,連到 jQuery API Documentation 去。

嗯?連到 jQuery API Documentaion 有什麼不對嗎?

前言

爬一爬 jqLite 的 source code 就可以知道,其實它是嘗試照著 jQuery 的部分 API 做 subset-implementation,結果就是雖然它給了 jQuery API Documentation 的連結,它部分 API 的部分功能是與 jQuery 不同的。如果你還是照著使用 jQuery 的用法來使用 jqLite 而沒有做好爬 jqLite source code 的心理準備,應該會有點 happy ...

本文整理了 v1.1.5 幾個比較常碰到的地雷,與各位共勉之。

Read on

踩到 ui-router 0.2.0 的 bug

其實這是一個看一看 ui-router 的 wiki,再瞄一下 source code 就可以發現的 bug ,不過由於影響不是慎大,也沒有寫在 test spec 裡,它就這樣在 0.2.0 release 接近兩個月的現在還躺在那兒。

竹林中

陰影

昨天在忙 git-source (其實也就是在忙這個 blog),觀察到一件讓我非常在意的事情。

下面是一頁我在 localhost 測試嵌入 git-source 到頁面中的截圖,這頁一開進去,歷史記錄馬上就有三筆。

緊接著,我按一次「重新整理」,變成下面這樣:

此刻,我驚覺這不是一個小問題,弄不好可能會毀掉我的 git-source 專案...

Read on

git-source

https://github.com/pc035860/git-source

git-source shows off your github pages hosted source codes and examples.

最近終於完成的小專案,其實是為了寫 blog 特別設計的,希望能夠同時滿足四件事情

  • 程式碼上色
  • 能看結果
  • 可以編輯
  • 速度快

原本 hosting 有考慮過 Plunker,不過 Plunker 的穩定度跟速度實在有待加強,最後就決定採用 GitHub Pages

因此目前 blog 內的 內嵌 demo 都是透過 git-source 實現的。
更新:Blog 搬移到 logdown 之後還是改用 Plunker了。

用 ngQueue 來完成需要排隊的非同步工作

$q promise 是使用 AngularJS 時相當重要的一環,熟悉並活用之後可以讓許多原本看似較雜亂的 event-base 程式碼看起來謹然有序(當然也不是所有的 event-base 程式都適合使用 promise 來實現)。

排隊的需求

有時候我們會碰到一些特別的情況:

一段一段的動作是依序進行,但觸發的時候卻又是一次性的(比如說在一個 loop 中一次排定 10 個非同步的工作,需要依序執行)。

用程式碼表示大概是這樣:

var taskIds = [1, 2, 3, 4, 5];

angular.forEach(taskIds, function (taskId) {

  // 以 $http 為例
  $http.get('/someUrl', {params: {taskId: taskId}});

});

console.log('工作順利完成!');

然而我們真正想做的事情是這樣:

$http.get('/someUrl', {params: {taskId: 1}})
.then(function () {
  return $http.get('/someUrl', {params: {taskId: 2}});
})
.then(function () {
  return $http.get('/someUrl', {params: {taskId: 3}});
})
.then(function () {
  return $http.get('/someUrl', {params: {taskId: 4}});
})
.then(function () {
  return $http.get('/someUrl', {params: {taskId: 5}});
})
.then(function () {
  console.log('工作順利完成!');
});

在上面的例子中,工作只有五項所以固然可以自己打一打程式碼解決,一般來說碰到的情況會有下面的困境:

  • 工作項目數未知(為動態的或為參數)
  • 同時可以進行的工作數不只一項(以上面為例即 1, 2 可以同時進行,而 3, 4 可以同時進行,最後再單獨執行 5 即可)
Read on

Programmatical form submission

AngularJS 做為一個以建立 Single Page App 為目標的 framework,當然是不希望你在 web app 中碰到需要與後端做資料溝通時會需要 refresh 整個網頁,因此它提供了完整的 HTML <form> 解決方案,諸如:

  • 透過 form directive 攔截預設的 form submission
  • 多樣化而且可以自訂的 validation 功能
  • 提供 ng-submit 以及 [type="submit"] 上的 ng-click 作為真正送出 form 時候的程式動作

問題

然而,我們不時的還是會碰到真正需要 page refresh 的 form submission

  • 該功能還沒有做 API 的時候(容易發生在從 backend-based 轉移成為 SPA 的時候)
  • 介接第三方網站功能的時候(例如金流業者)

貼心如 AngularJS,當然也理解你的困難,於是

Angular prevents the default action (form submission to the server) unless the <form> element has an action attribute specified.

可惜天下的事情通常都不會如此順利(蛤)。

Read on

AngularJS browser autofill workaround by using a directive

http://stackoverflow.com/questions/14965968/angularjs-browser-autofill-workaround-by-using-a-directive

Apparently this is a known issue with Angular and is currently open

app.directive('autoFillSync', function($timeout) {
   return {
      require: 'ngModel',
      link: function(scope, elem, attrs, ngModel) {
          var origVal = elem.val();
          $timeout(function () {
              var newVal = elem.val();
              if(ngModel.$pristine && origVal !== newVal) {
                  ngModel.$setViewValue(newVal);
              }
          }, 500);
      }
   }
});
<form name="myForm" ng-submit="login()">
   <label for="username">Username</label>
   <input type="text" id="username" name="username" ng-model="username" auto-fill-sync/><br/>
   <label for="password">Password</label>
   <input type="password" id="password" name="password" ng-model="password" auto-fill-sync/><br/>
   <button type="submit">Login</button>
</form>

我的想法

印象中以前查的時候好像還沒有丟出這個 auto-fill-sync 的 directive,所以這次真的碰到要解決這個問題,我很快的想到應該實驗一下 browser 的 autofill 是不是真的會在 <input> 裡 fill 進東西。

如果只是 AngularJS 那邊沒有抓到 autofill 的動作,應該就還算好解決,只要想辦法通知 AngularJS 就好了。

怕是怕 browser 是直到你確定要送出前夕才塞入 autofill 的值,這樣誰也幫不了我了~~~

$('#username').val();
// output: "我的帳號"

好加在實驗結果是後者 (呼)

於是我寫了一個自己的版本(因為沒有先看 stackoverflow 那篇...)

autocomplete-fix.js
app.directive('autocompleteFix', ['$timeout', '$log', function ($timeout, $log) {
  return {
    require: 'ngModel',
    restrict: 'A',
    link: function postLink(scope, iElm, iAttrs, ctrl) {
      /**
       * Delay for 450ms works for Chrome on OS X
       */
      var _delay = 450;

      if (iAttrs.autocompleteFix) {
        _delay = parseInt(iAttrs.autocompleteFix, 10);
      }

      $log.debug('delay', _delay);

      $timeout(function () {
        ctrl.$setViewValue(iElm.val());
      }, _delay);
    }
  };
}]);

有趣的地方是,_delay 在 300, 400 時,都會有機會沒通知到 AngularJS,所以我自己試的恰當值大約是 450(而 stackoverflow 上的是 500)。

感覺 stackoverflow 上的版本似乎考慮比較周到,所以就留個 gist 存著吧!

在網頁上檢查自家 Chrome extension 是否已安裝

續上一篇文章

直接在網站頁面上安裝你的 Chrome extension

最後提到為了能夠在提供安裝的頁面展現出我們的誠意(明明上一篇好像不是這樣說的),總得有個辦法來各別針對 未安裝/已安裝 的使用者做頁面的客製化。

基本原理

其實就是官方文件提供的方法。

Extensions can communicate with the embedding page via content scripts to let it know that they are already installed.

可以歸納出大致兩種基本作法:

  • 在需要檢查的頁面讀入特定 content script 之後,檢查時利用 window.postMessagecontent script 對話,基本上只要約定一下傳輸的內容就可以知道 extension 安裝成功
  • 在需要檢查的頁面讀入特定 content script,讓它直接在該頁面上塞一個 <div id="my-awesome-extension"></div>,檢查的動作只要檢查該 div 是否存在即可

實作

這邊選擇第二種做法,節奏明快!

Read on

angular-easyfb v0.2.2 released!

https://github.com/pc035860/angular-easyfb/releases/tag/v0.2.2

  • setInitFunction() in configure block for initialization customization
  • Make $FB.Event.unsubscribe unsubscribes events properly

2014 的第一天修掉了一個可怕的 bug。

之前照著人家回報的 issue 做了一個 plunk,意外發現 $FB.Event.unsubscribe 竟然沒有作用(雖然那 issue 不是回報這個),查了 Facebook 的 doc 才發現 FB.Event.unsubscribe 需要帶上原本 call FB.Event.subscribe 時用的 listener function 才能夠順利運作。

老實說以目前的 implementation 要讓這件事可以 work 還蠻玄妙的,總之還是改好了 YA!
FB.Event 應該算是蠻常用到的 API ,出 bug 真是太可怕了...

ngQueue v0.1.0 released!

https://github.com/pc035860/ngQueue/releases/tag/v0.1.0

The very first release.

其實關於 ngQueue,早在 用 ngQueue 來完成需要排隊的非同步工作 就已經有介紹過用法了。不過當時在 GitHub 的 release 頁面 上並沒有 tag 任何一次的 release,所以這次才算是真正的第一次 release 啦~

本來是想說反正別人使用的需求也不大,我做 release 只是加上 GitHub tag 之後用 bower 對一些人的開發流程來說比較方便而已... 這次也就是因為我的下一個 module 有這個 ngQueue 的 dependency 需求,求個方便控管 dependency 之餘也就順便把 ngQueue release 好了。

當然也不是什麼都沒改就 release,這樣也太沒誠意。

為了下一個 module,ngQueue$queueFactory 新增了 deferred 這個只加了幾行 code 但其實非常厲害的選項(都我在講)。原本為了加這個選項還有新的 queue API remove(),我天花亂墜地寫了一群 code,最後全部砍光光了(爆頭)...

總之也請期待下一個 module!