Kết hợp bộ điều khiển thực thi AngularJS hai lần


532

Tôi hiểu AngularJS chạy qua một số mã hai lần, đôi khi nhiều hơn, như $watch các sự kiện, liên tục kiểm tra trạng thái mô hình, v.v.

Tuy nhiên mã của tôi:

function MyController($scope, User, local) {

var $scope.User = local.get(); // Get locally save user data

User.get({ id: $scope.User._id.$oid }, function(user) {
  $scope.User = new User(user);
  local.save($scope.User);
});

//...

Được thực hiện hai lần, chèn 2 bản ghi vào DB của tôi. Tôi rõ ràng vẫn đang học vì tôi đã đập đầu vào điều này từ lâu!


104
Nếu bộ điều khiển của bạn đang chạy hai lần thì hãy kiểm tra xem bạn có đang khởi tạo ứng dụng Angular của mình hai lần không (bằng cách khởi chạy tự động với ng-appvà với bootstrap thủ công). Đồng thời kiểm tra xem bạn đã gắn bộ điều khiển của mình vào nhiều thành phần chưa (với ng-controller).
Stewie

7
bạn có thể giải thích những gì bạn có nghĩa là "và với bootstrap thủ công" không?
thể thao


2
Tôi nhận thấy hành vi của bộ điều khiển trùng lặp trong một ứng dụng mà tôi đã thừa hưởng khi khắc phục sự cố khi ghi nhật ký và thấy bảng điều khiển ghi nhật ký hai lần. Ngọn lửa đăng nhập đầu tiên có một giá trị, nhưng lần thứ hai không được xác định. Sau khi loại bỏ chỉ thị ng-bộ điều khiển HTML cho bộ điều khiển, nhật ký giao diện điều khiển thứ hai không được xác định sẽ biến mất.
Daniel Nalbach

2
nếu angular.js được thêm hai lần, thì điều này cũng có thể xảy ra
Mohit

Câu trả lời:


1050

Bộ định tuyến ứng dụng đã chỉ định điều hướng để MyControllerthích như vậy:

$routeProvider.when('/',
                   { templateUrl: 'pages/home.html',
                     controller: MyController });

Nhưng tôi cũng có cái này trong home.html:

<div data-ng-controller="MyController">

Điều này tiêu hóa bộ điều khiển hai lần. Xóa data-ng-controllerthuộc tính khỏi HTML đã giải quyết vấn đề. Ngoài ra, thuộc controller:tính có thể đã bị xóa khỏi chỉ thị định tuyến.

Vấn đề này cũng xuất hiện khi sử dụng điều hướng theo thẻ. Ví dụ: app.jscó thể chứa:

  .state('tab.reports', {
    url: '/reports',
    views: {
      'tab-reports': {
        templateUrl: 'templates/tab-reports.html',
        controller: 'ReportsCtrl'
      }
    }
  })

Tab báo cáo tương ứng HTML có thể giống:

<ion-view view-title="Reports">
  <ion-content ng-controller="ReportsCtrl">

Điều này cũng sẽ dẫn đến việc chạy bộ điều khiển hai lần.


4
Đây có vẻ là nơi tốt nhất để rời khỏi đây, sau nhiều giờ chải mã, đừng hỏi tôi làm thế nào, nhưng khi tôi đổi <div ng-view>...sang <div class="ng-view">..., vấn đề này đã dừng lại với tôi. Tôi chưa từng bắt gặp hành vi này trước đây, nhưng có lẽ ai đó cũng có hành vi này.
Phix

5
Có một lời giải thích tốt về điều này trong các tài liệu cho ngContoder, docs.angularjs.org/api/ng/directive/ngControll
Charles

1
Có cách nào để khai báo một bộ điều khiển trong tuyến đường, và sau đó một bộ điều khiển khác trên chính trang html không? Làm thế nào để đảm bảo cả hai công trình và không có xung đột? Hay điều này là không cần thiết?
Người suy nghĩ

1
Nó có lẽ là không cần thiết, có thể sử dụng một vài chỉ thị thay thế?
Greg

2
@Josh để nó trong HTML làm giảm tính linh hoạt. Bạn đang buộc mẫu của bạn trực tiếp vào một bộ điều khiển. Bạn có thể sử dụng ctrl như cú pháp với định tuyến.
Greg

127

Tài liệu AngularJS - ngContoder
Lưu ý rằng bạn cũng có thể đính kèm bộ điều khiển vào DOM bằng cách khai báo nó trong định nghĩa tuyến thông qua dịch vụ $ route. Một lỗi phổ biến là khai báo lại bộ điều khiển bằng cách sử dụng bộ điều khiển ng trong chính khuôn mẫu. Điều này sẽ khiến bộ điều khiển được gắn và thực hiện hai lần.

Khi bạn sử dụng ngRoute với ng-viewchỉ thị, bộ điều khiển sẽ được gắn vào phần tử dom đó theo mặc định (hoặc ui-view nếu bạn sử dụng ui-router). Vì vậy, bạn sẽ không cần phải đính kèm lại trong mẫu.


Anwser trùng lặp. Các tác giả đã trả lời nói như vậy. Chỉ cần cuộn xuống cho đến cuối trang.
Iago

4
Tôi thấy khó hiểu khi tác giả đặt nó như một ghi chú bên trong khi đó rõ ràng là cách chính xác để làm điều đó. Các trích dẫn từ các tài liệu trả lời câu hỏi rõ ràng. Và tôi chắc chắn nhiều người sẽ tìm thấy liên kết đến tài liệu hữu ích.
shxfee

Điều này giải quyết vấn đề của tôi về bộ điều khiển được thực hiện hai lần. Tất cả những gì tôi đã làm là loại bỏ bộ điều khiển ng trong mẫu và bây giờ nó chỉ thực hiện một lần.
torbenrudgaard

70

Tôi vừa trải qua điều này, nhưng vấn đề khác với câu trả lời được chấp nhận. Tôi thực sự để điều này ở đây cho bản thân tương lai của tôi, bao gồm các bước tôi đã trải qua để khắc phục nó.

  1. Xóa các khai báo bộ điều khiển dự phòng
  2. Kiểm tra dấu gạch chéo trong các tuyến đường
  3. Kiểm tra ng-ifs
  4. Kiểm tra bất kỳ ng-viewcuộc gọi gói không cần thiết nào (tôi vô tình để lại một cuộc gọi ng-viewđang thực sự bao bọcng-view kết thúc. Điều này dẫn đến ba cuộc gọi đến bộ điều khiển của tôi.)
  5. Nếu bạn đang ở trên Rails, bạn nên xóa turbolinksđá quý khỏi application.jstệp của mình . Tôi đã lãng phí cả một ngày để khám phá điều đó. Tìm thấy câu trả lời ở đây .
  6. Khởi tạo ứng dụng hai lần với ng-app và với bootstrap. Kết hợp bộ điều khiển thực thi AngularJS hai lần
  7. Khi sử dụng $ compile trên toàn bộ phần tử trong hàm 'link'-hàm của lệnh cũng có bộ điều khiển riêng được xác định và sử dụng hàm gọi lại của bộ điều khiển này trong mẫu thông qua ng-click, v.v. Tìm thấy câu trả lời ở đây .

5. nếu bạn đang ở trên Rails, bạn nên xóa turbolinksgem khỏi application.jstệp của mình . Điều đó làm tôi phát điên, lãng phí cả ngày để khám phá điều đó. Nỗi đau thực sự. Tìm thấy câu trả lời ở đây
18augst

6. Khởi tạo ứng dụng hai lần với ng-app và với bootstrap. stackoverflow.com/questions/15535336/ cường
thadeuszlay

1
Cảm ơn . Đối với tôi, điều làm việc là đặt dấu gạch chéo ở cuối tuyến
Pramod Sharma

Cảm ơn một triệu, đã phá vỡ đầu của tôi về điều này. kết thúc là số 7! (luôn luôn là người cuối cùng ...).
Rabbi Shuki Gur

Wow, tin hay không, tôi đã xem qua mọi thứ trong danh sách của bạn và tôi vẫn gặp vấn đề.
Jacob Stamm

14

Chỉ muốn thêm một trường hợp nữa khi bộ điều khiển có thể khởi tạo hai lần (điều này thực tế cho angular.js 1.3.1):

<div ng-if="loading">Loading...</div>
<div ng-if="!loading">
    <div ng-view></div>
</div>

Trong trường hợp này $ route.c hiện sẽ được đặt khi ng-view sẽ init. Điều đó gây ra khởi tạo kép.

Để khắc phục, chỉ cần thay đổi ng-if thành ng-show / ng-hide và tất cả sẽ hoạt động tốt.


Nó giúp tôi, tôi đã sử dụng ng-show / ng-hide thay vì ng-if, tôi có hai ng-view, nó không phải là vấn đề mà là vô hiệu hóa trong khi ng-show / ng-hide không
Dimitri Kopriwa

Cảm ơn rất nhiều. Cách tiếp cận này đã giải quyết vấn đề của tôi. Đã gọi ui-viewhai lần giống nhau mà gọi bộ điều khiển hai lần.
curreggie

7

Muốn thêm để tham khảo:

Việc thực thi mã bộ điều khiển kép cũng có thể được gây ra bằng cách tham chiếu bộ điều khiển trong một lệnh cũng chạy trên trang.

ví dụ

return {

            restrict: 'A',
            controller: 'myController',
            link: function ($scope) { ....

Khi bạn cũng có ng-controller = "myContoder" trong HTML của bạn


Giải pháp cho điều này là gì?
Sagar Bhosale

1
Chỉ sử dụng cái này hay cái khác
gb2d


6

Tôi xé ứng dụng của mình và tất cả các phụ thuộc của nó vào các bit về vấn đề này (chi tiết ở đây: Ứng dụng AngularJS khởi tạo hai lần (đã thử các giải pháp thông thường ..) )

Và cuối cùng, tất cả là lỗi của plugin Batarang Chrome.

Nghị quyết trong câu trả lời này :

Tôi thực sự khuyên bạn nên điều đầu tiên trong danh sách của bất kỳ ai là vô hiệu hóa nó trên mỗi bài đăng trước khi thay đổi mã.


Đây là vấn đề của tôi. Plugin chạy một phiên bản kép của ứng dụng Angular của bạn để nó có thể cung cấp thông tin phạm vi (không biết tại sao nó không thể sử dụng phiên bản ban đầu).
rgdigital

Tôi rất vui vì nó đã giúp, tôi đã mất một thời gian để tìm hiểu điều này.
Lewis

5

Nếu bạn biết bộ điều khiển của mình vô tình thực thi nhiều lần, hãy thử tìm kiếm thông qua các tệp của bạn để biết tên của bộ điều khiển vi phạm, ví dụ: search: MyContoder thông qua tất cả các tệp. Có khả năng nó đã bị sao chép trong một số tệp html / js khác và bạn đã quên thay đổi nó khi bạn phải phát triển hoặc sử dụng các partials / bộ điều khiển đó. Nguồn: Tôi đã phạm sai lầm này


5

Tôi gặp vấn đề tương tự, trong một ứng dụng đơn giản (không có định tuyến và tham chiếu bộ điều khiển ng đơn giản) và hàm tạo của bộ điều khiển của tôi đã chạy hai lần. Cuối cùng, tôi phát hiện ra rằng vấn đề của tôi là tuyên bố sau đây để tự động khởi động ứng dụng AngularJS của tôi trong chế độ xem Dao cạo của tôi

<html ng-app="mTest1">

Tôi cũng đã tự khởi động nó bằng angular.bootstrap tức là

angular.bootstrap(document, [this.app.name]);

Vì vậy, loại bỏ một trong số họ, nó làm việc cho tôi.


4

Trong một số trường hợp, lệnh của bạn chạy hai lần khi bạn chỉ đơn giản là không đóng lại lệnh của bạn như thế này:

<my-directive>Some content<my-directive>

Điều này sẽ chạy chỉ thị của bạn hai lần. Ngoài ra, có một trường hợp khác thường khi lệnh của bạn chạy hai lần:

đảm bảo bạn không bao gồm chỉ thị của mình trong index.html TWICE !


Ngoài ra nếu bạn đang sử dụng Bộ định tuyến UI, việc chỉ định tên lệnh cho chế độ xem ui bên trong một lớp dường như dẫn đến việc thực hiện kép. Thay đổi nó thành E hoặc <ui-view> </ ui-view> khắc phục sự cố thực thi kép.
SteveGSD

2

Đã gãi đầu về vấn đề này với bản dựng RC AngularJS 1.4, sau đó nhận ra không có câu trả lời nào ở trên được áp dụng vì nó có nguồn gốc từ thư viện bộ định tuyến mới cho Angular 1.4 và Angular 2 tại thời điểm viết bài này. Do đó, tôi sẽ bỏ một ghi chú ở đây cho bất kỳ ai có thể đang sử dụng thư viện tuyến đường Angular mới.

Về cơ bản nếu một trang html chứa một lệnh ng-viewportđể tải các phần của ứng dụng của bạn, bằng cách nhấp vào một siêu liên kết được chỉ định trong ng-linksẽ khiến bộ điều khiển đích của thành phần được liên kết được tải hai lần. Sự khác biệt tinh tế là, nếu trình duyệt đã tải bộ điều khiển đích, bằng cách nhấp lại vào cùng một siêu liên kết sẽ chỉ gọi bộ điều khiển một lần.

Chưa tìm thấy cách giải quyết khả thi nào, mặc dù tôi tin rằng hành vi này phù hợp với quan sát của shaunxu và hy vọng vấn đề này sẽ được giải quyết trong bản dựng tương lai của thư viện tuyến mới và cùng với các bản phát hành AngularJS 1.4.


2

Trong trường hợp của tôi, tôi tìm thấy hai khung nhìn bằng cùng một bộ điều khiển.

$stateProvider.state('app', {
  url: '',
  views: {
    "viewOne@app": {
      controller: 'CtrlOne as CtrlOne',
      templateUrl: 'main/one.tpl.html'
    },
    "viewTwo@app": {
      controller: 'CtrlOne as CtrlOne',
      templateUrl: 'main/two.tpl.html'
    }
  }
});

2

Vấn đề tôi gặp phải có thể là tiếp tuyến, nhưng vì việc googling đưa tôi đến câu hỏi này, điều này có thể phù hợp. Vấn đề gây ra cho tôi cái đầu xấu xí khi sử dụng Bộ định tuyến UI, nhưng chỉ khi tôi cố gắng làm mới trang bằng nút làm mới trình duyệt. Ứng dụng sử dụng Bộ định tuyến giao diện người dùng với trạng thái trừu tượng cha mẹ và sau đó con loại bỏ cha mẹ. Trên run()chức năng ứng dụng , có một $state.go('...child-state...')lệnh. Trạng thái cha mẹ sử dụng mộtresolve , và lúc đầu tôi nghĩ có lẽ một trình điều khiển con đang thực thi hai lần.

Mọi thứ đều ổn trước khi URL được thêm vào hàm băm.
www.someoldwebaddress.org

Sau đó, khi url đã được sửa đổi bởi UI Router,
www.someoldwebaddress.org#/childstate

... Và sau đó khi tôi làm mới trang bằng nút làm mới trình duyệt , sẽ $stateChangeStartkích hoạt hai lần và mỗi lần chỉ vào childstate.

Các resolvevề tình trạng cha mẹ là những gì đang bắn hai lần.

Có lẽ đây là một loại bùn; bất kể, điều này dường như loại bỏ vấn đề đối với tôi: trong khu vực mã $stateProviderđược gọi lần đầu tiên , trước tiên hãy kiểm tra xem window.location.hash có phải là một chuỗi rỗng không. Nếu có, tất cả đều tốt; nếu không, thì đặt window.location.hash thành một chuỗi rỗng. Sau đó, có vẻ như $statechỉ cố gắng đi đâu đó một lần chứ không phải hai lần.

Ngoài ra, nếu bạn không muốn dựa vào mặc định của ứng dụng runstate.go(...), bạn có thể thử chụp giá trị băm và sử dụng giá trị băm để xác định trạng thái con bạn đang ở ngay trước khi làm mới trang và thêm một điều kiện vào khu vực trong mã nơi bạn đặt state.go(...).


1

Trong trường hợp của tôi, đó là do mẫu url tôi đã sử dụng

url của tôi giống như / ui / project /: tham số1 /: tham số2.

Tôi không cần paramerter2 trong tất cả các trường hợp thay đổi trạng thái. Trong trường hợp tôi không cần thứ hai tham số url của tôi sẽ như thế nào / ui / dự án /: argument1 /. Và vì vậy bất cứ khi nào tôi đã có một sự thay đổi trạng thái tôi sẽ phải điều khiển của tôi làm mới hai lần.

Giải pháp là đặt tham số2 dưới dạng chuỗi rỗng và thay đổi trạng thái.


1

Tôi đã có sự khởi tạo kép này xảy ra vì một lý do khác. Đối với một số chuyển đổi tuyến trong ứng dụng của tôi, tôi muốn buộc cuộn đến gần đầu trang (ví dụ: trong kết quả tìm kiếm được phân trang ... nhấp vào tiếp theo sẽ đưa bạn đến đầu trang 2).

Tôi đã làm điều này bằng cách thêm một trình lắng nghe vào $rootScope $on $viewContentLoadedđó (dựa trên các điều kiện nhất định) được thực thi

$location.hash('top');

Vô tình điều này đã khiến các tuyến đường của tôi được đánh giá lại và các bộ điều khiển được xác định lại



1

Trong trường hợp của tôi, việc đổi tên bộ điều khiển thành một tên khác đã giải quyết vấn đề.

Có một sự xung đột giữa tên bộ điều khiển với mô-đun " angular-ui-tree ": Tôi đã đổi tên bộ điều khiển của mình từ "CatalogerTreeContoder" thành " TreeContoder " và sau đó bộ điều khiển này bắt đầu được bắt đầu hai lần trên trang trong đó sử dụng chỉ thị " ui-tree " lệnh này sử dụng bộ điều khiển có tên " TreeContoder ".


1

Đối với những người sử dụng cú pháp ControllerAs, chỉ cần khai báo nhãn của trình điều khiển trong $ Routeprovider như sau:

$routeprovider
        .when('/link', {
            templateUrl: 'templateUrl',
            controller: 'UploadsController as ctrl'
        })

hoặc là

$routeprovider
        .when('/link', {
            templateUrl: 'templateUrl',
            controller: 'UploadsController'
            controllerAs: 'ctrl'
        })

Sau khi khai báo $ Routeprovider, không cung cấp bộ điều khiển như trong khung nhìn. Thay vào đó sử dụng nhãn trong chế độ xem.


0

Tôi đã có cùng một vấn đề và sau khi thử tất cả các câu trả lời, cuối cùng tôi thấy rằng tôi đã có một chỉ thị theo quan điểm của tôi bị ràng buộc với cùng một bộ điều khiển.

APP.directive('MyDirective', function() {
  return {
    restrict: 'AE',
    scope: {},
    templateUrl: '../views/quiz.html',
    controller: 'ShowClassController'
}
});

Sau khi gỡ bỏ lệnh, bộ điều khiển dừng được gọi hai lần. Bây giờ câu hỏi của tôi là, làm thế nào có thể sử dụng chỉ thị này ràng buộc với phạm vi điều khiển mà không có vấn đề này?


0

Tôi chỉ giải quyết của tôi, mà thực sự khá thất vọng. Đây là một ứng dụng lai ion, tôi đã sử dụng ui-router v0.2.13. Trong trường hợp của tôi, có một trình đọc epub (sử dụng epub.js) liên tục báo cáo "không tìm thấy tài liệu" khi tôi điều hướng đến thư viện sách của mình và chọn bất kỳ cuốn sách nào khác. Khi tôi tải lại, cuốn sách trình duyệt đã được hiển thị hoàn hảo nhưng khi tôi chọn một cuốn sách khác lại gặp vấn đề tương tự.

Tôi giải quyết rất đơn giản. Tôi chỉ lấy ra reload:truetừ $state.go("app.reader", { fileName: fn, reload: true });trong tôiLibraryController


0

Tôi có cùng một vấn đề angular-route@1.6.7, và nó là do dấu gạch chéo ở cuối tuyến regex:

.when('/goods/publish/:classId/', option)

đến

.when('/goods/publish/:classId', option)

và nó hoạt động chính xác.


0

Chỉ cần thêm trường hợp của tôi ở đây là tốt:

Tôi đã sử dụng angular-ui-router với$state.go('new_state', {foo: "foo@bar"})

Khi tôi đã thêm encodeURIComponent vào tham số, vấn đề đã biến mất : $state.go('new_state', {foo: encodeURIComponent("foo@bar")}).

Chuyện gì đã xảy ra? Nhân vật "@" trong giá trị tham số không được phép trong URL. Do đó, góc-ui-router tạo ra bộ điều khiển của tôi hai lần: trong quá trình tạo đầu tiên nó thông qua bản gốc "foo @ bar", trong quá trình tạo thứ hai nó sẽ vượt qua được mã hóa phiên bản "foo% 40bar". Khi tôi được mã hóa một cách rõ ràng thông số như trình bày ở trên, vấn đề đi đi.


-1

Tôi nhận ra rằng tôi đang được gọi hai lần là vì tôi đã gọi phương thức hai lần từ html của mình.

`<form class="form-horizontal" name="x" ng-submit="findX() novalidate >
 <input type="text"....>
 <input type="text"....>
 <input type="text"....>
 <button type="submit" class="btn btn-sm btn-primary" ng-click="findX()"
</form>`

Phần được tô sáng đã khiến findX () được gọi hai lần. Hy vọng nó sẽ giúp được ai đó.

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.