【问题标题】:Linking directly to both parent+child views/controllers from the main navigation menu从主导航菜单直接链接到父+子视图/控制器
【发布时间】:2017-01-06 08:38:29
【问题描述】:

考虑这个例子:

主路由器位于

app.js

  • someparent/childroute1
  • someparent/childroute2
  • 路线3

“someparent”是“基本控制器和视图”。它在视图、自定义元素和绑定中有一些可重用的 html 标记,这些标记将由其所有“子视图和控制器”共享。子视图和控制器将访问这些。

在“someparent.html”内(除了共享标记)还有一个<router-view>标签,子路由和页面应该在其中呈现,但在someparent.html内没有导航。

从 app.js 中的主路由器/路由应该可以单击链接并登陆 - 不是在父/基类“someparent”本身,而是直接在“someparent”“base/parent”的子类视图和控制器”,当您单击从 app.js 中的路由构建的 app.html 的导航菜单中的链接时(并且可能 someparent.js 中的路由将子路由器注入父级或什么?)。 /p>

所以本质上,我需要实现与基本路由几乎相同的功能 - 但正如我所提到的,我需要将这些路由/页面中的多个作为父视图/控制器的一部分。我在谷歌上搜索了好几周都找不到任何信息,所以希望这里的人能够理解我的问题,并且知道如何在 Aurelia 中以正确的方式解决这个问题?

【问题讨论】:

    标签: javascript aurelia


    【解决方案1】:

    创建一个类来包含您的共享状态并在您的视图模型中依赖该类。您可以使用NewInstance.of 解析器来控制何时创建共享状态以及何时重用。

    这是一个例子:https://gist.run?id=4cbf5e9fa71ad4f4041556e4595d3e36

    shared-state.js

    export class SharedState {
      fromdate = '';
      todate = '';
      language = 'English';
    }
    

    shared-parent.js

    import {inject, NewInstance} from 'aurelia-framework';
    import {SharedState} from './shared-state';
    
    @inject(NewInstance.of(SharedState)) // <-- this says create a new instance of the SharedState whenever a SharedParent instance is created
    export class SharedParent {
      constructor(state) {
        this.state = state;
      }
    
      configureRouter(config, router){
        config.title = 'Aurelia';
        config.map([
            { route: ['', 'child-a'], moduleId: './child-a', nav: true, title: 'Child A' },
            { route: 'child-b', moduleId: './child-b', nav: true, title: 'Child B' },
        ]);
    
        this.router = router;
      }
    }
    

    注意:如果您使用@inject(SharedState) 而不是@inject(NewInstance.of(SharedState)),则SharedState 的单个实例将与所有组件共享。这可能是你正在寻找的,我不确定。 @inject(NewInstance.of(SharedState)) 的目的是确保父级及其子级拥有自己的SharedState 实例。

    child-a.js

    import {inject} from 'aurelia-framework';
    import {SharedState} from './shared-state';
    
    @inject(SharedState)
    export class ChildA {
      constructor(state) {
        this.state = state;
      }
    }
    

    child-b.js

    import {inject} from 'aurelia-framework';
    import {SharedState} from './shared-state';
    
    @inject(SharedState)
    export class ChildB {
      constructor(state) {
        this.state = state;
      }
    }
    

    【讨论】:

    • 这是完美的答案。非常感谢您甚至花时间编写有效的代码示例。惊人! (“你可以在 20 小时内奖励你的赏金”.. bah ;-))
    • 我还有一个问题杰里米。每次更改“语言”(实际代码中的更多属性)时,我都会使用 SomethingChanged(newVal, oldVal){} 来修改其他一些视图模型属性。虽然 SharedState 方式很好,但我认为做类似 $parent.someprop 的事情会更容易吗?但这不起作用,因为孩子在路由器视图中而不是组合视图中,对吗?我喜欢 SharedState 的方式,但是说语言从父级发生了变化,当发生这种情况时,子级需要改变 SharedState 对象的一些其他值。你会怎么处理呢?
    • 我想坚持 SharedState 方法是最好的,但我不确定哪种观察方法是最好的。这里github.com/axwalker/aurelia-observer-patterns/wiki/… 提到了 BindingEngine、aurelia-event-aggregator、Signals,甚至还有一个名为 aurelia-flux 的插件。我不确定其中哪一个是观察和响应共享状态对象变化的最佳解决方案。
    • 我想我想通了,有点。只需将 SharedParent 注入 ChildA 等,在构造函数中引用 this.parent = parent 就可以很好地工作。我没有在 SharedState 对象中存储 10 多个属性,而是直接从子级设置它们,例如“parent.prop1”等。然后,每当父级发生变化时,我可以使用 eventtaggregator 发布事件,并监听给定的频道如果需要发生某些事情,请在孩子中重新加载或更新。我认为你回答的关键是路由——至少那对我帮助最大:-)
    • 我对此提出了后续问题。 stackoverflow.com/questions/42832255/…
    【解决方案2】:

    在更好地理解原始问题后,我会提出以下解决方案,它利用了 Aurelia 路由器中可用的“附加数据”参数。欲了解更多信息,请参阅http://aurelia.io/hub.html#/doc/article/aurelia/router/latest/router-configuration/4

    app.js

    configureRouter(config, router) {
      this.router = router;
      config.title = 'My Application Title';
      config.map([
        { route: ['', 'home'], name: 'home', moduleId: 'homefolder/home' },
        { route: 'someparent/child1', name: 'child1', moduleId: 'someparentfolder/someparent', settings: {data: 'child1'} },
        { route: 'someparent/child2', name: 'child2', moduleId: 'someparentfolder/someparent', settings: {data: 'child2'} },
        { route: 'someparent/child3', name: 'child3', moduleId: 'someparentfolder/someparent', settings: {data: 'child3'} }
      ]);
    }
    

    someparent.js

    import {inject} from 'aurelia-framework';
    import {Router} from 'aurelia-router';
    @inject(Router)
    export class SomeParent {
      constructor(router) {
        this.router = router;
      }
    }
    

    someparent.html

    <template>
      <require from="./child1"></require>
      <require from="./child2"></require>
      <require from="./child3"></require>
    
      <h1>Some Parent Page Title</h1>
    
      <div if.bind="router.currentInstruction.config.settings.data == 'child1'">
        <h2>Child Component 1</h2>
        <child-component-one linkeddata.bind="child1"></child-component-one>
      </div>
    
      <div if.bind="router.currentInstruction.config.settings.data == 'child2'">
        <h2>Child Component 2</h2>
        <child-component-two linkeddata.bind="child2"></child-component-two>
      </div>
    
      <div if.bind="router.currentInstruction.config.settings.data == 'child3'">
        <h2>Child Component 3</h2>
        <child-component-three linkeddata.bind="child3"></child-component-three>
      </div>
    
    </template>
    

    其他想法:

    我测试了上面的内容,它可以工作。希望通过使用路由设置数据参数,您可以向父路由器“获取消息”,以了解您想要显示哪个子路由器。根据您的特定应用程序,您可能更喜欢将其实现为子路由器,但正如我在上面演示的那样简单地绑定/取消绑定单个子视图是一个简单的解决方案。它还向您展示了如何访问您可以为 Aurelia 路由器中的每个路由提供的额外参数。

    【讨论】:

    • 嗨 LStarky。我感谢您的努力,但我不禁认为必须有更好的方法。我刚刚开始赏金,因为我希望就最佳解决方案提供一些权威答案。这似乎是一个非常有创意的解决方法,但如果没有其他方法,我什至更愿意将父级(具有可绑定属性和表单元素)转换为自定义元素,绑定它并直接链接到孩子,现在嵌入了“前父母”(现在是自定义元素)。但是,是的......只有当 Aurelia 没有干净的方法来做到这一点时:-)
    • 不用担心。我也会对最好的解决方案感兴趣!
    • @LStarky,你为什么在这里使用“附加数据”参数而不是路由名称? routeConfig.name 可以从 activate 函数中访问,而无需注入整个路由器。我是否遗漏了什么,或者这只是更一般的示例,旨在展示如何使用更复杂的参数?
    • 原问题需要向子路由器发送消息,并且不能从父路由器访问子路由名称。
    • 哦,我明白了。谢谢。
    【解决方案3】:

    我对 Aurelia 还比较陌生(大约 3 个月),所以可能会有更“专家”的答案,但你要做的是非常基本的。请记住,Aurelia 完全基于组件,以至于每个组件基本上都是页面上的一个元素。渲染“父”视图/控制器时,您的“子”视图/控制器只是该父页面上的元素。所以只需要渲染父页面并确保子页面链接正确即可。

    路由器(在 app.js 中):

    configureRouter(config, router) {
      this.router = router;
      config.title = 'My Application Title';
      config.map([
        { route: ['', 'home'], name: 'home', moduleId: 'homefolder/home' },
        { route: 'someparent', name: 'someparentnamefornamedroutes-optional', moduleId: 'someparentfolder/someparent' },
      ]);
    }
    

    父 ViewModel(在 someparentfolder/someparent.js 中)

    // imports go here, like: import { inject } from 'aurelia-framework';
    
    // injects go here, like: @inject(MyClass)
    export class SomeParent {
      child1 = {
        fname: "Debbie",
        lname: "Smith"
      };
      constructor() {
    
      }
    }
    

    父视图(在 someparentfolder/someparent.html 中)

    <template>
      <require from="./child1"></require>
      <require from="./child2"></require>
    
      <h1>Some Parent Page Title</h1>
    
      <h2>Child Component 1</h2>
      <child-component-one linkeddata.bind="child1"></child-component-one>
    
      <h2>Child Component 2</h2>
      <child-component-two></child-component-two>
    
    </template>
    

    子 1 ViewModel(在 someparentfolder/child1.js 中)

    import { inject, bindable, bindingMode } from 'aurelia-framework';
    import { Core } from 'core';
    
    @inject(Core)
    export class ChildComponentOne { // use InitCaps, which will be translated to dash case by Aurelia for the element ref in SomeParent
      @bindable({ defaultBindingMode: bindingMode.twoWay }) linkeddata;
      constructor(core) {
        this.core = core;
      }
      attached() {
        // example calling core function
        var response = this.core.myCustomFunction();
      }
    }
    

    子 1 视图(在 someparentfolder/child1.html 中)

    <template>
      <h3>Hello, ${linkeddata.fname}</h3>
      <p>Here's something from core: ${core.value1}</p>
    </template>
    

    (对 Child 2 ViewModel 和 View 使用相同的概念)

    直接导航到子组件:

    上述场景将每个子组件“嵌入”在 SomeParent 页面中。但是,如果您只想将每个子组件作为其自己的导航路由器视图打开(直接在您的主 &lt;router-view&gt;&lt;/router-view&gt; 内容窗口中打开),只需使用如下路由器定义:

    configureRouter(config, router) {
      this.router = router;
      config.title = 'My Application Title';
      config.map([
        { route: ['', 'home'], name: 'home', moduleId: 'homefolder/home' },
        { route: 'someparent', name: 'someparent', moduleId: 'someparentfolder/someparent' },
        { route: 'someparent/child1', name: 'child1', moduleId: 'someparentfolder/child1' },
        { route: 'someparent/child2', name: 'child2', moduleId: 'someparentfolder/child2' },
        { route: 'someparent/child3', name: 'child3', moduleId: 'someparentfolder/child3' }
      ]);
    }
    

    另一种情况:

    也许您正在寻找的是一个 Singular 类,其中包含一个用于存储所有组件都将访问的状态和常用函数的公共位置。我通过创建一个名为core.js 的模型来实现这一点。我已经在上面添加了这些详细信息,您还可以在项目根 (/src) 文件夹中创建以下文件。

    核心类(在 /src/core.js 中):

    // Some example imports to support the common class
    import { inject, noView } from 'aurelia-framework';
    import { HttpClient, json } from 'aurelia-fetch-client';
    import { I18N } from 'aurelia-i18n';
    import { EventAggregator } from 'aurelia-event-aggregator';
    
    @noView // this decorator is needed since there is no core.html
    @inject(EventAggregator, I18N, HttpClient)
    export class Core {
      value1 = "Test data 1";
      value2 = "Test data 2";
      constructor(eventAggregator, i18n, httpClient) {
        // store local handles
        this.eventAggregator = eventAggregator;
        this.i18n = i18n;
        this.httpClient = httpClient;
      }
    
      myCustomFunction() {
        // some code here, available to any component that has core.js injected
      }
    }
    

    装订注意事项:

    我为您提供了一个示例,说明如何使用 JavaScript 对象设置与子组件的双向绑定。 Aurelia 非常灵活,你可以用很多不同的方式来做这件事,但这似乎是相当标准的。如果您不需要孩子来操作 child1 中包含的数据,您可以删除 @bindable 装饰器上的括号注释,以便它只是 @bindable linkeddata;

    您可以添加多个参数来链接多条数据,或者将它们分组到一个对象或数组中。

    由于我还是一个相对较新的用户,我记得经历了所有这些。如果您有任何后续 cmets 或问题,请告诉我。

    无论如何,如果有任何真正的专家在看这个,也请教我! :-)

    【讨论】:

    • 感谢您的回答,但我需要能够从主要点击指向以下各项的链接:孩子 1、孩子 2、孩子 3 等(或者至少需要以这种方式工作)导航。据我所知,您提供的代码 sn-ps 只是将它们全部直接“嵌入”到父视图中
    • 当您说“单击链接”时,您是否希望它滚动到 SomeParent 页面的该部分?或者您是否希望它作为自己的页面导航到每个孩子?如果每个 Child 都显示为自己的页面,您仍然可以使用上述代码,只需更改路由器即可。我将编辑我的回复以添加该信息,但如果我仍然不理解,请更具体。
    • 也许您正在寻找的是一个 Singular 类,它包含一个用于存储所有组件都将访问的状态和常用函数的公共位置。我通过创建一个名为core.js 的类(没有视图)来实现这一点。稍后我会写出详细信息。
    • 我想从主应用路由器“直接导航到子路由”几乎是正确的。但不是“直接”,而是“到孩子,包裹在父母里面”,所以我想你需要先以某种方式路由到父母,然后从那里到孩子。但是由于父级中没有带有路线的导航或菜单,所以我对如何做到这一点有点迷茫。我非常感谢您的示例,但这并不是我所追求的。回家后我会尝试组装一个plunkr。可能会更容易理解
    • 父母有什么这么重要?如果它没有视图......它似乎没用。当然,除非您尝试使用父母为孩子提供一些信息,在这种情况下,上面的 core.js 可以为您做到这一点。
    猜你喜欢
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2012-01-02
    相关资源
    最近更新 更多