web-dev-qa-db-ja.com

Angular-ui.router:ビューを更新せずにURLを更新

Angular SPAは、さまざまなおすすめリストを表示し、いくつかのレストランデータの異なるカットに基づいて場所のGoogleマップを表示します( m.amsterdamfoodie.nl =)。これらの各リストに独自のURLを持たせたいGoogleが異なるリストをクロールできるようにするために、offcanvasナビゲーションに_<a>_タグを使用します。

現在、_<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'
_

考えられる手がかりの1つは、以下のコードを使用して、たとえば http://afmnew.herokuapp.com/criteria/cuisine/italian ナビゲートするとビューが更新されますが、ロードすると http://afmnew.herokuapp.com/ =更新はありませんが、代わりにURLの更新はありません。なぜそれが起こっているのか全く分かりません。

23
Simon H

以前の議論に基づいて、UI-Router ここに。私はあなたの挑戦を正しく理解していると信じています... 実例 があります。これが完全にスイートではない場合、いくつかのインスピレーションとして受け取ってください

免責事項:配管工では、これを達成できませんでした: http://m.amsterdamfoodie.nl/ 、しかし、原則はその中にあるべきです 類似

したがって、状態の定義があります(2つの状態しかありません)

  $stateProvider
    .state('main', {
        url: '/',
        views: {
          '@' : {
            templateUrl: 'tpl.layout.html',
            controller: 'MainCtrl',
          },
          'right@main' : { templateUrl: 'tpl.right.html',}, 
          'map@main' : {
            templateUrl: 'tpl.map.html',
            controller: 'MapCtrl',
          },
          'list@main' : {
            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'、例: 'right@main'

サブビュー、main.criterialayoutビューに挿入します。

そのURLは記号で始まります^url : '^/criteria/:criteria/:value')、/スラッシュはメインのスラッシュであり、子のダブルスラッシュではありません

また、コントローラーもありますが、ここでは少し素朴ですが、背景には実際のデータ負荷(基準に基づく)が存在する可能性があることを示しているはずです。

ここで最も重要なのは、親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が提供する機能をどのように使用できるかがわかります。

上記の抜粋を確認してください ここ実際の例

拡張:新しいプランカー ここ

マップビューを再作成したくない場合は、子状態defからそのフォームを省略できます。

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

これで、マップVIEWはモデルの変更を受信するだけになります(監視することができます)が、ビューとコントローラーは再レンダリングされません

また、別のプランカーがあります http://plnkr.co/edit/y0GzHv?p=previewcontrollerAsを使用します

.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>

最後のプランカーは ここ です

11
Radim Köhler

これは、私が正しく理解している場合の道の例です。

$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

$stateここにAPI: http://angular-ui.github.io/ui-router/site/#/api/ui.router.state 。$ state

16

スコープの継承を使用して、ビューを更新せずに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> 

子コントローラーと子ビューのみが更新されます。次に、必要なパラメーターを指定して親スコープ関数を呼び出すことができます。

2
bahadir

簡単な答えは、do notになってしまい、変更するビュー内にマップを配置しました。受け入れられた答えは、サブビューでページを構成する方法についてより多くの詳細を提供しますが、キーポイントはマップをビューの一部にするのではなく、その動作を変更するビューに接続し、コントローラを使用して市場のアイコンを更新します。

0
Simon H