Interfacing between view-models in Aurelia - javascript

I have two views on the same page. View A’s view-model needs to call a method in view B’s view-model. Is that possible with Aurelia?
Also, is this the best approach? Would it be better to use the EventAggregator (pub/sub) to communicate between the view-models?
----- More Details -----
To be much more specific, there is a nav bar used in my app.html file like this:
<template>
<require from="nav-bar-view"></require>
<nav-bar-view></nav-bar-view>
<router-view></router-view>
</template>
View-models within the router-view need to be able to change the nav bar's title and button text.
My initial design was to use pub/sub to communicate the changes to the nav bar view-model. Since that seemed a bit messy and overcomplicated, I wanted to come up with a simpler approach.
My latest idea is to create a singleton NavBar class that is injected into the NavBarView class and the "consumer" view-models.
The following is a simplified version of the code:
nav-bar-view.html:
<template>
<div class="center">${navBar.title}</div>
</template>
nav-bar-view.js:
import {inject} from 'aurelia-framework';
import {NavBar} from 'nav-bar';
#inject(NavBar)
export class NavBarView {
constructor(navBar) {
this.navBar = navBar;
}
}
nav-bar.js:
import {singleton} from 'aurelia-framework';
#singleton()
export class NavBar {
constructor() {
this.title = '';
}
}
view.js (a consumer of the nav bar):
import {inject} from 'aurelia-framework';
import {NavBar} from 'nav-bar';
#inject(NavBar)
export class View {
constructor(navBar) {
this.navBar = navBar;
}
attached() {
this.navBar.title = 'This View's Title';
}
}
Again, this is much simpler than the actual code, but it servers to illustrate the idea.
I've tried it out and it works fine. Does this make sense? Is there a better way?

pub/sub would work but I suspect you're looking for something a little more targeted than that.
The preferred way to pass something into a custom element is via a bindable property. Assuming you have component-a and component-b and A needs to call a method on B's view-model. Here's what you could do:
get a reference to B's view-model so we can bind to it's properties and methods:
<component-b view-model.ref="b"></component-b>
Add bindable property to component A so we can give component A the reference to B's method.
import {bindable} from 'aurelia-framework';
export class ComponentA {
#bindable sayHello;
}
Bind component A's sayHello property to B's sayHello method.
<component-a say-hello.call="b.sayHello()"></component-a>
Here's a runnable example: https://gist.run/?id=91269472d4e6509e32123ca2a63dd9ca
Edit
Based on the updated information in the question, here's what I would recommend:
1. Create a class that contain's your nav-bar state
export class NavState {
title = 'some default title';
}
2. Take a dependency on the NavState in your nav-bar component
#inject(NavState)
export class NavBar {
constructor(state) {
this.state = state; // now you can bind to "state.title" in nav-bar.html
}
...
}
3. Take a dependency on the NavState in components that need to change the title.
#inject(NavState)
export class MyComponentThatChangesTheTitle {
constructor(state) {
this.state.title = 'something else';
}
...
}
This will be more flexible than passing around a component's viewmodel as state. For example, with this model, you can configure the title before the nav-bar is even instantiated.

Related

Testcafe - Page object structure and default class

I'm modeling a web page with a lot of items on it. Coming from a Ruby background, I had one class for each, say, large item and its subitems on the page. For instance, a navbar would be its own class:
import { Selector, t } from 'testcafe';
export class NavBar {
constructor () {
this.home = Selector('#home')
this.intro = Selector('#intro')
...
}
}
export class HeaderSection {
...
}
Questions:
Do I need a default class? My IDE is complaining, but the test work. I believe, the answer is no, but it's a good practice(?)
What's the recommended way to write a complex page model in JavaScript? I'm leaning to have one-page class, say index and then have multiple child classes (Navbar and HeaderSection on my example) that inherit from the index class
This is what I think it should be:
import { Selector, t } from 'testcafe';
export default class Index {
}
export class NavBar extends Index {
constructor () {
super ();
this.home = Selector('#home')
this.intro = Selector('#intro')
...
}
}
export class HeaderSection extends Index {
constructor () {
super ();
...
}
}
so when I import the page model into my test case, I can call import Index from ../pages/index_page.js
Do I need a default class? My IDE is complaining, but the test work. I believe, the answer is no, but it's a good practice(?)
It's not necessary. The default keyword determines the way of the export in JavaScript. You can organize page objects as you like.
What's the recommended way to write a complex page model in JavaScript? I'm leaning to have one page class, say index and then have multiple child classes (Navbar and HeaderSection on my example) that inherit from the index class
It depends on page complexity. If test page is simple then one page object class for one page is enough. If test page is complex, creating separate classes for complex controls is a good approach.

writing function outside of a class in react component

I've seen code like this
function abc(){
return 'abc'
}
class MyComponent extends React.Component {
static abc = abc;
render() { return <h1>{this.abc}</h1>; }
}
where function abc is defined outside of a react class. I have no clue why the author did it that way, why can't just do it within the class?
These are ES6 static methods and are not exclusive to React. They are members of the component class and not of instances of the component. They are not used extensively in React, but they can be useful. It is even mentioned in the React docs:
Sometimes it’s useful to define a static method on a React component.
For example, Relay containers expose a static method getFragment to
facilitate the composition of GraphQL fragments.
They can be used as common members of the Component, shared by all instances of it. To give you an idea, other static members of a React class are displayName and defaultProps.
Also see Static methods in React. As you can see, there aren't many cases where you would use a static method.
For one thing declaring functions outside the class are easier to EXPORT. Which serves a great deal when testing your react application i.e. through jest.
import React, { Component } from 'react';
class Contacts extends Component {
render() {
return (
Contact()
);
}
}
const Contact = () => {
return (
<div>
<p>Contactssssss</p>
</div>
);
};
export default Contacts;

How to get route parameter in aurelia custom component

I'm creating a custom component in aureliajs framework and injecting it a Router instance:
#inject(Router)
export class Pagination {
constructor(router) {
this.router = router;
}
}
It will be used with list view-models in order to setup some basic pagination. Therefore I need to read current page number from an active route (that would look like: orders/:pageNum. I'm not sure however how to do it? I mean - I know it should probably be placed in Pagination attached method, but how to get to this :pageNum param?
Create a bindable property in your custom element. Take the page number from the active route and bind it to the custom element. For instance:
import { bindable } from 'aurelia-framework';
#inject(Router)
export class Pagination {
#bindable page;
constructor(router) {
this.router = router;
}
}
Usage:
<pagination page.bind="page"></pagination>
To get the page number in the active route, use the activate() hook:
activate(params) {
this.page = params.pageNum;
}
You can use router.currentInstruction.params and router.currentInstruction.queryParams.
You can also observe the router to be notified when the route changes:
let sub = this.bindingEngine.propertyObserver(
this.router, 'currentInstruction').subscribe(currentInstruction => {
console.log(currentInstruction);
});

Difference between decorators and mixins in react

I am a beginner to react and finding myself confused between mixin and decorators. Can somebody elaborate? Thanks.
They both extend and/or override methods of the React Component. They are used to share common functionality between components, in places where extending a class would not work and is not intended.
An example would be the PureRenderMixin, which overrides the shouldComponentUpdate method and compares the props of a component to decide, if a rerender should be executed.
However, mixins are deprecated and do not work with the ES6 syntax of React anymore. Your options are either to use inheritance or decorators to achieve the same result.
Example
Here is an example of the (kind of) PureRenderMixin, using a decorator. Also I used Immutable.js.
// pure.js
import React from 'react';
import assign from 'object-assign';
import {is} from 'immutable';
/**
* Pure Render Decorator
* #param props
* #returns {function()}
*/
export default (...props) => (Component) => class PureComponent extends React.Component {
shouldComponentUpdate(nextProps) {
if (!props.length) {
props = Object.keys(nextProps);
}
for (let i = 0, l = props.length; i < l; i++) {
if (!is(nextProps[props[i]], this.props[props[i]])) {
return true;
}
}
return false;
}
render() {
return React.createElement(Component, assign({},
this.props,
this.state
));
}
}
The general usage of the decorator would be #pure(params).
params can contain the name of the props or can be empty. In the decorator you see the ...props as a parameter. This is where the params are passed in.
The parameter Component of the inner function gets the React Component passed in, on which you use the decorator.
You can use the decorator in your component as follows:
import React from 'react';
import pure from './pure.js';
#pure('myProp', 'anotherProp')
export default MyComponent extends React.Component {
static propTypes = {
myProp: React.PropTypes.any,
anotherProp: React.PropTypes.any
}
render() {
return <p>I only re-render, when my props changed.</p>;
}
}
What does it do?
The decorator overrides the shouldComponentUpdate method of the component. Everytime the React Component calls its shouldComponentUpdate method, it now uses the one provided in the decorator.
The decorator itself compares the props of the Component to the next props that it is going to receive. Only if the props change, the component will update.
This is nice, because it prevents unnecessary rendering - that's great for performance!
You see, decorators are basically functions that take parameters (such as the React Component) and modify them in order to make code reusable. It takes a bit of getting used to, but its no rocket science. :-)
If there are any more questions, please feel free to ask!

Angular2: Creating child components programmatically

Question
How to create child components inside a parent component and display them in the view afterwards using Angular2? How to make sure the injectables are injected correctly into the child components?
Example
import {Component, View, bootstrap} from 'angular2/angular2';
import {ChildComponent} from './ChildComponent';
#Component({
selector: 'parent'
})
#View({
template: `
<div>
<h1>the children:</h1>
<!-- ??? three child views shall be inserted here ??? -->
</div>`,
directives: [ChildComponent]
})
class ParentComponent {
children: ChildComponent[];
constructor() {
// when creating the children, their constructors
// shall still be called with the injectables.
// E.g. constructor(childName:string, additionalInjectable:SomeInjectable)
children.push(new ChildComponent("Child A"));
children.push(new ChildComponent("Child B"));
children.push(new ChildComponent("Child C"));
// How to create the components correctly?
}
}
bootstrap(ParentComponent);
Edit
I found the DynamicComponentLoader in the API docs preview. But I get the following error when following the example: There is no dynamic component directive at element 0
This is generally not the approach I would take. Instead I would rely on databinding against an array that will render out more child components as objects are added to the backing array. Essentially child components wrapped in an ng-for
I have an example here that is similar in that it renders a dynamic list of children. Not 100% the same, but seems like the concept is still the same:
http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0
Warning: DynamicComponentLoader has been deprecated in RC.4
In Angular 2.0, loadIntoLocation method of DynamicComponentLoader serve this purpose of creating parent-child relationship. By using this approach you can dynamically create relationship between two components.
Here is the sample code in which paper is my parent and bulletin is my child component.
paper.component.ts
import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
import { BulletinComponent } from './bulletin.component';
#Component({
selector: 'paper',
templateUrl: 'app/views/paper.html'
}
})
export class PaperComponent {
constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
}
ngOnInit(){
this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');
}
}
bulletin.component.ts
import {Component} from 'angular2/core';
#Component({
selector: 'bulletin',
template: '<div>Hi!</div>'
}
})
export class BulletinComponent {}
paper.html
<div>
<div #child></div>
</div>
Few things you needs to be take care of are mentioned in this answer
You should use ComponentFactoryResolver and ViewElementRef to add component at runtime.Let's have a look at below code.
let factory = this.componentFactoryResolver.resolveComponentFactory(SpreadSheetComponent);
let res = this.viewContainerRef.createComponent(factory);
Put the above code inside your ngOnInit function and replace "SpreadSheetComponent" by your component name.
Hope this will work.
Programmatically add components to DOM in Angular 2/4 app
We need to use ngAfterContentInit() lifecycle method from AfterContentInit. It is called after the directive content has been fully initialized.
In the parent-component.html, add the a div like this:
<div #container> </div>
The parent-component.ts file looks like this:
class ParentComponent implements AfterContentInit {
#ViewChild("container", { read: ViewContainerRef }) divContainer
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
ngAfterContentInit() {
let childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(childComponent);
this.divContainer.createComponent(childComponentFactory);
let childComponentRef = this.divContainer.createComponent(childComponentFactory);
childComponentRef.instance.someInputValue = "Assigned value";
}
}
Inside src\app\app.module.ts, add the following entry to the #NgModule() method parameters:
entryComponents:[
childComponent
],
Notice that we're not accessing the div#container using the #ViewChild("container") divContainer approach. We need it's reference instead of the nativeElement. We will access it as ViewContainerRef:
#ViewChild("container", {read: ViewContainerRef}) divContainer
The ViewContainerRef has a method called createComponent() which requires a component factory to be passed as a parameter. For the same, we need to inject a ComponentFactoryResolver. It has a method which basically loads a component.
The right approach depends on the situation you're trying to solve.
If the number of children is unknown then NgFor is the right approach.
If it is fixed, as you mentioned, 3 children, you can use the DynamicComponentLoader to load them manually.
The benefits of manual loading is better control over the elements and a reference to them within the Parent (which can also be gained using templating...)
If you need to populate the children with data, this can also be done via injection, the Parent is injected with a data object populating the children in place...
Again, a lot of options.
I have used 'DynamicComponentLoader' in my modal example, https://github.com/shlomiassaf/angular2-modal

Categories

Resources