Phát triển ứng dụng AngularJS với bộ mô-đun động


81

Tôi có một ứng dụng có bố cục phức tạp, nơi người dùng có thể đặt (kéo / thả) tiện ích con (bằng cách chọn từ một tập hợp hơn 100 tiện ích con được xác định trước) trong đó mọi tiện ích con là một triển khai tùy chỉnh hiển thị một tập hợp dữ liệu (được tìm nạp bằng lệnh gọi REST) theo một cách cụ thể. Tôi đã đọc rất nhiều bài đăng trên blog, câu hỏi về stackoverflow và tài liệu AngularJS chính thức nhưng tôi không thể tìm ra cách tôi nên thiết kế ứng dụng của mình để xử lý các yêu cầu đó. Nhìn vào các ứng dụng demo, có một mô-đun duy nhất (ng-app) và khi xây dựng nó trong tệp .js, các mô-đun phụ thuộc được khai báo là phụ thuộc của nó, tuy nhiên, tôi có một bộ lớn các tiện ích và bằng cách nào đó, không nên mô tả tất cả chúng ở đó. Tôi cần gợi ý cho những câu hỏi sau:

  • Tôi nên thiết kế ứng dụng và tiện ích của mình như thế nào - tôi nên có một mô-đun AngularJS riêng biệt hay mỗi tiện ích phải là một chỉ thị cho mô-đun chính?
  • Nếu tôi thiết kế widget của mình dưới dạng chỉ thị, có cách nào để xác định sự phụ thuộc trong một chỉ thị không. Tức là chỉ thị của tôi sử dụng ng-calender trong việc triển khai nó?
  • Nếu tôi thiết kế mỗi tiện ích con dưới dạng một mô-đun riêng biệt, có cách nào để thêm động mô-đun tiện ích con làm phụ thuộc vào mô-đun chính không?
  • Tôi nên thiết kế bộ điều khiển như thế nào - có lẽ một bộ điều khiển cho mỗi widget?
  • Làm cách nào để tách trạng thái (phạm vi) nếu tôi có nhiều tiện ích con từ cùng một loại trong chế độ xem?
  • Có những phương pháp hay nhất để thiết kế các widget có thể tái sử dụng với AngularJS không?

BIÊN TẬP

Tham khảo hữu ích:


1
Suy nghĩ đầu tiên của tôi là tạo mỗi widget dưới dạng một cặp tệp HTML / JS riêng biệt, được hiển thị qua ng-include. Mẹo là chỉ tải các tệp JS bộ điều khiển cần thiết.
Shmiddty

Tôi có thể thêm động html có chứa ng-include không?
Adrian Mitev

1
Tôi cũng tin như thế. Trong bộ điều khiển chính của bạn, bạn sẽ có một bộ sưu tập xác định tiện ích con nào đang hoạt động, trong phần đánh dấu cho bộ sưu tập, bạn sẽ có liên kết ng-include với một số thuộc tính trỏ đến HTML src xem của tiện ích con. Tôi khá chắc chắn rằng điều này hiệu quả, nhưng tôi chưa thực sự làm điều gì đó như thế này.
Shmiddty

Câu trả lời:


61

Đây chỉ là những lời khuyên chung chung.

Tôi nên thiết kế ứng dụng và tiện ích của mình như thế nào - tôi nên có một mô-đun AngularJS riêng biệt hay mỗi tiện ích phải là một chỉ thị cho mô-đun chính?

Bạn đang nói về một loạt các widget, có vẻ tự nhiên khi chia chúng thành nhiều mô-đun. Một số tiện ích có thể có nhiều điểm chung hơn các tiện ích khác. Một số có thể rất chung chung và phù hợp với các dự án khác, số khác thì cụ thể hơn.

Nếu tôi thiết kế widget của mình dưới dạng chỉ thị, có cách nào để xác định sự phụ thuộc trong một chỉ thị không. Tức là chỉ thị của tôi sử dụng ng-calender trong việc triển khai nó?

Sự phụ thuộc vào các mô-đun khác được thực hiện ở cấp độ mô-đun, nhưng không có vấn đề gì nếu mô-đun Aphụ thuộc vào mô-đun Bvà cả hai ABphụ thuộc vào mô-đun C. Directives là một lựa chọn tự nhiên để tạo widget trong Angular. Nếu một chỉ thị phụ thuộc vào một chỉ thị khác, bạn có thể xác định chúng trong cùng một mô-đun hoặc tạo ra sự phụ thuộc trên một mức mô-đun.

Nếu tôi thiết kế mỗi tiện ích con dưới dạng một mô-đun riêng biệt, có cách nào để thêm động mô-đun tiện ích con làm phụ thuộc vào mô-đun chính không?

Tôi không chắc tại sao bạn lại muốn làm điều này và tôi không chắc cách thực hiện. Các chỉ thị và dịch vụ không được khởi tạo trước khi chúng được sử dụng trong Angular. Nếu bạn có một thư viện khổng lồ các chỉ thị (widget) và biết rằng bạn có thể sẽ sử dụng một số trong số chúng, nhưng không phải tất cả chúng - nhưng bạn không biết cái nào sẽ được sử dụng khi ứng dụng được khởi tạo, bạn thực sự có thể "lười biếng tải "các chỉ thị của bạn sau khi mô-đun của bạn đã được tải. Tôi đã tạo một ví dụ ở đây

Lợi ích là bạn có thể tải ứng dụng của mình nhanh chóng ngay cả khi bạn có nhiều mã, bởi vì bạn không phải tải các tập lệnh trước khi cần chúng. Điểm bất lợi là có thể có độ trễ đáng kể trong lần đầu tiên một chỉ thị mới được tải.

Tôi nên thiết kế bộ điều khiển như thế nào - có lẽ một bộ điều khiển cho mỗi widget?

Một widget có thể sẽ cần bộ điều khiển riêng. Các bộ điều khiển thường phải nhỏ, nếu chúng lớn, bạn có thể cân nhắc xem có chức năng nào phù hợp hơn trong một dịch vụ hay không.

Làm cách nào để tách trạng thái (phạm vi) nếu tôi có nhiều tiện ích con từ cùng một loại trong chế độ xem?

Các widget cần biến phạm vi chắc chắn phải có phạm vi riêng biệt của chúng ( scope:{ ... }trong cấu hình chỉ thị).

Có những phương pháp hay nhất để thiết kế các widget có thể tái sử dụng với AngularJS không?

Cô lập phạm vi, giữ các phụ thuộc ở mức tối thiểu cần thiết. Xem video của Misko về các phương pháp hay nhất trong Angular

Brian Ford cũng đã viết một bài báo về việc viết một ứng dụng khổng lồ trong Angular


Cảm ơn bạn vì câu trả lời tuyệt vời!
Adrian Mitev

17

Câu hỏi này cũng rất quan trọng đối với tôi. Trang chủ AngularJS có một vài ví dụ trên đó (bạn có thể gọi chúng là các widget) vì vậy tôi đã sử dụng mã nguồn của họ để thử và xem cách họ phân tách các widget của mình.

Đầu tiên, họ không bao giờ khai báo thuộc tính "ng-app". Họ dùng

function bootstrap() {
      if (window.prettyPrint && window.$ && $.fn.popover && angular.bootstrap &&
          hasModule('ngLocal.sk') && hasModule('ngLocal.us') && hasModule('homepage') && hasModule('ngResource')) {
            $(function(){
              angular.bootstrap(document, ['homepage', 'ngLocal.us']);
            });
      }
    }

để đảm bảo mọi thứ được tải chính xác. Ý tưởng gọn gàng, nhưng thật kỳ lạ khi họ đẩy thuộc tính ng-app vào bạn quá nhiều sau đó thậm chí không sử dụng nó. Dù sao thì đây cũng là mô-đun trang chủ mà họ tải bằng ứng dụng - http://angularjs.org/js/homepage.js

Trong đó có một chỉ thị gọi là appRun

  .directive('appRun', function(fetchCode, $templateCache, $browser) {
    return {
      terminal: true,
      link: function(scope, element, attrs) {
        var modules = [];

        modules.push(function($provide, $locationProvider) {
          $provide.value('$templateCache', {
            get: function(key) {
              var value = $templateCache.get(key);
              if (value) {
                value = value.replace(/\#\//mg, '/');
              }
              return value;
            }
          });
          $provide.value('$anchorScroll', angular.noop);
          $provide.value('$browser', $browser);
          $locationProvider.html5Mode(true);
          $locationProvider.hashPrefix('!');
        });
        if (attrs.module) {
          modules.push(attrs.module);
        }

        element.html(fetchCode(attrs.appRun));
        element.bind('click', function(event) {
          if (event.target.attributes.getNamedItem('ng-click')) {
            event.preventDefault();
          }
        });
        angular.bootstrap(element, modules);
      }
    };
  })

Tôi sẽ sử dụng danh sách Việc cần làm làm ví dụ. Đối với html, họ có

<div app-run="todo.html" class="well"></div>

và sau đó ở cuối trang, họ có

<script type="text/ng-template" id="todo.html">
  <h2>Todo</h2>
  <div ng-controller="TodoCtrl">
    <span>{{remaining()}} of {{todos.length}} remaining</span>
    [ <a href="" ng-click="archive()">archive</a> ]
    <ul class="unstyled">
      <li ng-repeat="todo in todos">
        <input type="checkbox" ng-model="todo.done">
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
    <form ng-submit="addTodo()">
      <input type="text" ng-model="todoText"  size="30"
             placeholder="add new todo here">
      <input class="btn-primary" type="submit" value="add">
    </form>
  </div>
</script>

Họ cũng có

<style type="text/css" id="todo.css"> //style stuff here </style>
<script id="todo.js"> //controller stuff here </script>

Mã được sử dụng, nhưng các thuộc tính id trên các tập lệnh đó không quan trọng để chạy ứng dụng. Đó chỉ là phần hiển thị mã nguồn ở bên trái của ứng dụng.

Về cơ bản, chúng có một chỉ thị được gọi là appRun sử dụng một hàm fetchCode

  .factory('fetchCode', function(indent) {
    return function get(id, spaces) {
      return indent(angular.element(document.getElementById(id)).html(), spaces);
    }
  })

để tìm nạp mã. Sau đó, họ sử dụng angle.bootstrap () để tạo một ứng dụng mới. Họ cũng có thể tải các mô-đun thông qua ứng dụng chạy. Ví dụ Dự án JavaScript được khởi tạo như

<div app-run="project.html" module="project" class="well"></div>

Hy vọng rằng điều này sẽ giúp. Tôi vẫn không chắc kỹ thuật "tốt nhất" là gì, nhưng có vẻ như trang chủ AngularJS chỉ đơn giản sử dụng một ứng dụng góc hoàn toàn riêng biệt (ng-app) cho mỗi ví dụ / widget. Tôi nghĩ tôi cũng sẽ làm như vậy, ngoại trừ việc thay đổi hàm fetchCode để tải nội dung với AJAX.


3
+1 cho điều này. Tôi có một câu hỏi tương tự ở đây tôi nghĩ rằng bạn có thể có thể giúp đỡ với: stackoverflow.com/questions/17557088/...
Dan Kanze

bạn đã chỉ cho tôi đúng hướng. Đối với bất cứ ai quan tâm đến chức năng hasModule Tôi sẽ để lại thực hiện của tôi ở đây:function hasModule(name) { try { angular.module(name); } catch(err) { return false; } return true; }
Asier Paz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.