web-dev-qa-db-fra.com

Routeur Angular-UI: les vues imbriquées ne fonctionnent pas

Création d'un formulaire en plusieurs étapes ("assistant"). Je suivais à l'origine ce tutoriel , ce qui fonctionnait très bien, mais j'essaie maintenant de l'adapter afin que la première étape soit intégrée sur la page d'accueil plutôt que d'être un état séparé. Peu importe ce que j'essaye, je ne peux pas créer un ui-sref chemin qui fonctionnera. J'ai toujours:

Impossible de résoudre ".where" à partir de l'état "home"

ou

Impossible de résoudre "wizard.where" à partir de l'état "home"

ou

Impossible de résoudre "wizard.where @" à partir de l'état "home"

…même si wizard.where@ fonctionne bien dans <div ui-view="wizard.where@"></div>. Quelle est la syntaxe correcte?

Voici les fichiers pertinents:

home.js (laissé les commentaires intacts afin que vous puissiez voir les différentes méthodes que j'essaie):

var wizard = {
  url: '/home/wizard',
  controller: 'VendorsCtrl',
  templateUrl: 'vendors/wizard.tpl.html'
};

angular.module( 'myApp.home', [
  'ui.router',
  'ui.bootstrap',
  'myApp.modal',
  'angularMoment'
])

.config(function config( $stateProvider, $urlRouterProvider ) {
  $stateProvider
    .state( 'home', {
      url: '/home',
      views: {
        "main": {
          controller: 'HomeCtrl',
          templateUrl: 'home/home.tpl.html'
        },
        "jumbotron": {
          controller: 'HomeCtrl',
          templateUrl: 'home/welcome.tpl.html'
        },
        "wizard": wizard,
        "wizard.where": {
          url: '/home/wizard/where',
          controller: 'VendorsCtrl',
          templateUrl: 'vendors/wizard-where.tpl.html',
          parent: wizard
        },
        "wizard.what": {
          url: '/home/wizard/what',
          controller: 'VendorsCtrl',
          templateUrl: 'vendors/wizard-what.tpl.html',
          parent: wizard
        },
        "wizard.when": {
          url: '/home/wizard/when',
          controller: 'VendorsCtrl',
          templateUrl: 'vendors/wizard-when.tpl.html',
          parent: wizard
        },
      },
      data: { pageTitle: 'Home' }
    })

    // route to show our basic form (/wizard)
    // .state('wizard', {
    //   url: '/wizard',
    //   views: {
    //     "main": {
    //       controller: 'VendorsCtrl',
    //       templateUrl: 'vendors/wizard.tpl.html'
    //     }
    //   },
    //   abstract: true,
    //   //data: { pageTitle: 'Vendor Search' }
    // })

    // nested states 
    // each of these sections will have their own view
    // url will be nested (/wizard/where)
    // .state('wizard.where', {
    //   url: '/where',
    //   templateUrl: 'vendors/wizard-where.tpl.html'
    // })

    // url will be /wizard/when
    // .state('wizard.when', {
    //   url: '/when',
    //   templateUrl: 'vendors/wizard-when.tpl.html'
    // })

    // url will be /wizard/vendor-types
    // .state('wizard.what', {
    //   url: '/what',
    //   templateUrl: 'vendors/wizard-what.tpl.html'
    // })
    ;

    // catch all route
    // send users to the form page 
    $urlRouterProvider.otherwise('/home/wizard/where');
})

wizard.tpl.html :

<div class="jumbotron vendate-wizard" ng-controller="VendorsCtrl as vendorsCtrl">
  <header class="page-title">
    <h1>{{ pageTitle }}</h1>
    <p>Answer the following three questions to search available vendors. All answers can be changed later.</p>

    <!-- the links to our nested states using relative paths -->
    <!-- add the active class if the state matches our ui-sref -->
    <div id="status-buttons" class="text-center">
      <a ui-sref-active="active" ui-sref="wizard.where@"><span>1</span> Where</a>
      <a ui-sref-active="active" ui-sref="wizard.what@"><span>2</span> What</a>
      <a ui-sref-active="active" ui-sref="wizard.when@"><span>3</span> When</a>
    </div>
  </header>

  <!-- use ng-submit to catch the form submission and use our Angular function -->
  <form id="signup-form" ng-submit="processForm()">

    <!-- our nested state views will be injected here -->
    <div id="form-views" ui-view="wizard.where@"></div>
  </form>
</div>

wizard.where.tpl.html :

<div class="form-group">
  <label class="h2" for="where">Where Is Your Wedding?</label>
  <p id="vendor-where-description">If left blank, vendors in all available locations will be shown.</p>
  <div class="input-group-lg">
    <input id="where" ng-model="formData.where" class="form-control" type="text" placeholder="Boston, MA" aria-describedby="vendor-where-description" />
  </div>
</div>

<ul class="list-inline">
  <li>
    <a ui-sref="wizard.what@" class="btn btn-block btn-primary">
      Next <span class="fa fa-arrow-right"></span>
    </a>
  </li>
</ul>
14
Hugh Guiney

J'ai créé plunker de travail ici

REMARQUE: vous devriez en savoir plus sur l'imbrication des états et les vues nommées. Parce que l'état actuel et la définition de la vue sont tout simplement faux.

Tout d'abord, nous ne devons pas utiliser la définition d'état ONE avec plusieurs views: {}. Mais nous devons les diviser en états réels. La hiérarchie aura trois niveaux

Le premier niveau - super root state

.state( 'home', {
  url: '/home',
  views: {
    "main": {
      controller: 'HomeCtrl',
      templateUrl: 'home/home.tpl.html'
    },
  }
})

Le deuxième niveau - wizzard, vérifiez que maintenant nous changeons l'url. Nous hériterons de sa première partie de notre parent (domicile)

.state("wizard", {
  parent: 'home',
  //url: '/home/wizard',
  url: '/wizard',
  controller: 'VendorsCtrl',
  templateUrl: 'vendors/wizard.tpl.html'
})

Le troisième niveau - tout où, quoi, quand maintenant héritera également de l'url. Ils n'ont pas à définir de parent, car cela fait partie de leurs noms

.state( "wizard.where",  {
      //url: '/home/wizard/where',
      url: '/where',
      controller: 'VendorsCtrl',
      templateUrl: 'vendors/wizard-where.tpl.html',
      //parent: wizard
})
.state( "wizard.what",  {
      //url: '/home/wizard/what',
      url: '/what',
      controller: 'VendorsCtrl',
      templateUrl: 'vendors/wizard-what.tpl.html',
      //parent: wizard
})
.state( "wizard.when",  {
      //url: '/home/wizard/when',
      url: '/when',
      controller: 'VendorsCtrl',
      templateUrl: 'vendors/wizard-when.tpl.html',
      //parent: wizard
})

Le parent wizzard doit maintenant contenir une cible de vue sans nom ui-view=""

<div ui-view=""></div>

Current wizard.tpl.html contient ceci:

<!-- our nested state views will be injected here -->
<div id="form-views" ui-view="wizard.where@"></div>

Le signe @ devrait être évité, car il pourrait être utilisé pour nommer la vue absulte - MAIS à l'intérieur de la définition de l'état. Donc, ce qui pourrait fonctionner est ui-view="someName

<!-- our nested state views will be injected here -->
<div id="form-views" ui-view="someName"></div>

Maintenant, ce sont ( dans l'exemple ici ) voir le contenu de home.tpl

<div>
  <h1>HOME</h1>

  <div ui-view=""></div>
</div>

Et wizzard.tpl

<div>
  <h2>WIZZARD</h2>

  <div ui-view=""></div>
</div>

Donc, nous avons une cible de vue sans nom à l'intérieur des états home et wizard, c'est très pratique, car nous pouvons utiliser la définition de l'état lumineux, sans views : {} objet. Et cela est toujours préférable au cas où nous n'aurions pas de vues multiples.

Cela signifie que cette définition d'état sera correctement injectée dans le modèle ci-dessus:

// no views - search in parent for a ui-view=""
...
.state( "wizard.when",  {
      url: '/when',
      controller: 'VendorsCtrl',
      templateUrl: 'vendors/wizard-when.tpl.html',
})
...

Consultez le doc:

Afficher les noms - Noms relatifs ou absolus

Dans les coulisses, chaque vue reçoit un nom absolu qui suit un schéma de viewname@statename, où viewname est le nom utilisé dans la directive view et state name est le nom absolu de l'état, par exemple contact.item. Vous pouvez également choisir d'écrire vos noms de vue dans la syntaxe absolue.

Par exemple, l'exemple précédent pourrait également s'écrire:

.state('report',{
    views: {
      'filters@': { },
      'tabledata@': { },
      'graph@': { }
    }
})

Notez que les noms de vue sont désormais spécifiés en tant que noms absolus, par opposition au nom relatif. Il cible les vues "filtres", "tabledata" et "graph" situées dans le modèle racine sans nom. Puisqu'il n'est pas nommé, il n'y a rien après le '@'. Le modèle racine sans nom est votre index.html.

Appel de l'État à partir de l'État

Lorsque nous voulons dans quel état naviguer vers quand, nous pouvons utiliser directiv ui-sref, mais il doit contenir le nom de l'état, pas afficher la convention de dénomination

// instead of this
<a ui-sref="wizard.what@"
we need this
<a ui-sref="wizard.what"

La raison, dans cette hiérarchie à trois niveaux, nous n'utilisons que les noms des parents et des enfants (pas le "parent" du grand parent), est masquée dans la définition de l'état. Parce que nous avons utilisé ceci:

.state("wizard", {
  parent: 'home',

Le parent est juste un parent, ne fait pas partie du nom de l'État. Ce qui est bien dans des scénarios comme celui-ci (nous avons besoin du parent racine/grand pour établir des éléments communs, mais son nom n'est pas nécessaire pour les sous-états)

Consultez le doc:

i-sref

Une directive qui lie un lien (<a> tag) à un état. Si l'état a une URL associée, la directive générera et mettra automatiquement à jour l'attribut href via la méthode $ state.href (). Cliquer sur le lien déclenchera une transition d'état avec des paramètres facultatifs.
...

Vous pouvez spécifier des options à passer à $ state.go () en utilisant le ui-sref-opts attribut. Les options sont limitées à l'emplacement, à l'héritage et au rechargement.

ui-sref - string - 'stateName' peut être n'importe quel état absolu ou relatif valide

42
Radim Köhler

[S] tep one est intégré sur la page d'accueil plutôt que d'être un état séparé

Vous devez traiter chaque ui-view comme un état, mais déclarer wizard.where comme état par défaut/index .

Notez que le tutoriel utilise $ urlRouterProvider pour faire form/profile l'état par défaut.

// catch all route
// send users to the form page 
$urlRouterProvider.otherwise('/form/profile');

De cette manière, cependant, /form finira par /form/profile.

Vous pouvez cependant créer un état d'URL vide avec modification mineure :

// route to show our basic form (/form)
.state('form', {
    url: '/form',
    templateUrl: 'form.html',
    controller: 'formController',
    abstract: true //<-- Declare parent as an abstract state. 
})

// nested states 
// each of these sections will have their own view
// url will be nested (/form)
.state('form.profile', {
    url: '', //<-- Empty string for "profile" state to override the /form abstract state
    templateUrl: 'form-profile.html'
})

// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form'); //<-- Default state is empty

@ radim-köhler a également fourni un excellent aperçu du routeur d'interface utilisateur et des définitions d'état.

3
SirTophamHatt