Backbone.js: dapatkan rute saat ini

139

Dengan menggunakan Backbone, apakah saya bisa mendapatkan nama untuk rute saat ini? Saya tahu cara mengikat ke peristiwa perubahan rute, tetapi saya ingin dapat menentukan rute saat ini di waktu lain, di antara perubahan.

Drew Dara-Abrams
sumber
Yang Anda maksud dengan "nama" adalah fungsi yang mengikat rute itu atau apa URL atau bagian hash URL saat ini?
John Munsch
1
Oh, ya, saya seharusnya lebih spesifik. Saya ingin tahu nama fungsinya. (Saya tahu cara mendapatkan bagian hash dari URL dengan melakukan location.hash atau Backbone.history.fragment.)
Drew Dara-Abrams

Jawaban:

210

Jika Anda telah membuat instance Router di aplikasi Anda, baris berikut menampilkan fragmen saat ini:

Backbone.history.getFragment();

Dari dokumentasi Backbone.js :

"[...] Histori berfungsi sebagai router global (per frame) untuk menangani peristiwa hashchange atau pushState, mencocokkan rute yang sesuai, dan memicu callback. Anda tidak perlu membuat sendiri salah satunya - Anda harus menggunakan referensi ke Backbone.history yang akan dibuat untuk Anda secara otomatis jika Anda menggunakan Router dengan rute. [...] "

Jika Anda membutuhkan nama fungsi yang terikat ke fragmen itu, Anda dapat membuat sesuatu seperti ini di dalam cakupan Router Anda:

alert( this.routes[Backbone.history.getFragment()] );

Atau seperti ini dari luar router Anda:

alert( myRouter.routes[Backbone.history.getFragment()] );
Robert
sumber
Terima kasih, itu bagus. Saya tidak pernah menyebut Backbone.history.fragment dalam dokumen. Apakah ini tambahan baru, atau hanya tidak berdokumen?
Drew Dara-Abrams
Saya pikir ini tidak berdokumen. Sebenarnya saya tidak ingat di mana saya melihatnya pertama kali, beberapa blog atau mungkin kode sumber backbone.
Robert
25
FYI, ini tidak berfungsi dengan rute seperti 'foo /: id' atau 'bar * params'
wprl
2
Penggunaan yang lebih baik Backbone.history.getFragment(), karena Backbone.history.fragmentmerupakan properti tersembunyi pribadi.
Vad
1
Saya telah mengoreksi jawaban mengikuti saran @Vad. Saya tidak lagi menggunakan Backbone tetapi komentarnya terdengar benar bagi saya. (Jawaban sebelumnya adalah: Backbone.history.fragment bukannya Backbone.history.getFragment ())
Robert
57

Jawaban Robert menarik, tetapi sayangnya itu hanya akan berfungsi jika hash persis seperti yang ditentukan dalam rute. Misalnya, jika Anda memiliki rute untuk user(/:uid)itu tidak akan cocok jika Backbone.history.fragmentadalah salah satu "user"atau "user/1"(keduanya merupakan dua kasus penggunaan paling jelas untuk rute tersebut). Dengan kata lain, ini hanya akan menemukan nama panggilan balik yang sesuai jika hashnya sama persis "user(/:uid)"(sangat tidak mungkin).

Karena saya membutuhkan fungsi ini saya memperpanjang Backbone.Routerdengan current-fungsi yang menggunakan kembali beberapa kode yang digunakan objek History dan Router untuk mencocokkan fragmen saat ini terhadap Rute yang ditentukan untuk memicu callback yang sesuai. Untuk kasus penggunaan saya, dibutuhkan parameter opsional route, yang jika disetel ke sesuatu yang benar akan mengembalikan nama fungsi yang sesuai yang ditentukan untuk rute tersebut. Jika tidak, ini akan mengembalikan fragmen hash saat ini dari Backbone.History.fragment.

Anda dapat menambahkan kode ke Perpanjang yang ada tempat Anda menginisialisasi dan menyiapkan router Backbone.

var Router = new Backbone.Router.extend({

    // Pretty basic stuff
    routes : {
        "home" : "home",
        "user(:/uid)" : "user",
        "test" : "completelyDifferent"
    },

    home : function() {
        // Home route
    },

    user : function(uid) {
        // User route
    },

    // Gets the current route callback function name
    // or current hash fragment
    current : function(route){
        if(route && Backbone.History.started) {
            var Router = this,
                // Get current fragment from Backbone.History
                fragment = Backbone.history.fragment,
                // Get current object of routes and convert to array-pairs
                routes = _.pairs(Router.routes);

            // Loop through array pairs and return
            // array on first truthful match.
            var matched = _.find(routes, function(handler) {
                var route = handler[0];

                // Convert the route to RegExp using the 
                // Backbone Router's internal convert
                // function (if it already isn't a RegExp)
                route = _.isRegExp(route) ? route :  Router._routeToRegExp(route);

                // Test the regexp against the current fragment
                return route.test(fragment);
            });

            // Returns callback name or false if 
            // no matches are found
            return matched ? matched[1] : false;
        } else {
            // Just return current hash fragment in History
            return Backbone.history.fragment
        }
    }
});

// Example uses:
// Location: /home
// console.log(Router.current()) // Outputs 'home'
// Location: /user/1
// console.log(Router.current(true)) // Outputs 'user'
// Location: /user/2
// console.log(Router.current()) // Outputs 'user/2'
// Location: /test
// console.log(Router.current(true)) // Outputs 'completelyDifferent'

Saya yakin beberapa perbaikan dapat dilakukan, tetapi ini adalah cara yang baik untuk Anda mulai. Juga, mudah untuk membuat fungsionalitas ini tanpa memperluas objek Route. Saya melakukan ini karena ini adalah cara paling nyaman untuk pengaturan saya.

Saya belum menguji ini sepenuhnya, jadi tolong beri tahu saya jika ada yang tidak beres.


UPDATE 04/25/2013

Saya melakukan beberapa perubahan pada fungsi, jadi alih-alih mengembalikan hash atau nama panggilan balik rute, saya mengembalikan objek dengan fragmen, params, dan rute sehingga Anda dapat mengakses semua data dari rute saat ini, seperti yang Anda lakukan dari rute- peristiwa.

Anda dapat melihat perubahan di bawah ini:

current : function() {
    var Router = this,
        fragment = Backbone.history.fragment,
        routes = _.pairs(Router.routes),
        route = null, params = null, matched;

    matched = _.find(routes, function(handler) {
        route = _.isRegExp(handler[0]) ? handler[0] : Router._routeToRegExp(handler[0]);
        return route.test(fragment);
    });

    if(matched) {
        // NEW: Extracts the params using the internal
        // function _extractParameters 
        params = Router._extractParameters(route, fragment);
        route = matched[1];
    }

    return {
        route : route,
        fragment : fragment,
        params : params
    };
}

Lihat kode sebelumnya untuk komentar dan penjelasan lebih lanjut, sebagian besar terlihat sama.

Simon Kjellberg
sumber
1
Akan menulis ini sendiri, tetapi mencari solusinya di Google terlebih dahulu. Senang saya melakukannya. Anda tidak persis apa yang saya inginkan, terima kasih!
Dan Abramov
Ini adalah versi yang sedikit lebih bertele-tele yang menurut saya lebih mudah dipahami pada pandangan pertama.
Dan Abramov
4
Wow, terima kasih untuk ini! Anda baru saja mendapat pengakuan di basis kode kami. ; P Kami men-tweaknya sedikit untuk Marionette. Ngomong-ngomong, sebagai pintasan, Anda dapat menyetel parameter ketiga (menyetel konteks) dari _.findfungsi sebagai this, sehingga menghilangkan kebutuhan akan Routervariabel (@DanAbramov sudah melakukan ini).
Tandai
@Mark Ini adalah kabar baik. Tetapi bagaimana saya dapat menggunakan fitur ini dalam marionette? Tidak ada apa-apa tentang itu di dokumen.
darksoulsong
2
@darksoulsong Jika Anda menggunakan Marionette.AppRouter, memperpanjang router Anda dengan fungsi di atas, tetapi pengganti Router.appRoutesuntuk Router.routes.
Tandai
11

Untuk mendapatkan rute panggilan (atau url) dari penangan rute yang disebut, Anda bisa mendapatkannya dengan memeriksa

Backbone.history.location.href ... url lengkap
Backbone.history.location.search ... string kueri dimulai dari?

Saya sampai di sini untuk mencari jawaban ini jadi saya rasa saya harus meninggalkan apa yang saya temukan.

yoshi
sumber
6

Jika Anda menggunakan pengaturan root untuk Router, Anda juga dapat memasukkannya untuk mendapatkan fragmen 'asli'.

(Backbone.history.options.root || "") + "/" + Backbone.history.fragment
pengguna1310662
sumber
6

Berikut adalah anak laki-laki lebih verbose (atau, tergantung selera Anda, lebih mudah dibaca) versi jawaban Simon :

current: function () {
  var fragment = Backbone.history.fragment,
      routes = _.pairs(this.routes),
      route,
      name,
      found;

  found = _.find(routes, function (namedRoute) {
    route = namedRoute[0];
    name = namedRoute[1];

    if (!_.isRegExp(route)) {
      route = this._routeToRegExp(route);
    }

    return route.test(fragment);
  }, this);

  if (found) {
    return {
      name: name,
      params: this._extractParameters(route, fragment),
      fragment: fragment
    };
  }
}
Dan Abramov
sumber
4

Jika Anda melihat sumber untuk Router, Anda akan melihat bahwa ketika router memicu peristiwa yang mengatakan bahwa ada sesuatu yang berubah, ia meneruskan namanya sebagai "route: name".

http://documentcloud.github.com/backbone/docs/backbone.html#section-84

Anda selalu dapat mengaitkan peristiwa "rute" di router dan menyimpannya untuk mendapatkan rute saat ini.

Brian Genisio
sumber
Oke, saya rasa ini semacam "buat sendiri".
Drew Dara-Abrams
Ya, tetapi Anda tidak akan mendapatkan routeacara jika trigger tidak true (disarankan & default).
Dan Abramov
0
router = new Backbone.Router.extend({
  routes: { "stuff/:id" : "stuff" },
  stuff: function(id) {}
});

router.on('route', function(route) {
  this.current = route;
});

Sekarang jika Anda menavigasi ke /stuff/1, router.current akan menjadi "barang"

Dave Aaron Smith
sumber