Skip to content Skip to sidebar Skip to footer

Linking Directly To Both Parent+child Views/controllers From The Main Navigation Menu

Consider this example: The main router is located in app.js someparent/childroute1 someparent/childroute2 route3 'someparent' is the 'base controller and view'. It has some reu

Solution 1:

Create a class to contain your shared state and take a dependency on that class in your view-models. You can use the NewInstance.of resolver to control when shared state is created vs reused.

Here's an example: https://gist.run?id=4cbf5e9fa71ad4f4041556e4595d3e36

enter image description here

shared-state.js

exportclassSharedState {
  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 createdexportclassSharedParent {
  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;
  }
}

note: if you use @inject(SharedState) instead of @inject(NewInstance.of(SharedState)), a single instance of SharedState will be shared with all components. This may be what you are looking for, I wasn't sure. The purpose of @inject(NewInstance.of(SharedState)) is to make sure the parent and it's children have their own SharedState instance.

child-a.js

import {inject} from'aurelia-framework';
import {SharedState} from'./shared-state';

@inject(SharedState)
exportclassChildA {
  constructor(state) {
    this.state = state;
  }
}

child-b.js

import {inject} from'aurelia-framework';
import {SharedState} from'./shared-state';

@inject(SharedState)
exportclassChildB {
  constructor(state) {
    this.state = state;
  }
}

Solution 2:

After better understanding the original question, I would propose the following solution, which takes advantage of the "Additional Data" parameter available in Aurelia's router. For more information, see 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)
exportclassSomeParent {
  constructor(router) {
    this.router = router;
  }
}

someparent.html

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

Additional thoughts:

I tested the above and it works. Hopefully by using the route settings data parameters you can "get the message through" to the parent router as to which child you want displayed. Depending on your specific application, you may prefer to implement this as a sub-router, but simply binding/unbinding the individual child views as I've demonstrated above is a simple solution. It also shows you how to access the extra parameters you can supply with each route in Aurelia's router.

Solution 3:

I'm still relatively new to Aurelia (about 3 months) so there might be a more "expert" answer out there, but what you're trying to do is quite basic. Remember that Aurelia is based completely on components, to the point that every component is basically an element on a page. When rendering the "parent" view/controller, your "child" view/controllers are just elements on that parent page. So you only need to render the parent page and ensure that the child pages are linked correctly.

Router (in 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' },
  ]);
}

Parent ViewModel (in someparentfolder/someparent.js)

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

  }
}

Parent View (in someparentfolder/someparent.html)

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

Child 1 ViewModel (in someparentfolder/child1.js)

import { inject, bindable, bindingMode } from'aurelia-framework';
import { Core } from'core';

@inject(Core)
exportclassChildComponentOne { // 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 functionvar response = this.core.myCustomFunction();
  }
}

Child 1 View (in someparentfolder/child1.html)

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

(Use same concepts for Child 2 ViewModel and View)

Navigation Directly to Child Components:

The above scenario has each of the child components "embedded" in the SomeParent page. However, if you want to simply open each Child Component as its own navigation router view (to open directly in your main <router-view></router-view> content window), just use a router definition like this:

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' }
  ]);
}

One More Scenario:

Perhaps what you're looking for is a Singular class that contains a common place to store state and common functions that all of your components will access. I implemented this by creating a model called core.js. I've added those details above, and you would also create the following file in your project root (/src) folder.

Core Class (in /src/core.js):

// Some example imports to support the common classimport { 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)
exportclassCore {
  value1 = "Test data 1";
  value2 = "Test data 2";
  constructor(eventAggregator, i18n, httpClient) {
    // store local handlesthis.eventAggregator = eventAggregator;
    this.i18n = i18n;
    this.httpClient = httpClient;
  }

  myCustomFunction() {
    // some code here, available to any component that has core.js injected
  }
}

Notes on Binding:

I gave you an example of how to set up two-way binding to the child component with a JavaScript object. Aurelia is very flexible and you could do this a lot of different ways but this seems to be fairly standard. If you don't need the child to manipulate the data contained in child1, you could delete the parenthetical notes on the @bindable decorator so it would simply be @bindable linkeddata;

You can add multiple parameters to link more than one piece of data, or group them into an object or array.

Since I'm still a relatively new user, I remember going through all of this. Please let me know if you have any follow-up comments or questions.

And by all means, if there are any true experts watching this, please teach me as well! :-)

Post a Comment for "Linking Directly To Both Parent+child Views/controllers From The Main Navigation Menu"