Javascript Angular-UI 路由器:嵌套视图不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29585103/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:37:18  来源:igfitidea点击:

Angular-UI Router: Nested Views Not Working

javascriptangularjsangular-ui-routerangular-uiangular-ui-bootstrap

提问by Hugh Guiney

Building a multi-step form (“wizard”). Was originally following this tutorial, which worked great, but am now trying to adapt it so step one is embedded on the homepage rather than being a separate state. No matter what I try, I can not create a ui-srefpath that will work. I always get:

构建多步骤表单(“向导”)。最初遵循本教程,效果很好,但现在我正在尝试对其进行调整,以便将第一步嵌入到主页中,而不是作为一个单独的状态。无论我尝试什么,我都无法创建一条ui-sref可行的路径。我总是得到:

Could not resolve '.where' from state 'home'

无法从状态“home”解析“.where”

or

或者

Could not resolve 'wizard.where' from state 'home'

无法从状态“home”解析“wizard.where”

or

或者

Could not resolve 'wizard.where@' from state 'home'

无法从状态“home”解析“wizard.where@”

…even though wizard.where@works fine in <div ui-view="wizard.where@"></div>. What is the correct syntax?

...即使wizard.where@<div ui-view="wizard.where@"></div>. 什么是正确的语法?

Here are the relevant files:

以下是相关文件:

home.js(left comments intact so you can see various methods I'm trying):

home.js(保留完整注释,以便您可以看到我正在尝试的各种方法):

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:

向导.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:

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

回答by Radim K?hler

I created working plunker here

我在这里创建了工作 plunker

NOTE: You should read about state nesting and named views more. Because the current state and view definition is simply wrong.

注意:您应该更多地了解状态嵌套和命名视图。因为当前的状态和视图定义是完全错误的。

Firstly, we should not use the ONEstate definition with many views: {}. But we should split them into real states. Hierarchy will have three levels

首先,我们不应该将ONE状态定义与 many 一起使用views: {}。但是我们应该将它们分成真实的状态。层次结构将具有三个级别

The first level - super root state

第一级——超级根状态

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

The second level - wizzard, check that now we change the url. We will inherit its first part from our parent (home)

第二级 - wizzard,检查现在我们更改了 url。我们将从我们的父母(家)那里继承它的第一部分

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

The third level - all where, what, when now will also inherit url. They do not have to define parent, because it is part of their names

第三级——所有where、what、when now 也会继承url。他们不必定义 parent,因为它是他们名字的一部分

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

Parent wizzardmust now contain unnamed view target ui-view=""

向导现在必须包含未命名的视图目标ui-view=""

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

Current wizard.tpl.htmlcontains this:

当前的wizard.tpl.html包含以下内容:

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

The sign @should be avoided, because it could be used for absulte view naming - BUT inside of the state defintion. So, what could work is ui-view="someName

@应该避免使用该符号,因为它可以用于绝对视图命名 - 但在状态定义内部。那么,可行的是ui-view="someName

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

Now, these are (in example here) view content of the home.tpl

现在,这些是(在此处的示例中)查看home.tpl

<div>
  <h1>HOME</h1>

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

And wizzard.tpl

wizzard.tpl

<div>
  <h2>WIZZARD</h2>

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

So, we have unnamed view target inside of homeand wizardstates, That is very handy, because we can use the light state definition, without views : {}object. And that is always preferred in case we do not have multi-views.

所以,我们在homewizard状态中有未命名的视图目标,这非常方便,因为我们可以使用光状态定义,而无需views : {}对象。如果我们没有多视图,这总是首选。

That means, that this state definition will properly be injected into above template:

这意味着,这个状态定义将被正确地注入到上面的模板中:

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

Check the doc:

检查文档:

View Names - Relative vs. Absolute Names

查看名称 - 相对名称与绝对名称

Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname@statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.

For example, the previous example could also be written as:

在幕后,每个视图都被分配了一个遵循方案的绝对名称viewname@statename,其中 viewname 是视图指令中使用的名称,状态名称是状态的绝对名称,例如 contact.item。您还可以选择以绝对语法编写视图名称。

例如,前面的例子也可以写成:

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

Notice that the view names are now specified as absolute names, as opposed to the relative name. It is targeting the 'filters', 'tabledata', and 'graph' views located in the root unnamed template. Since it's unnamed, there is nothing following the '@'. The root unnamed template is your index.html.

请注意,视图名称现在指定为绝对名称,而不是相对名称。它针对位于根未命名模板中的“过滤器”、“表数据”和“图形”视图。由于它未命名,因此“@”后面没有任何内容。根未命名模板是您的 index.html。

Calling the state from state

从状态调用状态

Whe we want in where state navigate to when, we can use directiv ui-sref, but it must contain state name, not view naming convention

当我们想要在 where state 导航到 when 时,我们可以使用 directiv ui-sref,但它必须包含状态名称,而不是视图命名约定

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

The reason, that in this three level hierarchy we do use only parent and child names (not grand parent 'home'), is hidden in state definition. Because we used this:

原因,在这个三级层次结构中,我们只使用父和子名称(不是祖父“家”),隐藏在状态定义中。因为我们使用了这个:

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

Parent is just a parent, not part of the state name. Which is good in scenarios like this (we need the root/grand parent to establish some comon stuff, but it name is not needed for substates)

Parent 只是一个父级,而不是州名的一部分。这在这样的场景中很好(我们需要根/祖父来建立一些共同的东西,但子状态不需要它的名字)

Check the doc:

检查文档:

ui-sref

ui-sref

A directive that binds a link (<a>tag) to a state. If the state has an associated URL, the directive will automatically generate & update the href attribute via the $state.href()method. Clicking the link will trigger a state transition with optional parameters.
...

You can specify options to pass to $state.go()using the ui-sref-optsattribute. Options are restricted to location, inherit, and reload.

ui-sref- string - 'stateName'can be any valid absolute or relative state

将链接(<a>标签)绑定到状态的指令。如果状态具有关联的 URL,则该指令将通过$state.href()方法自动生成和更新 href 属性。单击该链接将触发带有可选参数的状态转换。
...

您可以使用属性指定要传递给$state.go() 的选项ui-sref-opts。选项仅限于定位、继承和重新加载。

ui-sref- 字符串 - 'stateName'可以是任何有效的绝对或相对状态

回答by SirTophamHatt

[S]tep one is embedded on the homepage rather than being a separate state

[S]第一步嵌入在主页上,而不是单独的状态

You should treat each ui-view as a state, but declare wizard.whereas the default/index state.

您应该将每个 ui-view 视为一个状态,但声明wizard.where默认/索引状态

Note that the tutorial uses $urlRouterProvider to make form/profilethe default state.

请注意,本教程使用 $urlRouterProvider 来form/profile设置默认状态。

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

In this manner, however, /formwill end up as /form/profile.

然而,以这种方式,/form最终将成为/form/profile.

You may, however, create an empty URL state with minor modification:

但是,您可以通过少量修改创建一个空的 URL 状态:

// 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 has also provided great insight into UI-Router and state definitions.

@radim-k?hler 还提供了对 UI-Router 和状态定义的深入了解。