Menunda perubahan rute AngularJS hingga model dimuat untuk mencegah flicker

321

Saya bertanya-tanya apakah ada cara (mirip dengan Gmail) bagi AngularJS untuk menunda menampilkan rute baru sampai setelah masing-masing model dan datanya diambil menggunakan layanan masing-masing.

Misalnya, jika ada ProjectsControlleryang mencantumkan semua Proyek dan project_index.htmlyang merupakan templat yang menunjukkan Proyek-proyek ini, Project.query()akan diambil sepenuhnya sebelum menampilkan halaman baru.

Sampai saat itu, halaman lama masih akan terus ditampilkan (misalnya, jika saya sedang menelusuri halaman lain dan kemudian memutuskan untuk melihat indeks Proyek ini).

Misko Hevery
sumber

Jawaban:

374

Properti mengatasi $ routeProvider memungkinkan penundaan perubahan rute hingga data dimuat.

Pertama tentukan rute dengan resolveatribut seperti ini.

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: PhoneListCtrl.resolve}).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

perhatikan bahwa resolveproperti didefinisikan pada rute.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {
  phones: function(Phone, $q) {
    // see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
    var deferred = $q.defer();
    Phone.query(function(successData) {
            deferred.resolve(successData); 
    }, function(errorData) {
            deferred.reject(); // you could optionally pass error data here
    });
    return deferred.promise;
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}

Perhatikan bahwa definisi pengontrol berisi objek penyelesaian yang menyatakan hal-hal yang harus tersedia untuk konstruktor pengontrol. Di sini phonesdisuntikkan ke controller dan didefinisikan di resolveproperti.

The resolve.phonesFungsi bertanggung jawab untuk mengembalikan janji. Semua janji dikumpulkan dan perubahan rute ditunda hingga semua janji terselesaikan.

Demo kerja: http://mhevery.github.com/angular-phonecat/app/#/phones Sumber: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

Misko Hevery
sumber
10
@MiskoHevery - bagaimana jika controller Anda ada di dalam modul dan didefinisikan sebagai string daripada fungsi. Bagaimana Anda bisa mengatur atribut ketetapan seperti yang Anda lakukan?
aar0n
53
Bagaimana ini digunakan dalam angular.controller()definisi tipe controller? Dalam $routeProviderhal ini, saya pikir Anda harus menggunakan nama string pengontrol.
Ben Lesh
6
Adakah contoh menggunakan angular.controller () dan dengan versi terbaru AngularJS?
Laurent
22
@blesh, ketika Anda menggunakan angular.controller(), Anda dapat menetapkan hasil fungsi ini ke variabel ( var MyCtrl = angular.controller(...)) dan kemudian bekerja dengan itu lebih lanjut ( MyCtrl.loadData = function(){..}). Lihat video egghead
petrkotek
17
Saya masih ingin cara yang baik untuk dilakukan tanpa menempatkan pengontrol Anda di global. Saya tidak ingin membuang sampah ke seluruh tempat. Anda dapat melakukannya dengan konstanta, tetapi alangkah baiknya untuk dapat menempatkan fungsi resol pada / di controller, bukan di tempat lain.
Erik Honn
51

Berikut adalah contoh kerja minimal yang berfungsi untuk Angular 1.0.2

Templat:

<script type="text/ng-template" id="/editor-tpl.html">
    Editor Template {{datasets}}
</script>

<div ng-view>

</div>

JavaScript:

function MyCtrl($scope, datasets) {    
    $scope.datasets = datasets;
}

MyCtrl.resolve = {
    datasets : function($q, $http) {
        var deferred = $q.defer();

        $http({method: 'GET', url: '/someUrl'})
            .success(function(data) {
                deferred.resolve(data)
            })
            .error(function(data){
                //actually you'd want deffered.reject(data) here
                //but to show what would happen on success..
                deferred.resolve("error value");
            });

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​

http://jsfiddle.net/dTJ9N/3/

Versi efisien:

Karena $ http () sudah mengembalikan janji (alias ditangguhkan), kami sebenarnya tidak perlu membuat sendiri. Jadi kita bisa menyederhanakan MyCtrl. memutuskan untuk:

MyCtrl.resolve = {
    datasets : function($http) {
        return $http({
            method: 'GET', 
            url: 'http://fiddle.jshell.net/'
        });
    }
};

Hasil $ http () berisi data , status , header dan objek konfigurasi , jadi kita perlu mengubah tubuh MyCtrl menjadi:

$scope.datasets = datasets.data;

http://jsfiddle.net/dTJ9N/5/

mb21
sumber
Saya mencoba melakukan sesuatu seperti ini tetapi mengalami masalah dengan menyuntikkan 'dataset' karena tidak didefinisikan. Adakah pikiran?
Rob Bygrave
Hei mb21, saya pikir Anda mungkin dapat membantu saya dengan pertanyaan ini: stackoverflow.com/questions/14271713/…
winduptoy
Bisakah seseorang membantu saya mengubah jawaban ini ke format app.controller ('MyCtrl')? jsfiddle.net/5usya/1 tidak bekerja untuk saya.
user1071182
saya mendapatkan kesalahan:Unknown provider: datasetsProvider <- datasets
chovy
Anda dapat membuat jawaban Anda lebih sederhana dengan mengganti dataset dengan ini:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Morteza Tourani
32

Saya melihat beberapa orang bertanya bagaimana melakukan ini menggunakan metode angular.controller dengan injeksi dependensi ramah minification. Karena saya baru mendapatkan pekerjaan ini, saya merasa berkewajiban untuk kembali dan membantu. Inilah solusi saya (diadopsi dari pertanyaan awal dan jawaban Misko):

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: { 
            phones: ["Phone", "$q", function(Phone, $q) {
                var deferred = $q.defer();
                Phone.query(function(successData) {
                  deferred.resolve(successData); 
                }, function(errorData) {
                  deferred.reject(); // you could optionally pass error data here
                });
                return deferred.promise;
             ]
            },
            delay: ["$q","$defer", function($q, $defer) {
               var delay = $q.defer();
               $defer(delay.resolve, 1000);
               return delay.promise;
              }
            ]
        },

        }).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

angular.controller("PhoneListCtrl", [ "$scope", "phones", ($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}]);

Karena kode ini berasal dari pertanyaan / jawaban yang paling populer, kode ini tidak diuji, tetapi harus mengirim Anda ke arah yang benar jika Anda sudah mengerti cara membuat kode sudut ramah minifikasi. Satu bagian yang tidak saya perlukan adalah kode injeksi "Telepon" ke fungsi tekad untuk 'ponsel', saya juga tidak menggunakan objek 'penundaan' sama sekali.

Saya juga merekomendasikan video youtube ini http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , yang membantu saya sedikit

Jika Anda tertarik, saya memutuskan untuk juga menempelkan kode saya sendiri (Ditulis dalam coffeescript) sehingga Anda dapat melihat bagaimana saya membuatnya berfungsi.

FYI, sebelumnya saya menggunakan pengontrol generik yang membantu saya melakukan CRUD pada beberapa model:

appModule.config ['$routeProvider', ($routeProvider) ->
  genericControllers = ["boards","teachers","classrooms","students"]
  for controllerName in genericControllers
    $routeProvider
      .when "/#{controllerName}/",
        action: 'confirmLogin'
        controller: 'GenericController'
        controllerName: controllerName
        templateUrl: "/static/templates/#{controllerName}.html"
        resolve:
          items : ["$q", "$route", "$http", ($q, $route, $http) ->
             deferred = $q.defer()
             controllerName = $route.current.controllerName
             $http(
               method: "GET"
               url: "/api/#{controllerName}/"
             )
             .success (response) ->
               deferred.resolve(response.payload)
             .error (response) ->
               deferred.reject(response.message)

             return deferred.promise
          ]

  $routeProvider
    .otherwise
      redirectTo: '/'
      action: 'checkStatus'
]

appModule.controller "GenericController", ["$scope", "$route", "$http", "$cookies", "items", ($scope, $route, $http, $cookies, items) ->

  $scope.items = items
      #etc ....
    ]
bitwit
sumber
Apakah saya menyimpulkan dengan benar dari contoh Anda dan upaya saya yang gagal bahwa sekarang tidak mungkin untuk merujuk resolvefungsi di controller, dalam versi terbaru Angular? Jadi itu harus dinyatakan tepat di konfigurasi seperti di sini?
XML
@XMLilley Saya cukup yakin itu masalahnya. Contoh ini dari 1.1.2 ketika saya menulisnya, saya percaya. Saya tidak melihat dokumentasi tentang menempatkan tekad di dalam controller
bitwit
2
Keren Terimakasih. Ada banyak contoh melakukannya di SO (seperti dua teratas di sini), tetapi mereka semua dari 2012 dan awal 2013. Ini adalah pendekatan yang elegan, tetapi tampaknya sudah usang. Alternatif terbersih sekarang tampaknya menulis layanan individual yang merupakan objek janji.
XML
Terima kasih, ini berhasil untuk saya. Untuk siapa pun yang mendapatkan kesalahan tentang $deferlayanan yang tidak ditentukan , harap perhatikan bahwa dalam versi 1.5.7 dari AngularJS, Anda ingin menggunakannya $timeoutsebagai gantinya.
racl101
18

Komit ini , yang merupakan bagian dari versi 1.1.5 dan di atasnya, memperlihatkan $promiseobjek $resource. Versi ngResource termasuk komit ini memungkinkan penyelesaian sumber daya seperti ini:

$ routeProvider

resolve: {
    data: function(Resource) {
        return Resource.get().$promise;
    }
}

pengontrol

app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {

    $scope.data = data;

}]);
Maximilian Hoffmann
sumber
Versi mana yang termasuk komit itu?
XML
Versi tidak stabil terbaru (1.1.5) termasuk komit ini. ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Maximilian Hoffmann
Saya suka pendekatan yang kurang bertele-tele ini. Akan menyenangkan untuk membuat janji dari objek data aktual dan mengirimkannya secara langsung, tetapi ini adalah kode yang sangat sedikit sehingga berfungsi dengan baik.
Sam Barnum
1
Bagaimana sumber daya mengakses $ routeParams? Misalnya: di GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();Saya tidak bisa menggunakan seperti ini
zeronone
2
@zeronone Anda menyuntikkan $routeke tekad dan penggunaan Anda $route.current.params. Hati-hati, $routeParamsmasih menunjuk ke rute lama.
Brice Stacey
16

Cuplikan ini ramah injeksi ketergantungan (saya bahkan menggunakannya dalam kombinasi ngmin dan uglify ) dan ini adalah penggerak domain yang lebih elegan solusi berbasis .

Contoh di bawah ini mendaftarkan sumber daya Phone dan PhoneRout yang konstan , yang berisi semua informasi perutean Anda untuk domain (telepon) itu. Sesuatu yang saya tidak suka dalam jawaban yang diberikan adalah lokasi dari logika tekad - modul utama tidak boleh tahu apa-apa atau merasa terganggu tentang cara argumen sumber daya diberikan ke controller. Dengan cara ini, logika tetap berada di domain yang sama.

Catatan: jika Anda menggunakan ngmin (dan jika tidak: Anda seharusnya), Anda hanya perlu menulis fungsi ketetapan dengan konvensi array DI.

angular.module('myApp').factory('Phone',function ($resource) {
  return $resource('/api/phone/:id', {id: '@id'});
}).constant('phoneRoutes', {
    '/phone': {
      templateUrl: 'app/phone/index.tmpl.html',
      controller: 'PhoneIndexController'
    },
    '/phone/create': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        phone: ['$route', 'Phone', function ($route, Phone) {
          return new Phone();
        }]
      }
    },
    '/phone/edit/:id': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        form: ['$route', 'Phone', function ($route, Phone) {
          return Phone.get({ id: $route.current.params.id }).$promise;
        }]
      }
    }
  });

Bagian selanjutnya adalah menyuntikkan data perutean ketika modul berada dalam status configure dan menerapkannya ke $ routeProvider .

angular.module('myApp').config(function ($routeProvider, 
                                         phoneRoutes, 
                                         /* ... otherRoutes ... */) {

  $routeProvider.when('/', { templateUrl: 'app/main/index.tmpl.html' });

  // Loop through all paths provided by the injected route data.

  angular.forEach(phoneRoutes, function(routeData, path) {
    $routeProvider.when(path, routeData);
  });

  $routeProvider.otherwise({ redirectTo: '/' });

});

Menguji konfigurasi rute dengan pengaturan ini juga cukup mudah:

describe('phoneRoutes', function() {

  it('should match route configuration', function() {

    module('myApp');

    // Mock the Phone resource
    function PhoneMock() {}
    PhoneMock.get = function() { return {}; };

    module(function($provide) {
      $provide.value('Phone', FormMock);
    });

    inject(function($route, $location, $rootScope, phoneRoutes) {
      angular.forEach(phoneRoutes, function (routeData, path) {

        $location.path(path);
        $rootScope.$digest();

        expect($route.current.templateUrl).toBe(routeData.templateUrl);
        expect($route.current.controller).toBe(routeData.controller);
      });
    });
  });
});

Anda dapat melihatnya dalam kemuliaan penuh dalam percobaan terbaru saya (yang akan datang) . Walaupun metode ini bekerja dengan baik untuk saya, saya benar-benar bertanya-tanya mengapa $ injector tidak menunda pembangunan apa pun ketika mendeteksi injeksi apa pun yang merupakan objek janji ; itu akan membuat segalanya jadi lebih mudah.

Sunting: digunakan Angular v1.2 (rc2)

batal
sumber
2
Jawaban yang sangat bagus ini tampaknya jauh lebih sesuai dengan filosofi "Angular" (enkapsulasi, dll.). Kita semua harus melakukan upaya sadar untuk menghentikan logika dari merayapi seluruh basis kode seperti kudzu.
zakdances
I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectSaya menduga mereka menghilangkan fungsi ini karena mungkin mendorong pola desain yang berdampak negatif terhadap respons aplikasi. Aplikasi ideal dalam pikiran mereka adalah aplikasi yang benar-benar tidak sinkron, jadi penyelesaian harus menjadi kasus tepi.
zakdances
11

Menunda menunjukkan rute pasti akan mengarah ke kusut asinkron ... mengapa tidak hanya melacak status pemuatan entitas utama Anda dan menggunakannya dalam tampilan. Misalnya di controller Anda, Anda mungkin menggunakan callback sukses dan kesalahan pada ngResource:

$scope.httpStatus = 0; // in progress
$scope.projects = $resource.query('/projects', function() {
    $scope.httpStatus = 200;
  }, function(response) {
    $scope.httpStatus = response.status;
  });

Maka dalam tampilan Anda bisa melakukan apa saja:

<div ng-show="httpStatus == 0">
    Loading
</div>
<div ng-show="httpStatus == 200">
    Real stuff
    <div ng-repeat="project in projects">
         ...
    </div>
</div>
<div ng-show="httpStatus >= 400">
    Error, not found, etc. Could distinguish 4xx not found from 
    5xx server error even.
</div>
jpsimons
sumber
6
Mungkin mengekspos status HTTP ke tampilan tidak benar, lebih daripada berurusan dengan kelas CSS dan elemen DOM termasuk dalam controller. Saya mungkin akan menggunakan ide yang sama tetapi status abstrak di isValid () dan isLoaded ().
jpsimons
1
Ini memang bukan pemisahan terbaik kekhawatiran, ditambah bahwa itu akan macet jika Anda memiliki pengendali bersarang yang bergantung pada objek tertentu.
null
Ini cukup pintar .. untuk mengekspos kode status ke tampilan, Anda bisa menempelkan logika http di properti scope di dalam controller, kemudian mengikatnya. Juga jika Anda membuat beberapa panggilan ajax yang terjadi di latar belakang, Anda tetap ingin melakukan ini.
KingOfHypocrites
Ini akan baik-baik saja jika masalahnya hanya menunda pandangan. Tapi tekad paling baik digunakan jika Anda perlu menunda instantiasi controller - bukan hanya tampilan. (Mis: Jika Anda perlu memastikan JSON Anda dimuat karena pengontrol Anda meneruskannya ke arahan sebelum ditransfer.) Dari dokumen: "router akan menunggu semuanya diselesaikan atau yang akan ditolak sebelum pengontrolnya dipakai ".
Dan
8

Saya bekerja dari kode Misko di atas dan inilah yang telah saya lakukan dengannya. Ini adalah solusi yang lebih terkini karena $defertelah diubah menjadi $timeout. $timeoutNamun, penggantian akan menunggu periode waktu habis (dalam kode Misko, 1 detik), lalu kembalikan data dengan harapan waktu diselesaikan. Dengan cara ini, ia kembali secepatnya.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {

  phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
  }
}
Adil
sumber
7

Menggunakan AngularJS 1.1.5

Memperbarui fungsi 'ponsel' dalam jawaban Justen menggunakan sintaks AngularJS 1.1.5 .

Asli:

phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
}

Diperbarui:

phones: function(Phone) {
    return Phone.query().$promise;
}

Jauh lebih singkat berkat tim dan kontributor Angular. :)

Ini juga jawaban Maximilian Hoffmann. Tampaknya komit berhasil masuk ke 1.1.5.

OJ Raqueño
sumber
1
Sepertinya saya tidak dapat menemukan apa pun $promisedi dokumen . Mungkin sudah dikeluarkan pada v2.0 +.
zakdances
Ini hanya tersedia di 1.2
Thomas
5

Anda dapat menggunakan properti penyelesaian $ routeProvider untuk menunda perubahan rute sampai data dimuat.

angular.module('app', ['ngRoute']).
  config(['$routeProvider', function($routeProvider, EntitiesCtrlResolve, EntityCtrlResolve) {
    $routeProvider.
      when('/entities', {
        templateUrl: 'entities.html', 
        controller: 'EntitiesCtrl', 
        resolve: EntitiesCtrlResolve
      }).
      when('/entity/:entityId', {
        templateUrl: 'entity.html', 
        controller: 'EntityCtrl', 
        resolve: EntityCtrlResolve
      }).
      otherwise({redirectTo: '/entities'});
}]);

Perhatikan bahwa resolveproperti ditentukan pada rute.

EntitiesCtrlResolvedan EntityCtrlResolveadalah konstan benda didefinisikan dalam file yang sama EntitiesCtrldan EntityCtrlpengendali.

// EntitiesCtrl.js

angular.module('app').constant('EntitiesCtrlResolve', {
  Entities: function(EntitiesService) {
    return EntitiesService.getAll();
  }
});

angular.module('app').controller('EntitiesCtrl', function(Entities) {
  $scope.entities = Entities;

  // some code..
});

// EntityCtrl.js

angular.module('app').constant('EntityCtrlResolve', {
  Entity: function($route, EntitiesService) {
    return EntitiesService.getById($route.current.params.projectId);
  }
});

angular.module('app').controller('EntityCtrl', function(Entity) {
  $scope.entity = Entity;

  // some code..
});
Lyzanets Bohdan
sumber
3

Saya menyukai ide darkporter karena akan mudah bagi tim dev yang baru di AngularJS untuk memahami dan bekerja langsung.

Saya membuat adaptasi ini yang menggunakan 2 divs, satu untuk loader bar dan satu lagi untuk konten aktual yang ditampilkan setelah data dimuat. Penanganan kesalahan akan dilakukan di tempat lain.

Tambahkan bendera 'siap' ke $ scope:

$http({method: 'GET', url: '...'}).
    success(function(data, status, headers, config) {
        $scope.dataForView = data;      
        $scope.ready = true;  // <-- set true after loaded
    })
});

Dalam tampilan html:

<div ng-show="!ready">

    <!-- Show loading graphic, e.g. Twitter Boostrap progress bar -->
    <div class="progress progress-striped active">
        <div class="bar" style="width: 100%;"></div>
    </div>

</div>

<div ng-show="ready">

    <!-- Real content goes here and will appear after loading -->

</div>

Lihat juga: Boostrap progress bar docs

reggoodwin
sumber
Pisahkan sedikit jika Anda memuat banyak data. Bagaimana Anda tahu jika semuanya dimuat?
toxaq
Banyak hal telah berubah sejak saya menambahkan jawaban ini pada bulan Februari, dengan lebih banyak aktivitas di halaman ini. Sepertinya ada dukungan yang lebih baik di Angular untuk menyelesaikan masalah ini sekarang daripada apa yang disarankan di sini. Cheers,
reggoodwin
Saya datang agak terlambat, tetapi berurusan dengan banyak data tidak menjadi masalah besar. Anda hanya perlu menggunakan variabel terpisah (booleans: isReadyData1, isReadyData2 dll.) Untuk setiap permintaan, dan mengatur $ scope.ready = isReadyData1 && isReadyData2 ...; bekerja dengan baik untuk saya.
GuillaumeA
1

Saya suka jawaban di atas dan belajar banyak dari mereka tetapi ada sesuatu yang hilang dalam sebagian besar jawaban di atas.

Saya terjebak dalam skenario serupa di mana saya menyelesaikan url dengan beberapa data yang diambil pada permintaan pertama dari server. Masalah yang saya hadapi adalah bagaimana jika janji itu rejected.

Saya menggunakan penyedia kustom yang digunakan untuk mengembalikan Promiseyang diselesaikan oleh resolvedari $routeProviderpada saat fase config.

Yang ingin saya tekankan di sini adalah konsep when melakukan sesuatu seperti ini.

Ia melihat url di bar url dan kemudian masing-masing whenblok di disebut controller dan view sejauh ini sangat bagus.

Katakanlah saya telah mengikuti kode fase konfigurasi.

App.when('/', {
   templateUrl: '/assets/campaigns/index.html',
   controller: 'CampaignListCtr',
   resolve : {
      Auth : function(){
         return AuthServiceProvider.auth('campaign');
      }
   }
})
// Default route
.otherwise({
   redirectTo: '/segments'
});

Pada url root di browser blok pertama dijalankan dipanggil sebaliknya otherwisedipanggil.

Mari kita bayangkan sebuah skenario yang saya tekan rootUrl di AuthServicePrivider.auth()fungsi address bar dipanggil.

Katakanlah Janji dikembalikan dalam keadaan menolak lalu bagaimana ???

Tidak ada yang diberikan sama sekali.

Otherwise blok tidak akan dieksekusi karena untuk url apa pun yang tidak didefinisikan dalam blok konfigurasi dan tidak diketahui oleh fase konfigurasi angularJs.

Kami harus menangani acara yang dipecat ketika janji ini tidak terselesaikan. Pada kegagalan $routeChangeErorrdipecat$rootScope .

Itu dapat ditangkap seperti yang ditunjukkan pada kode di bawah ini.

$rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
    // Use params in redirection logic.
    // event is the routeChangeEvent
    // current is the current url
    // previous is the previous url
    $location.path($rootScope.rootPath);
});

IMO Ini umumnya ide yang baik untuk meletakkan kode pelacakan acara dalam menjalankan blok aplikasi. Kode ini berjalan tepat setelah fase konfigurasi aplikasi.

App.run(['$routeParams', '$rootScope', '$location', function($routeParams, $rootScope, $location){
   $rootScope.rootPath = "my custom path";
   // Event to listen to all the routeChangeErrors raised
   // by the resolve in config part of application
   $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
       // I am redirecting to rootPath I have set above.
       $location.path($rootScope.rootPath);
   });
}]);

Dengan cara ini kita dapat menangani kegagalan janji pada saat tahap konfigurasi.

Ashish Singh
sumber
0

Saya memiliki antarmuka panel geser multi-level yang kompleks, dengan lapisan layar yang dinonaktifkan. Membuat arahan pada menonaktifkan lapisan layar yang akan membuat acara klik untuk mengeksekusi keadaan seperti

$state.go('account.stream.social.view');

menghasilkan efek menjentikkan. history.back () bukannya berfungsi dengan baik, namun itu tidak selalu kembali dalam sejarah dalam kasus saya. SO yang saya temukan adalah bahwa jika saya hanya membuat atribut href pada layar menonaktifkan saya alih-alih state.go, bekerja seperti pesona.

<a class="disable-screen" back></a>

Arahan 'kembali'

app.directive('back', [ '$rootScope', function($rootScope) {

    return {
        restrict : 'A',
        link : function(scope, element, attrs) {
            element.attr('href', $rootScope.previousState.replace(/\./gi, '/'));
        }
    };

} ]);

app.js Saya baru saja menyimpan status sebelumnya

app.run(function($rootScope, $state) {      

    $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {         

        $rootScope.previousState = fromState.name;
        $rootScope.currentState = toState.name;


    });
});
Dima
sumber
-2

Salah satu solusi yang mungkin adalah dengan menggunakan arahan ng-jubah dengan elemen di mana kita menggunakan model misalnya

<div ng-cloak="">
  Value in  myModel is: {{myModel}}
</div>

Saya pikir ini membutuhkan sedikit usaha.

Devendra Singh
sumber