Kinh nghiệm đau thương với Mongoose và AngularJS

Vừa mất hàng giờ đồng hồ chỉ vì những cái cơ bản của 2 thằng này.

Với Mongoose, sub documents của nó không cho phép assign vào biến hay assign mảng như JavaScript vẫn làm được, chỉ có thể remove ra và push vào tương ứng.

Tham khảo thêm tại http://mongoosejs.com/docs/subdocs.html

Với AngularJS, mình không ngờ tới biến nó có tham chiếu (reference). Cái này thấy giống trong Java nên bạn nào biết Java chắc là hiểu. Chỉ cần biết AngularJS có hàm copy để xử lý vụ này:

<code> var dst = angular.copy(src); </code>

hàm copy này là deep copy..

Cài đặt Infinite Scrolling trong Mongoose (server-side) và AngularJS (client-side)

Lâu rồi mới có hứng lại để viết chút gì đó. Hôm nay sẽ là kĩ thuật Infinite Scrolling trên Web mà đồ án giữa kì của mình bắt buộc phải có. Nó (infinite scrolling) là gì ? Nó dịch ra nghĩa đen là “quay tay mệt nghỉ”, thật ra là cuộn vô tận. Đùa thôi, nó là kĩ thuật mà bạn thấy News Feed của Facebook sử dụng đó. Bạn càng cuộn (scroll) xuống dưới thì dữ liệu sẽ hiện ra thêm chứ không hề tải hết trước đó vì khối lượng dữ liệu quá lớn.

Giả sử bạn vào web mà chờ nó load 10.000 records về rồi xem từ từ thì biết khi nào mới xong, chưa kể đang tải về thì gặp sự cố :). Cách này có thể xem là mới so với cách phân trang (pagination) truyền thống. Dĩ nhiên mỗi cách có 1 ưu, nhược điểm riêng chẳng thể nói cách nào là hay hơn tuyệt đối, tùy vào mục đích, lượng dữ liệu, … mà ta có chiến lược cài đặt cách nào.

Về bản chất, theo mình thì để cài đặt Infinite Scrolling đơn giản cần:

– Ở server side, ta sẽ cài đặt các API trả dữ liệu ra theo từng trang, đoạn, khối. Ví dụ client sẽ gửi yêu cầu lấy về 10 records ở trang thứ 1. Server sẽ phải quy ước cái này để client gửi yêu cầu, khi đó server trả về khối dữ liệu cho client.

– Ở client side, ta phải cài đặt được sự kiện scroll xuống dưới đáy trang web (hoặc ở đâu là tùy nhưng chắc không ai làm người dùng khó chịu đâu). Ở mỗi sự kiện này, client dùng AJAX gửi yêu cầu khối dữ liệu tiếp theo tới server.

Cài đặt

Đây là cách cài đặt đơn giản nhất của mình trên server code bằng NodeJS, viết APIs bằng express framework và sử dụng Mongoose để kết nối MongoDB. Người đọc cần có kiến thức về mấy cái này chút ^_^.

Ở server side, ta sẽ viết 1 RESTful API với method là GET để lấy dữ liệu về.


var Project = require('..'models/project');
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
router.get('/projects/:page/:limit', function(req, res) {
    var token = req.headers.token;
    var decoded = jwt.decode(token);
    if (decoded && decoded.email) {
      var query = Project.find({ user: decoded.email})
                .skip(req.param("limit")*req.param("page"))
                .limit(req.param("limit"))
                .sort({ _id : -1});
      query.exec(function(err, projects) {
        //console.log(projects);
        res.json(projects);
      })
    }
  });

Đoạn code trên đây có nghĩa là khi client GET /projects/:page/:limit (ví dụ /projects/0/10) thì server sẽ trả về mảng các projects tại trang thứ page với số lượng là limit. Ta dùng kĩ thuật skip và limit của Mongoose để nhảy đến trang cần trả về.
– Project : ở đây là Model của Mongoose
– router : để định tuyến các url
– jwt : module hỗ trợ mã hóa và giải mã token trên NodeJS (cái này để chứng thực người dùng, không ảnh hưởng tới kỹ thuật này).

Ở phía client side bằng AngularJS, ta viết thêm 1 directive:


'use strict';
angular
  .module('your-app', [...])
  .directive('whenScrolled', function($timeout) {
    return function(scope, attr) {
        var funCheckBounds = function(evt) {
            if ($(window).scrollTop() >= ($(document).height() - $(window).height() - 20)){
              $timeout(function() {
                scope.$apply(attr.whenScrolled);
              })
            }
        };
        
        angular.element(window).bind('scroll load', funCheckBounds);
    };
});

Hàm trên cực kỳ đơn giản, nó kiểm tra scroll xuống dưới cuối trang web thì sẽ apply (gọi) hàm tương ứng với directive ‘whenScrolled’. Khi đó ở Controller tương ứng với trang html có gắn directive này ta viết hàm xử lý. Ví dụ:

Ở HTML,
when-scrolled="loadMoreProject()"


Ở Controller AngularJS:


$scope.busy = false;
$scope.stopped = false;
$scope.page = 0;
$scope.limit = 6;
$scope.projects = [];
$scope.loadMoreProject = function() {
        if ($scope.stopped || $scope.busy) return;
        $scope.busy = true;
        $http({
            method: "GET",
            url: "http://localhost:3000/projects/" + $scope.page + '/' + $scope.limit,
            headers: {
                "token": $scope.userInfo.token
            }
        }).success(function (data, status) {
            if (data.length === 0) {
                $scope.stopped = true;
                $scope.busy = true;
                return;
            }
            //simulate 1s delay when request
            $timeout(function() {
                for (var i = 0; i < data.length; i++)
                    $scope.projects.push(data[i]);
            }, 1000);
            $scope.busy = false;
            $scope.page += 1;
        });
    };

 

Hàm trên này gửi request tới url như đã cài đặt ở server, như ta thấy đó, client tùy biến được tham số limit luôn :D. Các biến $scope.busy$scope.stopped để kiểm tra trạng thái tương ứng tránh việc gửi request quá nhiều.

Tận dụng các biến trạng thái, ta có thể hiện loader cho đẹp sử dụng directive ng-show=”!stopped && busy”.
Đây là 1 kho loader mình chia sẻ http://cssload.net/

[iOS Tutorial] – Truyền dữ liệu giữa 2 màn hình (view) trong iOS

Việc truyền dữ liệu qua lại giữa các màn hình là rất cần thiết và thường xuyên sử dụng trong các ứng dụng iOS. Sau đây mình sẽ trình bày một số cách truyền dữ liệu mà mình biết từ đơn giản tới phức tạp, và tùy trường hợp mà các bạn sẽ chọn cách thích hợp cho mình.

Continue reading [iOS Tutorial] – Truyền dữ liệu giữa 2 màn hình (view) trong iOS

[Review] Chuyến đi Bình Hưng 1 ngày bằng xe máy

Chuyện là hôm qua ngày lễ 2/9, mình cùng 3 người bạn lên đường đi chơi đảo Bình Hưng.

Binh Hung Island
Đảo Bình Hưng trên Google Maps

Trên đây là vị trí đảo Bình Hưng, theo cái đường nét đứt ranh giới 2 tỉnh thì rõ ràng là nằm trong địa phận Ninh Thuận nhưng nhiều người nói ở Cam Ranh, Khánh Hòa.

Mang tiếng là dân Ninh Thuận mà quả thật là trước giờ chưa đi đảo Bình Hưng này bao giờ dù là không xa lắm, nếu đi từ trung tâm TP. Phan Rang – Tháp Chàm thì tầm 50 km. Ban đầu vẫn chưa quyết định là đi hay không nhưng với tinh thần chịu chơi, 8 h mình quyết định đi và 8h45 khởi hành. Đến nơi khoảng hơn 10h 1 tí, may mắn là trời nắng đẹp.

Continue reading [Review] Chuyến đi Bình Hưng 1 ngày bằng xe máy

Kết thúc 2 tháng thực tập tại ISB Việt Nam

Entry này xem như bài viết đầu tiên trên blog của tui, dĩ nhiên là Hello world thì không tính rồi vì đó chỉ là test chơi thôi :)).

Lan man 1 xíu, hồi trước cũng có blog, cũng viết blog nhưng bây giờ thì bản thân cảm thấy những gì mình học được, trải nghiệm được nếu không có cái gì đó lưu lại thì thật là phí. Dù sau thì viết blog lợi về nhiều cái: rèn kỹ năng viết lách, diễn giải dễ hiểu, rèn chính tả để khỏi quên tiếng Việt nữa :)). Thời gian dành viết blog coi như review lại mình đã làm được những gì, đỡ hơn là ngồi lướt Facebook hay Vozforums.

Thời gian đầu có thể 1 tuần chả đẻ được bài nào, hy vọng sau 1 thời gian tần suất post sẽ tăng lên :D.

Phew, cuối cùng cũng xong 2 tháng thực tập tại ISB Việt Nam (IVC – ISB Vietnam Cooperation) từ 23-6-2014 đến 23-8-2014.

Hôm nay là ngày kề cuối rồi, xong ngày mai nữa là kết thúc kỳ thực tập tại đây nên quyết định review về công việc và môi trường ở đây :

Continue reading Kết thúc 2 tháng thực tập tại ISB Việt Nam