ASP.NET MVC 5 với AngularJS - Phần 1


Giới thiệu
AngularJS theo cách tôi đã học, tôi đã có ý định chia sẻ với các bạn, những người đang đi tìm kiếm cho việc học AngularJS theo cách được rút gọn. Bài viết này là dành cho họ.
Dưới đây là luồng bài viết chúng ta sẽ đi lần lượt từng bước một. Trước tiên chúng ta sẽ lựa chọn với câu hỏi cái gì và tại sao.
  • Vậy AngularJS là gì?
  • Tại sao lại đặt tên là Angular?
  • Tại sao phải dùng một framework khác JavaScript? Jquery với Angular
  • Chúng ta sẽ so sánh sự khác biệt của chúng với một ví dụ
  • Tải về và cài đặt thư viện AngularJS trong Visual Studio 2015
  • Bắt đầu AngularJS với Visual Studio
  • Chúng ta sẽ đệ trình một form với AngularJS
  • Tìm hiểu về trạng thái Form
  • Gửi dữ liệu form đến MVC Controller
  • Tạo URL trong AngularJS
Ok, chúng ta hãy bắt đầu. Trước khi chúng ta tập trung vào bất kỳ chủ đề nào, chúng ta phải biết AngularJS là gì

AngularJS là gì
AngularJS là một framework JavaScript phía client dựa trên mẫu MVC. Nó là mã nguồn mở, được hỗ trợ và duy trì bởi Google. AngularJS còn gọi là "Angular" hay "Angular.js" đã được phát hành lần đầu vào năm 2009, mục đích để cải tiến quy trình phát triển và kiểm thử.

Tại sao lại đặt tên AngularJS
Chúng ta biết rằng HTML được chứa bên trong cặp dấu ngoặc nhọn (angle brackets - <>) (ví dụ: <html>) vì vậy tên Angular cũng đến từ ý nghĩa đó. AngularJS sử dụng các chỉ thị như ng-app, ng-model mà tiền tố với "ng" (chỉ dẫn cơ bản) làm chúng ta liên tưởng đến "Angular"

Tại sao phải dùng một framework khác JavaScript

Cả hai được dùng với mục đích tạo kịch bản phía Client, nhưng AngularJS đơn giản là mang đến nhiều tính năng và lợi ích hơn. Như chúng ta đã biết AngularJS là framework dựa trên mẫu MVC, đây là mẫu có tính chất module và có khả năng được sử dụng lại.
Dưới đây là tổng quan về cả hai:
AngularJS:
  • Là framework JavaScript MVC được hỗ trợ và duy trì bởi Google
  • Data-binding thông minh
  • Sử dụng mẫu thiết kế MVC
  • Hỗ trợ Form Validations
  • Hỗ trợ định tuyến (Single Page Application)
  • Sử dụng Dependency Injection(DI)
  • Unit test dễ dàng
  • Kiến trúc modular và tái sử dụng
  • REST-friendly
jQuery
  • Là framework JavaScript mã nguồn mở khá nhẹ
  • Là công cụ tuyệt vời cho việc tương tác và điều khiển các thành phần DOM
Ví dụ:
AngularJS
<body ng-app> 
    First Name: <input type="text"ng-model="fname"/><br/> 
    Last Name: <input type="text"ng-model="lname"/><br/> 
    Result: {{fname+''+lname}} 
</body> 
</html> 
<script src="Scripts/angular.min.js"></script> 

jQuery
<body> 
    First Name: <inputtype="text"id="txtfName"/><br/> 
    Last Name: <inputtype="text"id="txtlName"/><br/> 
    Result: <labelid="lblName"></label> 
</body> 
</html> 
<script src="Scripts/jquery-2.2.0.min.js"></script> 
<script type="text/javascript"> 
    $(document).ready(function () { 
        $(function () { 
            $('#txtfName').keyup(function () { 
                $('#lblName').text($('#txtfName').val() + ' ' + $('#txtlName').val()); 
            }); 

            $('#txtlName').keyup(function () { 
                $('#lblName').text($('#txtfName').val() + ' ' + $('#txtlName').val()); 
            }); 
        }); 
    }); 
</script> 

Bạn thích cái nào hơn? Cả hai đều cho ra cùng một kết quả
Ok, chúng ta hãy bắt đầu với AngularJS: trước tiên kiểm tra với một website đơn giản trong Visual Studio 2015

Tải và cài đặt thư viện AngularJS trong Visual Studio 2015
Truy cập vào website AngularJS và tải về

Nhấp chuột vào liên kết Tải về (Download) để tải phiên bản mới nhất của thư viện AngularJS.

AngularJS với Visual Studio
Hãy mở Visual Studio 2015 (IDE) lên, vào File -> New -> Project và sau đó một cửa sổ mới sẽ xuất hiện như hình bên dưới:
Hình 1.0
Nhấn chọn APS.NET Web Application, đổi tên ứng dụng, sau đó nhấn vào nút OK góc phải bên dưới, chọn Empty Template trong cửa sổ tiếp theo nhấn nút OK, Sẽ mất chút thời gian chờ đợi để chương trình nạp dự án trống.
Bây giờ điều đầu tiên chúng ta cần làm là đăng ký AngularJS.core trong ứng dụng này. Chúng ta cần phải có được tham chiếu từ Nuget
Để làm điều này chúng ta nhấn chuột phải lên tên dự án, nhấn vào Manager Nuget Packages như trong hình sau:
Hình 1.1
Trong cửa sổ tiếp theo lựa chọn tìm kiếm với tên "Angular", cài đặt phiên bản được cập nhật của AngularJS.core như hình sau
Hình 1.2
hoặc vào menu Tools -> NuGet Package Manager -> Package Manager Console, gõ vào lệnh Install-Package AngularJS.Core.
Tương tự chúng ta cũng cần thêm thư viện jQuery vào dự án của chúng ta như hình dưới
Hình 1.3
Các tiến trình cài đặt của chúng ta đã hoàn thành. Bây giờ kiểm tra nó với mã AngularJS trước đó của chúng ta so với phần jQuery.
Hãy thêm mới một trang HTML và đặt tên cho nó là "index"
Hình 1.4
Mã trong file index.html:
<body ng-app> First Name:  
    <input type="text" ng-model="fname" />  
    <br/> Last Name:  
    <input type="text" ng-model="lname" />  
    <br/> Result: {{fname+''+lname}} </body>  
    </html>  
    <script src="Scripts/angular.min.js">
</script>  
Chú ý rằng ví dụ trên là mã của một file HTML đơn giản, nhưng có một số thuộc tính mới chưa biết và dấu ngoặc ôm.
Các thuộc tính mới "ng-app", "ng-model" là các chỉ dẫn Angular và "{{}}" là biểu thức, sau đây chúng ta sẽ đi thảo luận về chúng.
Ồ, vậy chỉ dẫn AngularJS là gì? Các chỉ dẫn AngularJS là sự mở rộng các thuộc tính HTML với tiền tố "ng"
Để tìm hiểu nhiều hơn về các chỉ dẫn AngularJS, bấm vào đây

Kết quả

Form với AngularJS
Chúng ta hãy thêm một dự án MVC trống để làm việc với form và sau đó chúng ta sẽ gửi dữ liệu đến Controller. Để làm việc này bạn nhấn chuột phải lên solution đang tồn tại, nhấn File -> New -> Project và đặt tên cho nó là AngularJSForm
Hình 1.5
Bây giờ đăng ký thư viện AngularJS, xem lại Hình 1.1 - 1.3 ở trên.
Trong thư mục Controller thêm một class HomeController, và tạo một View tên là Index.cshtml
Hình 1.6
Chúng ta vào thư mục View -> Shared, sửa file _layout.cshtml, thêm "ng-app='myFormApp' vào thẻ body
<body ng-app="myFormApp">

Trong file Index.cshtml sửa như sau:
Form:
 <div id="content" ng-controller="CustomerController">
    <span ng-show="isViewLoading" class="viewLoading">
        <img src="~/Content/images/ng-loader.gif" /> loading...
    </span>
    <div ng-class="result">{{message}}</div>
    <hr />
    <form ng-submit="submitForm()" name="frmCustomer">
        <div>
            <label for="name">Customer Name</label>
            <input type="text" ng-model="cust.CustName" name="cname" placeholder="Enter your name" required ng-minlength="4" ng-maxlength="14" />
            <span class="error" ng-show="(frmCustomer.$dirty||submitted) && frmCustomer.cname.$error.required">Customer name is Required</span>
            <span class="error" ng-show="frmCustomer.$dirty && frmCustomer.cname.$error.minlength">Minimum length required is 5</span>
            <span class="error" ng-show="frmCustomer.$dirty && frmCustomer.cname.$error.maxlength">Maximum length required is 15</span>
        </div>
        <div>
            <label for="email">E-mail address</label>
            <input type="email" ng-model="cust.CustEmail" name="email" placeholder="Enter your Email" required />
            <span class="error" ng-show="(frmCustomer.$dirty ||submitted) && frmCustomer.email.$error.required">EmailId is Required!</span>
            <span class="error" ng-show="(frmCustomer.$dirty ||submitted) && frmCustomer.$error.email">Invalid EmailId!</span>
        </div>
        <div>
            <input type="submit" value="Submit" ng-disabled="frmCustomer.$invalid">
        </div>
    </form>
</div> 
Script:
@section JavaScript{
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/angular.min.js"></script>
    <script src="~/Scripts/angular-route.min.js"></script>
    <script>
           angular.module('myFormApp', []).
              controller('CustomerController', function ($scope, $http, $location, $window)              {
                 $scope.cust = {};
                 $scope.message = '';
                 $scope.result = "color-default";
                 $scope.isViewLoading = false;

                 //get called when user submits the form
                 $scope.submitForm = function () 
                                                    {
                    $scope.isViewLoading = true;
                    console.log('Form is submitted with:', $scope.cust);

                    //$http service that send or receive data from the remote server
                    $http(
                                                       {
                      method: 'POST',
                      url: '/Home/CreateCustomer',data: $scope.cust
                    }).success(function (data, status, headers, config) 
                    {
                      $scope.errors = [];
                      if (data.success === true) 
                                                    {
                          $scope.cust = {};
                          $scope.message = 'Form data Submitted!';
                          $scope.result = "color-green";
                          $location.path(data.redirectUrl);
                          $window.location.reload();
                      }
                      else {
                          $scope.errors = data.errors;
                      }
                    }).error(function (data, status, headers, config) 
                                                       {
                        $scope.errors = [];
                        $scope.message = 'Unexpected Error while saving data!!';
                    });
                    $scope.isViewLoading = false;
                  }
               }).config(function ($locationProvider) 
               {

                  //default = 'false'
                  $locationProvider.html5Mode(true);
               });

     </script>
}

Form style:
<style> 

#content label { 
    width: 150px; 
} 

#content input[type=submit] { 
    margin-left: 154px; 
    width: 120px; 
    padding: 5px15px; 
    background: #ff6a00; 
    border: 0none; 
    cursor: pointer; 
    color: #fff; 
} 

.error { 
    color: red; 
} 

.color-default { 
    color: #000; 
} 

.color-red { 
    color: red; 
} 

.color-green { 
    color: green; 
} 

#content input.ng-dirty.ng-invalid { 
    border: 1pxsolidred; 
    background-color: #fff4f4; 
} 

</style> 

Kết quả:
Giải thích code
Trong ví dụ về form của chúng ta có thêm một số thuộc tính mới "ng-app, ng-controller, ng-show, ng-class, ng-submit, ng-model, ng-disabled" chúng ta đã thấy "ng-model" được sử dụng trong ví dụ đầu tiên.
Đây là tất cả các chỉ dẫn của AngularJS
Chúng ta hãy thảo luận về điểm bắt đầu ứng dụng AngularJS, là Module. Vậy module trong AngularJS là gì?
Module: module không là gì cả nhưng là một container (khung chứa) các phần khác nhau của một ứng dụng cũng như là controller, service, filter, directives, factories, ...
Module xác định nơi ứng dụng cần được khởi động.

angular.module('myFormApp', [])

Đây là một phương thức module có hai tham số
Tham số đầu tiên là tên module, tham chiếu đến module "myFormApp" trong <body ng-app='myFormApp'>
Tham số thứ 2 là một mảng rỗng trong phương thức "angular.module(‘myFormApp’, [])". Mảng này là danh sách của các module phụ thuộc khác.

Tiếp theo chúng ta sẽ tập trung vào Javascript Controller (CustomerController)
Javascript Controller
.controller('CustomerController', function ($scope) { 
   //inner code 
});

Controller là một hàm Javascript trong AngularJS. Chỉ thị ng-controller trong <div id=”content” ng-controller=”CustomerController”>  chỉ định điều khiển ứng dụng
Sử dụng đối tượng $scope để điều khiển quyền kiểm soát hành vi của ứng dụng. Vậy thì, $scope là gì?
$scope: $scope là một "đối tượng" mà ở đó các phần tử DOM và controller "liên kết lại với nhau". Nói một cách đơn giản, $scope là cầu nối giữa HTML View và Javascript Controller
Vào đây để tìm hiểu thêm về $scope

HTML View:
<div ng-class="result">{{message}}</div>

Javascript Controller
.controller('CustomerController', function ($scope) { 
   $scope.message = 'Show in view'; 
}) 

Chúng ta đi thảo thuận về hợp lệ (validations) của form
Hợp lệ - Validations
Hợp lệ kiểm tra thông tin mà người sử dụng nhập vào có hợp lệ hay không. AngularJS cũng có tính năng hợp lệ dữ liệu giống như JQuery/JQueryUnobtrusive. Ở đây chúng ta sẽ tìm hiểu ngắn gọn về chúng.
Trường được yêu cầu hợp lệ (Required Field Validator): để hợp lệ kiểm tra dữ liệu người dùng nhập có rỗng hay không, với AngularJS chúng ta sử dụng thuộc tính HTML5 "required" hoặc "có thể sử dụng ng-required = "true"
<inputtype="email"ng-model="cust.CustEmail"name="email"required/>

Thông báo hợp lệ - Validation Message
<span class="error" ng-show="(frmCustomer.$dirty||submitted)&&
frmCustomer.email.$error.required">
EmailId is Required!</span>

Hợp lệ theo vùng - Range Validator: để kiểm tra dữ liệu đầu vào theo vùng với Angular chúng ta sử dụng các thuộc tính HTML5 "ng-minlength, ng-maxlength"
<inputtype="text" ng-model="cust.CustName" name="cname" requiredng-minlength="4" <br /> ng-maxlength="14"/>

Thông báo hợp lệ - Validation Message
<spanclass="error" ng-show="frmCustomer.$dirty&&<br />frmCustomer.cname.$error.minlength">Minimum length required is 5</span> 

<spanclass="error" ng-show="frmCustomer.$dirty&& <br />frmCustomer.cname.$error.maxlength">Minimum length required is 15</span>

Kiểu hợp lệ - Type Validator: để kiểm tra kiểu email chúng ta có thể sử dụng hợp lệ này trong AngularJS
<spanclass="error" ng-show="(frmCustomer.$dirty||submitted)&&<br />frmCustomer.$error.email">Invalid EmailId!</span>  

Cập nhật trạng thái Form
Bản thân AngularJS có một số thuộc tính để cập nhật trạng thái form. Chúng là các giá trị true hay false
  • $error: Đối tượng chứa tất cả các thuộc tính hợp lệ áp dụng cho một phần tử cụ thể
  • $pristine: true nếu người dùng chưa tác động đến điều khiển, ngược lại trả về False
  • $valid: true nếu model hợp lệ
  • $invalid: true nếu model không hợp lệ
  • $dirty: true nếu người dùng đã thay đổi giá trị model ít nhất một lần 
  • $touched: true nếu người dùng đã chuyển hướng ra khỏi điều khiển
  • $untouched: true nếu người dùng chưa chuyển hướng ra khỏi điều khiển
Gửi dữ liệu Form đến MVC Controller
Để gửi form đến MVC Controller chúng ta cần thêm một phương thức vào HomeController và chúng ta cần sửa đổi CustomerController trong phần kịch bản - script.
Trong HomeController chúng ta thêm phương thức như sau:
[HttpPost] 
public JsonResultCreateCustomer(Customer model) 
{ 
    if (ModelState.IsValid) 
    { 
        //Data save to database 
        returnJson(new 
        { 
            success = true 
        }); 
    } 
    return Json(new 
    { 
        success = false, 
            errors = ModelState.Keys.
                      SelectMany(i => ModelState[i].Errors).
                      Select(m => m.ErrorMessage).ToArray() 
    }); 
}

Tiếp đến sửa đổi Javascript trong CustomerController
angular.module('myFormApp', []). 
controller('CustomerController', function ($scope, $http) 
{ 
    $scope.cust = {}; 
    $scope.message = ''; 
    $scope.result = "color-default"; 
    $scope.isViewLoading = false; 
    //get called when user submits the form 
    $scope.submitForm = function () 
    { 
        $scope.isViewLoading = true; 
        console.log('Form is submitted with:', $scope.cust); 
        //$http service that send or receive data from the remote server 
        $http( 
        { 
            method: 'POST', 
            url: '/Home/CreateCustomer', 
            data: $scope.cust 
        }).success(function (data, status, headers, config) 
        { 
            $scope.errors = []; 
            if (data.success === true) 
            { 
                $scope.cust = {}; 
                $scope.message = 'Form data Submitted!'; 
                $scope.result = "color-green"; 
            } 
            else 
            { 
                $scope.errors = data.errors; 
            } 
        }).error(function (data, status, headers, config) 
        { 
            $scope.errors = []; 
            $scope.message = 'Unexpected Error while saving data!!'; 
        }); 
        $scope.isViewLoading = false; 
    } 
});

Chúng ta hãy đặt một điểm ngắt - breakpoint ở phương thức CreateCustomer() trong HomeController và chạy nó, sau khi bạn gửi form nó sẽ dừng lại ở điểm ngắt - breakpoint.
Hình 1.7
Trong chế độ debug chúng ta có thể thấy model đã được ... với dữ liệu form mà chúng ta có thể gửi đến và lưu vào cơ sở dữ liệu.
Kết quả:
Kết quả tương tự như trước.

Giải thích code
Ở ví dụ về việc gửi dữ liệu form đến Controller ở trên chúng ra đã sử dụng dịch vụ (service) $http. Ồ vậy chúng là gì?
  • Service: Các dịch vụ (services) AngularJS là các hàm Javascript có mối quan hệ với controller, model hoặc các chỉ thị tùy chỉnh (custom directives). AngularJS cũng có các dịch vụ hữu ích khác như $interval, $window, $log. Để tìm hiểu nhiều hơn về các dịch vụ bấm vào đây
  • $http service: Chúng ta có thể sử dụng dịch vụ $http để gửi một yêu cầu AJAX. Trong ví dụ này, chúng ta đã gửi yêu cầu Http POST đến máy chủ từ xa để gửi dữ liệu.
Tạo URL HashTag(#) miễn phí trong AngularJS
Tiếp theo chúng ra sẽ chuyển hướng đến một URL mới sau khi form được gửi sử dụng dịch vụ $location, AngularJS sẽ thêm "#" vào url để ngăn chặn chuyển hướng đến một url mong muốn.
Tìm hiểu nhiều hơn về $locationProvide ở đây

Giải quyết vấn đề này bằng cách rút "#" ra khỏi URL, chúng ta cần Inject $locationProvider trong hàm config() của angular root module sau đó thiết lập html5Mode là true
.config(function ($locationProvider) { 
   $locationProvider.html5Mode(true); 
});

Trong phần script chúng ta cần sửa đổi CustomerController và thêm vào hai dòng code mà sẽ chỉ ra đường dẫn và chuyển hướng nạp lại toàn bộ trang
$location.path(data.redirectUrl); 
$window.location.reload();

Thêm thẻ base trong phần head của master page/layout (_layout.cshtml) của ứng dụng
<basehref="/"> 

CustomerController(Javascript) đầy đủ:
angular.module('myFormApp', []). 
controller('CustomerController', function ($scope, $http, $location, $window) 
{ 
    $scope.cust = {}; 
    $scope.message = ''; 
    $scope.result = "color-default"; 
    $scope.isViewLoading = false; 

    //get called when user submits the form 
    $scope.submitForm = function () 
    { 
        $scope.isViewLoading = true; 
        console.log('Form is submitted with:', $scope.cust); 

        //$http service that send or receive data from the remote server 
        $http( 
        { 
            method: 'POST', 
            url: '/Home/CreateCustomer', 
            data: $scope.cust 
        }).success(function (data, status, headers, config) 
        { 
            $scope.errors = []; 
            if (data.success === true) 
            { 
                $scope.cust = {}; 
                $scope.message = 'Form data Submitted!'; 
                $scope.result = "color-green"; 
                $location.path(data.redirectUrl); 
                $window.location.reload(); 
            } 
            else 
            { 
                $scope.errors = data.errors; 
            } 
        }).error(function (data, status, headers, config) 
        { 
            $scope.errors = []; 
            $scope.message = 'Unexpected Error while saving data!!'; 
        }); 
        $scope.isViewLoading = false; 
    } 
}).config(function ($locationProvider) 
{ 
    //default = 'false' 
    $locationProvider.html5Mode(true); 
}); 

HomeController(MVC) đầy đủ:
public class HomeController: Controller 
{ 
    // GET: Home 
    publicActionResult Index() 
    { 
        return View(); 
    } 

    [HttpPost] 

    public JsonResultCreateCustomer(Customer model) 
    { 
        if (ModelState.IsValid) 
        { 
            //Data save to database 
            varRedirectUrl = Url.Action("About", "Home", new 
            { 
                area = "" 
            }); 

            returnJson(new 
            { 
                success = true, redirectUrl = RedirectUrl 
            }); 
        } 

        returnJson(new 
        { 
            success = false, 
                errors = ModelState.Keys.
                            SelectMany(i => ModelState[i].Errors).
                            Select(m => m.ErrorMessage).ToArray() 
        }); 
    } 

    // GET: Home/About 
    public ActionResult About() 
    { 
        return View(); 
    }
} 


Kết quả
Nếu bạn quan tâm
Phần tiếp theo chúng ta sẽ tạo một ứng dụng mẫu với AngularJS để thực hiện CRUD với ASP.NET MVC.
Share on Google Plus

About Practice makes perfect

Chia sẻ cũng là một cách để học được nhiều hơn.
Có công mài sắt có ngày nên kim - Practice makes perfect
    Blogger Comment

0 nhận xét:

Đăng nhận xét