Backbone.js: pobierz aktualną trasę

137

Czy za pomocą Backbone mogę uzyskać nazwę bieżącej trasy? Wiem, jak powiązać zdarzenia zmiany trasy, ale chciałbym móc określić bieżącą trasę w innym czasie, pomiędzy zmianami.

Drew Dara-Abrams
źródło
Przez „nazwę” masz na myśli funkcję, z którą wiąże się ta trasa, czy tylko aktualny adres URL lub część z krzyżykiem adresu URL?
John Munsch,
1
O tak, powinienem był być bardziej szczegółowy. Chciałbym poznać nazwę funkcji. (Wiem, jak uzyskać część z krzyżykiem adresu URL, wykonując location.hash lub Backbone.history.fragment.)
Drew Dara-Abrams

Odpowiedzi:

209

Jeśli utworzyłeś wystąpienie routera w swojej aplikacji, poniższy wiersz zwraca bieżący fragment:

Backbone.history.getFragment();

Z dokumentacji Backbone.js :

„[...] Historia służy jako router globalny (na ramkę) do obsługi zdarzeń związanych z hashchange lub pushState, dopasowywania odpowiedniej trasy i wywoływania wywołań zwrotnych. Nigdy nie powinno być konieczne samodzielne tworzenie jednego z nich - należy użyć odniesienia do Backbone.history, który zostanie utworzony automatycznie, jeśli użyjesz routerów z trasami. [...] "

Jeśli potrzebujesz nazwy funkcji związanej z tym fragmentem, możesz zrobić coś takiego w zasięgu routera:

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

Lub w ten sposób spoza routera:

alert( myRouter.routes[Backbone.history.getFragment()] );
Robert
źródło
Dzięki, to świetnie. W dokumencie nigdy nie było wzmianki o Backbone.history.fragment. Czy to nowy dodatek, czy po prostu nieudokumentowany?
Drew Dara-Abrams
Myślę, że to nieudokumentowane. Właściwie nie pamiętam, gdzie widziałem go po raz pierwszy, jakiś blog, a może kod źródłowy szkieletu.
Robert,
25
Do Twojej wiadomości, to nie działa z trasami takimi jak „foo /: id” lub „bar * params”
wprl
2
Lepsze wykorzystanie Backbone.history.getFragment(), ponieważ Backbone.history.fragmentjest to prywatna ukryta własność.
Vad
1
Poprawiłem odpowiedź po sugestii @Vad. Nie używam już Backbone, ale jego komentarz brzmi dla mnie dobrze. (Poprzednia odpowiedź brzmiała: Backbone.history.fragment zamiast Backbone.history.getFragment ())
Robert
57

Odpowiedź Roberta jest interesująca, ale niestety zadziała tylko wtedy, gdy hash jest dokładnie taki, jak zdefiniowano w trasie. Jeśli na przykład masz trasę, user(/:uid)nie zostanie ona dopasowana, jeśli Backbone.history.fragmentjest to albo "user"albo "user/1"(oba są dwoma najbardziej oczywistymi przypadkami użycia takiej trasy). Innymi słowy, znajdzie odpowiednią nazwę wywołania zwrotnego tylko wtedy, gdy hash jest dokładnie "user(/:uid)"(bardzo mało prawdopodobne).

Ponieważ potrzebowałem tej funkcjonalności, rozszerzyłem funkcję Backbone.Routera, currentktóra ponownie wykorzystuje część kodu używanego przez obiekt Historia i Router w celu dopasowania bieżącego fragmentu do zdefiniowanych tras w celu wyzwolenia odpowiedniego wywołania zwrotnego. W moim przypadku przyjmuje opcjonalny parametr route, który, jeśli jest ustawiony na cokolwiek prawdziwe, zwróci odpowiednią nazwę funkcji zdefiniowaną dla trasy. W przeciwnym razie będzie to powrót aktualny hash-fragment z Backbone.History.fragment.

Możesz dodać kod do istniejącego rozszerzenia Extend, w którym zainicjujesz i skonfigurujesz 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'

Jestem pewien, że można by wprowadzić pewne ulepszenia, ale to dobry sposób na rozpoczęcie. Ponadto łatwo jest stworzyć tę funkcjonalność bez rozszerzania obiektu Route. Zrobiłem to, ponieważ był to najwygodniejszy sposób na moją konfigurację.

Nie testowałem tego jeszcze w pełni, więc daj mi znać, jeśli coś pójdzie na południe.


AKTUALIZACJA 25.04.2013

Dokonałem pewnych zmian w funkcji, więc zamiast zwracać nazwę skrótu lub wywołania zwrotnego trasy, zwracam obiekt z fragmentem, parametrami i trasą, dzięki czemu można uzyskać dostęp do wszystkich danych z bieżącej trasy, podobnie jak w przypadku trasy- zdarzenie.

Możesz zobaczyć zmiany poniżej:

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
    };
}

Zobacz poprzedni kod, aby uzyskać dalsze komentarze i wyjaśnienia, wyglądają one w większości tak samo.

Simon Kjellberg
źródło
1
Miałem sam to napisać, ale najpierw szukałem rozwiązania w Google. Cieszę się, że to zrobiłem. Twój robi dokładnie to , czego chciałem, dzięki!
Dan Abramov
Oto nieco bardziej szczegółowa wersja, która moim zdaniem jest łatwiejsza do zrozumienia na pierwszy rzut oka.
Dan Abramov
4
Wow, dzięki za to! Właśnie dostałeś potwierdzenie w naszej bazie kodów. ; P Poprawiliśmy go nieznacznie dla Marionette. Nawiasem mówiąc, jako skrót, można ustawić trzeci parametr (zestawy kontekstu) z _.findfunkcji jako this, eliminując tym samym potrzebę Routerzmiennej (@DanAbramov już to zrobił).
Mark
@Mark To dobra wiadomość. Ale jak mogę wykorzystać tę funkcję w marionetce? W dokumentacji nie ma nic na ten temat.
darksoulsong
2
@darksoulsong Jeśli używasz Marionette.AppRouterprzedłużyć swój router z funkcją powyżej, jednak substytut Router.appRoutesdla Router.routes.
Mark
11

Aby uzyskać trasę wywołującą (lub adres URL) z wywoływanej procedury obsługi trasy, możesz ją uzyskać, zaznaczając

Backbone.history.location.href ... pełny adres URL
Backbone.history.location.search ... ciąg zapytania zaczynający się od?

Dotarłem tutaj w poszukiwaniu tej odpowiedzi, więc myślę, że powinienem zostawić to, co znalazłem.

yoshi
źródło
6

Jeśli używasz ustawienia root dla routera, możesz je również dołączyć, aby uzyskać „prawdziwy” fragment.

(Backbone.history.options.root || "") + "/" + Backbone.history.fragment
user1310662
źródło
6

Oto odrobinę bardziej rozwlekła (lub, w zależności od gustu, bardziej czytelna) wersja odpowiedzi Simona :

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
źródło
4

Jeśli spojrzysz na źródło Router, zobaczysz, że kiedy router wyzwala zdarzenie, mówiąc, że coś się zmienia, przekazuje mu nazwę jako „route: name”.

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

Zawsze możesz podpiąć zdarzenie „route” do routera i zapisać je w celu uzyskania aktualnej trasy.

Brian Genisio
źródło
OK, myślę, że to coś w rodzaju „zbuduj to sam”.
Drew Dara-Abrams,
Tak, ale nie otrzymasz routezdarzenia, jeśli trigger nie jest true (zalecane i domyślne).
Dan Abramov