Подтвердить что ты не робот

Angular -ui.router: обновить URL без обновления вида

У меня есть SPA Angular, в котором представлены различные списки рекомендаций и карта местоположений Google, основанная на разных сокращениях некоторых данных ресторана (см. m.amsterdamfoodie.nl). Я хочу, чтобы каждый из этих списков имел свой собственный URL. Чтобы Google сканировал разные списки, я использую теги <a> для навигации offcanvas.

В настоящее время тег <a> вызывает обновление представления, что очень заметно с картой.

  • Я могу предотвратить это с помощью ng-click и $event.preventDefault() (см. ниже фрагменты кода), но затем мне нужно реализовать средство обновления URL-адреса браузера.
  • Но при попытке Angular $state или в браузере history.pushstate я в конечном итоге запускаю изменения состояния и обновление представления...!

Поэтому мой вопрос: как я могу обновить модель и URL-адрес, но не обновляя представление? (См. также Angular/UI-Router - Как обновить URL без обновления всего?

Я экспериментировал с множеством подходов и в настоящее время имею этот html

<a href="criteria/price/1" class="btn btn-default" ng-click="main.action($event)">Budget</a>

В контроллере:

this.action = ($event) ->
    $event.preventDefault()
    params = $event.target.href.match(/criteria\/(.*)\/(.*)$/)

    # seems to cause a view refresh
    # history.pushState({}, "page 2", "criteria/"+params[1]+"/"+params[2]);

    # seems to cause a view refresh
    # $state.transitionTo 'criteria', {criteria:params[1], q:params[2]}, {inherit:false}

    updateModel(...)

И, по-моему, происходит то, что я запускаю код $stateProvider:

angular.module 'afmnewApp'
.config ($stateProvider) ->
  $stateProvider
  .state 'main',
    url: '/'
    templateUrl: 'app/main/main.html'
    controller: 'MainCtrl'
    controllerAs: 'main'
  .state 'criteria',
    url: '/criteria/:criteria/:q'
    templateUrl: 'app/main/main.html'
    controller: 'MainCtrl'
    controllerAs: 'main'

Одна из возможных подсказок заключается в том, что с приведенным ниже кодом, если я загружу, например. http://afmnew.herokuapp.com/criteria/cuisine/italian, тогда представление обновляется при навигации, тогда как если я загружу http://afmnew.herokuapp.com/ нет обновлений, но вместо них нет обновлений URL. Я не понимаю, почему это происходит вообще.

4b9b3361

Ответ 1

Основываясь на наших предыдущих обсуждениях, я хочу дать вам некоторое представление о том, как использовать UI-Router здесь. Я считаю, что я правильно понимаю ваш вызов... рабочий пример. Если это не полностью подходит, пожалуйста, возьмите его как вдохновение

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: С плункером я не смог этого добиться: http://m.amsterdamfoodie.nl/, но принцип должен быть в том, что пример аналогичный

Итак, существует определение состояния (у нас есть только два состояния)

  $stateProvider
    .state('main', {
        url: '/',
        views: {
          '@' : {
            templateUrl: 'tpl.layout.html',
            controller: 'MainCtrl',
          },
          '[email protected]' : { templateUrl: 'tpl.right.html',}, 
          '[email protected]' : {
            templateUrl: 'tpl.map.html',
            controller: 'MapCtrl',
          },
          '[email protected]' : {
            templateUrl: 'tpl.list.html',
            controller: 'ListCtrl',
          },
        },
      })
    .state('main.criteria', {
        url: '^/criteria/:criteria/:value',
        views: {
          'map' : {
            templateUrl: 'tpl.map.html',
            controller: 'MapCtrl',
          },
          'list' : {
            templateUrl: 'tpl.list.html',
            controller: 'ListCtrl',
          },
        },
      })
}];

Это будет наш основной tpl.layout.html

<div>

  <section class="main">

    <section class="map">
      <div ui-view="map"></div>
    </section>

    <section class="list">
      <div ui-view="list"></div>
    </section>

  </section>

  <section class="right">
    <div ui-view="right"></div>
  </section>

</div>

Как мы видим, основное состояние задает эти вложенные представления основного состояния: 'viewName @main', например. '[email protected]'

Также subview, main.criteria вставляет в представления макет.

Его url начинается со знака ^ (url : '^/criteria/:criteria/:value'), что позволяет иметь / косую черту для основной, а не двойной косой черты для дочернего

А также есть контроллеры, они здесь немного наивные, но они должны показать, что на фоне может быть реальная загрузка данных (на основе критериев).

Наиболее важным здесь является то, что PARENT MainCtrl создает $scope.Model = {}. Это свойство будет (благодаря наследованию) разделяться между родителями и детьми. Вот почему все это будет работать:

app.controller('MainCtrl', function($scope)
{
  $scope.Model = {};
  $scope.Model.data = ['Rest1', 'Rest2', 'Rest3', 'Rest4', 'Rest5'];  
  $scope.Model.randOrd = function (){ return (Math.round(Math.random())-0.5); };
})
.controller('ListCtrl', function($scope, $stateParams)
{
  $scope.Model.list = []
  $scope.Model.data
    .sort( $scope.Model.randOrd )
    .forEach(function(i) {$scope.Model.list.push(i + " - " + $stateParams.value || "root")})
  $scope.Model.selected = $scope.Model.list[0];
  $scope.Model.select = function(index){
    $scope.Model.selected = $scope.Model.list[index];  
  }
})

Это должно понять, как мы можем использовать функции, предоставляемые нами с помощью UI-Router:

Проверьте приведенный выше экстракт здесь, в рабочий пример

Расширить: новый plunker здесь

Если мы не хотим, чтобы отображение карты было воссоздано, мы можем просто опустить эту форму в дочернее состояние def:

.state('main.criteria', {
    url: '^/criteria/:criteria/:value',
    views: {
      // 'map' : {
      //  templateUrl: 'tpl.map.html',
      //  controller: 'MapCtrl',
      //},
      'list' : {
        templateUrl: 'tpl.list.html',
        controller: 'ListCtrl',
      },
    },
  })

Теперь наша карта VIEW будет просто получать изменения в модели (может быть просмотрена), но просмотр и контроллер не будут перезагружены

ТАКЖЕ, есть еще один plunker http://plnkr.co/edit/y0GzHv?p=preview, который использует controllerAs

.state('main', {
    url: '/',
    views: {
      '@' : {
        templateUrl: 'tpl.layout.html',
        controller: 'MainCtrl',
        controllerAs: 'main',        // here
      },
      ...
    },
  })
.state('main.criteria', {
    url: '^/criteria/:criteria/:value',
    views: {
      'list' : {
        templateUrl: 'tpl.list.html',
        controller: 'ListCtrl',
        controllerAs: 'list',      // here
      },
    },
  })

и это можно использовать следующим образом:

<h4>{{main.hello()}}</h4>
<h4>{{list.hello()}}</h4>

Последний плункер здесь

Ответ 2

Это пример пути, если я правильно понимаю:

$state.go('my.state', {id:data.id}, {notify:false, reload:false});
//And to remove the id from the url:
$state.go('my.state', {id:undefined}, {notify:false, reload:false});

От пользователя l-liava-l в выпуске https://github.com/angular-ui/ui-router/issues/64

Здесь вы можете проверить API $state: http://angular-ui.github.io/ui-router/site/#/api/ui.router.state. $state

Ответ 3

вы можете использовать наследование области для обновления URL-адреса без обновления

$stateProvider
            .state('itemList', {
                url: '/itemlist',
                templateUrl: 'Scripts/app/item/ItemListTemplate.html',
                controller: 'ItemListController as itemList'
                //abstract: true //abstract maybe?
            }).state('itemList.itemDetail', {
                url: '/:itemName/:itemID',
                templateUrl: 'Scripts/app/item/ItemDetailTemplate.html',
                controller: 'ItemDetailController as itemDetail',
                resolve: {
                    'CurrentItemID': ['$stateParams',function ($stateParams) {
                        return $stateParams['itemID'];
                    }]
                }
            })

если дочерний вид находится внутри родительского представления, оба контроллера используют один и тот же объем. поэтому вы можете разместить фиктивный (или необходимый) ui-view внутри родительского представления, который будет заполнен детским представлением.

и вставьте

$scope.loadChildData = function(itemID){..blabla..};

в родительском контроллере, который будет вызываться дочерним контроллером при загрузке контроллера. поэтому, когда пользователь нажимает

<a ui-sref="childState({itemID: 12})">bla</a> 

будет обновлен только дочерний контроллер и дочерний вид. то вы можете вызвать функцию родительской области с необходимыми параметрами.

Ответ 4

Короткий ответ закончился тем, что не поместил карту внутри изменяющегося представления. В принятом ответе содержится более подробная информация о том, как структурировать страницу с под-представлениями, но ключевой момент заключается не в том, чтобы сделать карту частью представления, а в том, чтобы связать ее поведение с видом, который меняет и использовать контроллер для обновить значки на рынке.