I want to create a tree in VS code, but my problem is how to manually add a node to my tree. I am not sure from where to start. I tried to review all the projects that created a tree for VScode as an extension.
My problem is that I am not an expert in Typescript and the examples are not so clear or I am not sure how it is working.
Would you mind helping me to understand how to create the tree in VS code? My problem is with creating a node and then adding the node to tree.
I reviewed these projects:
vscode-code-outline
vscode-extension-samples
vscode-git-tree-compare
vscode-html-languageserver-bin
vscode-mock-debug
vscode-tree-view
Update1:
I managed to use "vscode-extension-samples" and generate the below code examples; now I don't know what I should do, or in other words, how to fill the tree. I tried to use mytree class to fill the data but it didn't work. Would you mind advising me what is next?
extension.ts
'use strict';
import * as vscode from 'vscode';
import { DepNodeProvider } from './nodeDependencies'
import { JsonOutlineProvider } from './jsonOutline'
import { FtpExplorer } from './ftpExplorer.textDocumentContentProvider'
import { FileExplorer } from './fileExplorer';
//mycode
import { SCCExplorer } from './sccExplorer';
export function activate(context: vscode.ExtensionContext) {
// Complete Tree View Sample
new FtpExplorer(context);
new FileExplorer(context);
//mycode
new SCCExplorer(context);
// Following are just data provider samples
const rootPath = vscode.workspace.rootPath;
const nodeDependenciesProvider = new DepNodeProvider(rootPath);
const jsonOutlineProvider = new JsonOutlineProvider(context);
vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider);
vscode.commands.registerCommand('nodeDependencies.refreshEntry', () => nodeDependenciesProvider.refresh());
vscode.commands.registerCommand('nodeDependencies.addEntry', node => vscode.window.showInformationMessage('Successfully called add entry'));
vscode.commands.registerCommand('nodeDependencies.deleteEntry', node => vscode.window.showInformationMessage('Successfully called delete entry'));
vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`)));
vscode.window.registerTreeDataProvider('jsonOutline', jsonOutlineProvider);
vscode.commands.registerCommand('jsonOutline.refresh', () => jsonOutlineProvider.refresh());
vscode.commands.registerCommand('jsonOutline.refreshNode', offset => jsonOutlineProvider.refresh(offset));
vscode.commands.registerCommand('jsonOutline.renameNode', offset => jsonOutlineProvider.rename(offset));
vscode.commands.registerCommand('extension.openJsonSelection', range => jsonOutlineProvider.select(range));
}
sccExplorer.ts
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import * as rimraf from 'rimraf';
//#region Utilities
interface Entry {
uri: vscode.Uri,
type: vscode.FileType
}
//#endregion
export class FileSystemProvider implements vscode.TreeDataProvider<Entry> {
getTreeItem(element: Entry): vscode.TreeItem | Thenable<vscode.TreeItem> {
throw new Error("Method not implemented.");
}
onDidChangeTreeData?: vscode.Event<Entry>;
getChildren(element?: Entry): vscode.ProviderResult<Entry[]> {
throw new Error("Method not implemented.");
}
getParent?(element: Entry): vscode.ProviderResult<Entry> {
throw new Error("Method not implemented.");
}
private _onDidChangeFile: vscode.EventEmitter<vscode.FileChangeEvent[]>;
constructor() {
this._onDidChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
}
}
export class SCCExplorer {
private fileExplorer: vscode.TreeView<any>;
constructor(context: vscode.ExtensionContext) {
const treeDataProvider = new myTree().directories;
this.fileExplorer = vscode.window.createTreeView('scc_Explorer', { treeDataProvider });
vscode.commands.registerCommand('scc_Explorer.openFile', (resource) => this.openResource(resource));
}
private openResource(resource: vscode.Uri): void {
vscode.window.showTextDocument(resource);
}
}
export class myTree{
directories: any;
constructor()
{
this.directories = [
{
name: 'parent1',
child: [{
name: 'child1',
child: []
},
{
name: 'child2',
child: []
}]
},
{
name: 'parent2',
child: {
name: 'child1',
child: []
}
},
{
name: 'parent2',
child: [{
name: 'child1',
child: []
},
{
name: 'child2',
child: []
}]
}];
}
}
I finally got it working. It took me very long and I had it right all the time. My issue was I never did explicitly expand the items, so I would never see nested results and only the top level.
Basic working example
import * as vscode from "vscode";
export class OutlineProvider
implements vscode.TreeDataProvider<any> {
constructor(private outline: any) {
console.log(outline);
}
getTreeItem(item: any): vscode.TreeItem {
return new vscode.TreeItem(
item.label,
item.children.length > 0
? vscode.TreeItemCollapsibleState.Expanded
: vscode.TreeItemCollapsibleState.None
);
}
getChildren(element?: any): Thenable<[]> {
if (element) {
return Promise.resolve(element.children);
} else {
return Promise.resolve(this.outline);
}
}
}
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
"outliner.outline",
async () => {
vscode.window.registerTreeDataProvider(
"documentOutline",
new OutlineProvider([dataObject])
);
}
);
context.subscriptions.push(disposable);
}
const dataObject = {
label: "level one",
children: [
{
label: "level two a",
children: [
{
label: "level three",
children: [],
},
],
},
{
label: "level two b",
children: [],
},
],
}
And of course in package.json
"contributes": {
"commands": [
{
"command": "outliner.outline",
"title": "Outline"
}
],
"views": {
"explorer": [
{
"id": "documentOutline",
"name": "Document Outline"
}
]
}
},
Types
note the type for treeDataProvider is not neccecarly what you return. Only the getTree item has to return a tree item or a class that extends it.
interface CustomType {
label: string
children?: CustomType[]
}
export class TypeExample
implements vscode.TreeDataProvider<CustomType> {
constructor(private data: CustomType[]) { }
getTreeItem(element: CustomType): vscode.TreeItem {
return new vscode.TreeItem(
element.label,
(element.children?.length ?? 0) > 0
? vscode.TreeItemCollapsibleState.Expanded
: vscode.TreeItemCollapsibleState.None
);
}
getChildren(element?: CustomType): Thenable<CustomType[]> {
return element && Promise.resolve(element.children ?? [])
|| Promise.resolve(this.data);
}
}
I thought at first the type of the data provider should be the return type of the tree item, this doesnt make much sense of course and I was trying to wrap my head around the reasoning. Now I understand that you pass your custom type in and all other methods inherit this type and expect this type as its argument. Only the getTreeItem method has to return a valid tree item that can be rendered.
Related
we are using ngxs and we do have some lazy selectors defined in separated files from the state definition
export class SectionSelectors {
#Selector([CatalogState])
static ById(state: CatalogModel) {
return function getSectionById(id: number): Section {
const selectedSection: Section = state.sections[id];
return selectedSection;
};
}
}
And we have test cases like
import { TestBed } from '#angular/core/testing';
import { Section } from '#miq-catalog/catalog';
import { NgxsModule, Store } from '#ngxs/store';
import { CatalogModel, CatalogState } from './catalog.state';
import { SectionSelectors } from './section.selectors';
describe('SectionSelectors', () => {
it('should select the section by id', () => {
const one: Section = { sectionId: 1, title: '', columns: [] };
const two: Section = { sectionId: 2, title: '', columns: [] };
const state: CatalogModel = {
catalog: [],
sections: { 1: one, 2: two },
columns: {},
catalogLoaded: true,
};
const selectionFunction = SectionSelectors.ById(state);
const result = selectionFunction(1);
expect(result).toBeDefined();
expect(result).toBe(one);
expect(result.sectionId).toBe(1);
const result2 = selectionFunction(2);
expect(result2).toBeDefined();
expect(result2).toBe(two);
expect(result2.sectionId).toBe(2);
});
});
We are passing the state to the selector however we are getting the next error
An error was thrown in afterAll
Uncaught ReferenceError: Cannot access 'CatalogState' before initialization
ReferenceError: Cannot access 'CatalogState' before initialization
I noticed that if I move these selector to the CatalogState (where the #State definition is) the problem is fixed. But this is forcing us to put all selectors there and we think it's good to have them scoped on their own related files so we don't "pollute" with mixed selectors.
Is there a way we can fix the test case? Does someone already faced this Lazy Selector testing before?
As complementary info this is how our State looks like
#State({
name: 'Catalog',
defaults: {
catalogLoaded: false,
columns: {},
sections: {},
catalog: [],
},
})
export class CatalogState {
constructor(private store: Store) {}
#Action(RetrieveCatalogInfo)
#Action(ChangeColumnConfig)
#Action(ClearCatalog)
public executeAction(ctx: StateContext<CatalogModel>, params: ExecutableAction<CatalogModel>) {
return params.execute({ ctx, store: this.store });
}
}
This should not be a problem with the latest version of NGXS (since v3.6.1).
I'm brand new to Angular and typescript and still trying to make it through but now I can't.
I bought a template for Angular (VEX) and I would like to integrate data from firebase into a datatable already present in the template.
In the template, this table is fed by static data and I would like to replace that with my call to firebase.
I am really lost and I would like to understand how I can do to get there.
Here is the manage-users-component.ts
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '#angular/core';
import { Observable, of, ReplaySubject } from 'rxjs';
import { filter } from 'rxjs/operators';
import { Customer } from './interfaces/customer.model';
import { MatTableDataSource } from '#angular/material/table';
import { MatPaginator } from '#angular/material/paginator';
import { MatSort } from '#angular/material/sort';
import { MatDialog } from '#angular/material/dialog';
import { TableColumn } from '../../../#vex/interfaces/table-column.interface';
import { aioTableData, aioTableLabels } from '../../static-data/aio-table-data';
import { CustomerCreateUpdateComponent } from './customer-create-update/customer-create-update.component';
import icEdit from '#iconify/icons-ic/twotone-edit';
import icDelete from '#iconify/icons-ic/twotone-delete';
import icSearch from '#iconify/icons-ic/twotone-search';
import icAdd from '#iconify/icons-ic/twotone-add';
import icFilterList from '#iconify/icons-ic/twotone-filter-list';
import { SelectionModel } from '#angular/cdk/collections';
import icMoreHoriz from '#iconify/icons-ic/twotone-more-horiz';
import icFolder from '#iconify/icons-ic/twotone-folder';
import { fadeInUp400ms } from '../../../#vex/animations/fade-in-up.animation';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions } from '#angular/material/form-field';
import { stagger40ms } from '../../../#vex/animations/stagger.animation';
import { FormControl } from '#angular/forms';
import { UntilDestroy, untilDestroyed } from '#ngneat/until-destroy';
import { MatSelectChange } from '#angular/material/select';
import icPhone from '#iconify/icons-ic/twotone-phone';
import icMail from '#iconify/icons-ic/twotone-mail';
import icMap from '#iconify/icons-ic/twotone-map';
import firebase from 'firebase';
import { UserManageService } from '../../services/user-manage.service';
#UntilDestroy()
#Component({
selector: 'vex-manage-users',
templateUrl: './manage-users.component.html',
styleUrls: ['./manage-users.component.scss'],
animations: [
fadeInUp400ms,
stagger40ms
],
providers: [
{
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: {
appearance: 'standard'
} as MatFormFieldDefaultOptions
}
]
})
export class ManageUsersComponent implements OnInit, AfterViewInit {
layoutCtrl = new FormControl('boxed');
/**
* Simulating a service with HTTP that returns Observables
* You probably want to remove this and do all requests in a service with HTTP
*/
subject$: ReplaySubject<Customer[]> = new ReplaySubject<Customer[]>(1);
data$: Observable<Customer[]> = this.subject$.asObservable();
customers: Customer[];
#Input()
columns: TableColumn<Customer>[] = [
{ label: 'Checkbox', property: 'checkbox', type: 'checkbox', visible: true },
{ label: 'ShipTo', property: 'extId', type: 'text', visible: true },
{ label: 'uid', property: 'uid', type: 'text', visible: true },
{ label: 'Compagny', property: 'compagny', type: 'text', visible: true },
{ label: 'Name', property: 'name', type: 'text', visible: true, cssClasses: ['font-medium'] },
{ label: 'First Name', property: 'firstName', type: 'text', visible: false },
{ label: 'Last Name', property: 'lastName', type: 'text', visible: false },
{ label: 'Email', property: 'email', type: 'text', visible: true },
{ label: 'Phone', property: 'phone', type: 'text', visible: true },
{ label: 'Role', property: 'role', type: 'text', visible: true },
{ label: 'Actions', property: 'actions', type: 'button', visible: true }
];
pageSize = 10;
pageSizeOptions: number[] = [5, 10, 20, 50];
dataSource: MatTableDataSource<Customer> | null;
selection = new SelectionModel<Customer>(true, []);
searchCtrl = new FormControl();
labels = aioTableLabels;
icPhone = icPhone;
icMail = icMail;
icMap = icMap;
icEdit = icEdit;
icSearch = icSearch;
icDelete = icDelete;
icAdd = icAdd;
icFilterList = icFilterList;
icMoreHoriz = icMoreHoriz;
icFolder = icFolder;
#ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
#ViewChild(MatSort, { static: true }) sort: MatSort;
constructor(private dialog: MatDialog,
private usersManageService: UserManageService ) {
}
get visibleColumns() {
return this.columns.filter(column => column.visible).map(column => column.property);
}
/**
* Example on how to get data and pass it to the table - usually you would want a dedicated service with a HTTP request for this
* We are simulating this request here.
*/
getData() {
return of(aioTableData.map(customer => new Customer(customer)));
}
ngOnInit() {
const users = this.usersManageService.getUsers();
console.log(users);
this.getData().subscribe(customers => {
this.subject$.next(customers);
});
console.log(aioTableData);
this.dataSource = new MatTableDataSource();
this.data$.pipe(
filter<Customer[]>(Boolean)
).subscribe(customers => {
this.customers = customers;
this.dataSource.data = customers;
});
this.searchCtrl.valueChanges.pipe(
untilDestroyed(this)
).subscribe(value => this.onFilterChange(value));
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
createCustomer() {
this.dialog.open(CustomerCreateUpdateComponent).afterClosed().subscribe((customer: Customer) => {
/**
* Customer is the updated customer (if the user pressed Save - otherwise it's null)
*/
if (customer) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
this.customers.unshift(new Customer(customer));
this.subject$.next(this.customers);
}
});
}
updateCustomer(customer: Customer) {
this.dialog.open(CustomerCreateUpdateComponent, {
data: customer
}).afterClosed().subscribe(updatedCustomer => {
/**
* Customer is the updated customer (if the user pressed Save - otherwise it's null)
*/
if (updatedCustomer) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
const index = this.customers.findIndex((existingCustomer) => existingCustomer.uid === updatedCustomer.uid);
this.customers[index] = new Customer(updatedCustomer);
this.subject$.next(this.customers);
}
});
}
deleteCustomer(customer: Customer) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
this.customers.splice(this.customers.findIndex((existingCustomer) => existingCustomer.uid === customer.uid), 1);
this.selection.deselect(customer);
this.subject$.next(this.customers);
}
deleteCustomers(customers: Customer[]) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
customers.forEach(c => this.deleteCustomer(c));
}
onFilterChange(value: string) {
if (!this.dataSource) {
return;
}
value = value.trim();
value = value.toLowerCase();
this.dataSource.filter = value;
}
toggleColumnVisibility(column, event) {
event.stopPropagation();
event.stopImmediatePropagation();
column.visible = !column.visible;
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
trackByProperty<T>(index: number, column: TableColumn<T>) {
return column.property;
}
// onLabelChange(change: MatSelectChange, row: Customer) {
// const index = this.customers.findIndex(c => c === row);
// this.customers[index].labels = change.value;
// this.subject$.next(this.customers);
// }
}
Here is the Firebase Realtime database "users" that I would like to insert in my datatable
Here is the function I imaginated for do the job but I dont know where to put it into
getUsers() {
firebase.database().ref('/users').once('value').then((snapshot) => {
const users = snapshot.val();
return users;
});
}
I am completely stuck and I thank very much in advance anyone who can help me.
I made some progress in my case and I was able to recover the values I wanted.
The array returned by my function matches the array of the example template.
getData() {
const users = [];
firebase.database().ref('/users').once('value').then((snapshot) => {
users.push(snapshot.val()) ;
const users2 = users[0];
const mapped = Object.keys(users2).map(key => (users2[key]));
console.log(mapped.map(customer => new Customer(customer)));
// console.log(aioTableData.map(customer => new Customer(customer)));
return of(mapped.map(customer => new Customer(customer)));
});
// return of(aioTableData.map(customer => new Customer(customer)));
The problem is that in ngOnInit, when the call to the function is done, it expects a subscribe and I don't know how to do it.
ngOnInit() {
this.getData().subscribe(customers => {
this.subject$.next(customers);
});
this.dataSource = new MatTableDataSource();
this.data$.pipe(
filter<Customer[]>(Boolean)
).subscribe(customers => {
this.customers = customers;
this.dataSource.data = customers;
});
this.searchCtrl.valueChanges.pipe(
untilDestroyed(this)
).subscribe(value => this.onFilterChange(value));
}
An idea?
I'm fairly new to dat.GUI and I'm having problems getting it to work with a more complex variable.
I have the following:
let complexVariable= {
topLayer:
{
deeperLayer:
{
deepestLayer: 20,
//Other stuff
}
}
}
let gui = new dat.GUI();
gui.add(complexVariable, "topLayer.deeperLayer.deepestLayer", 10, 40);
This gives me the following error:
Uncaught Error: Object "[object Object]" has no property "topLayer.deeperLayer.deepestLayer"
Any help here would be appreciated.
It currently doesn't seem possible by looking at the source code. They use bracket notation with the single property you pass in on the object.
function add(gui, object, property, params) {
if (object[property] === undefined) {
throw new Error(`Object "${object}" has no property "${property}"`);
}
...
So what you are telling dat.GUI to do is find a top level property "topLayer.deeperLayer.deepestLayer" and that obviously does not exist on your object. It seems like more code would have to be written to support nested properties.
dat.gui would have to do something like if (object[property1][property2][...] === undefined) or in your case - complexVariable["topLayer"]["deeperLayer"]["deepestLayer"];
It's obvious that question is ~4 years old. But I was searching for the same and I found your question before realizing that it's possible. Below is E2E sample in Angular.
import { AfterViewInit, Component, ViewChild, ViewContainerRef } from '#angular/core';
import { GUI, GUIController, GUIParams } from 'dat.gui';
export type EditorMetadata = Array<IEditorFolder>;
export interface IDatState {
state: EditorMetadata;
instance: GUI;
metadata: EditorMetadata;
}
export interface IEditorFolder {
title: string;
property?: string;
tooltip: string;
elements?: Array<IEditorElement>;
folders?: Array<IEditorFolder>;
folderRef?: GUI;
}
export interface IEditorElement {
title: string;
tooltip: string;
options?: any;
elementRef?: GUIController;
target?: Object;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
#ViewChild('container', { read: ViewContainerRef, static: true })
container: ViewContainerRef;
constructor() { }
ngAfterViewInit(): void {
this.composeEditor(this.container.element.nativeElement, options, foldersMetadata);
}
composeEditor(container: HTMLElement, editorOptions: GUIParams, editorMetadata: EditorMetadata): IDatState {
const
addFolder = (folder: IEditorFolder, parent: GUI): GUI => {
const
{ title, tooltip } = folder,
folderController = parent.addFolder(title);
folderController.domElement.setAttribute('title', tooltip);
return folderController;
},
addElement = (folder: GUI, element: IEditorElement, target: Object): GUIController => {
const
{ title, options, tooltip } = element,
elmController = folder
.add(target, title, options)
.name(title)
.onChange((value) => {
console.log(model);
});
elmController.domElement.setAttribute('title', tooltip);
return elmController;
},
createDat = (options: GUIParams, metadata: EditorMetadata): IDatState => {
const
state: IDatState = {
instance: new GUI(options),
state: JSON.parse(JSON.stringify(metadata)), // Don't touch original metadata, instead clone it.
metadata: metadata, // Return original metadata
};
return state;
},
process = (folders: EditorMetadata, parent: GUI, model: Object): void => {
folders.forEach(folder => {
const target: Object = folder.property ? model[folder.property] : model; // Root level or object nested prop?
folder.folderRef = addFolder(folder, parent);
folder.elements && folder.elements.forEach(element => element.elementRef = addElement(folder.folderRef, element, target));
folder.folders && process(folder.folders, folder.folderRef, target);
});
},
{ state, instance, metadata } = createDat(editorOptions, editorMetadata);
process(state, instance, model);
container.appendChild(instance.domElement);
return { state, instance, metadata };
}
}
const options: GUIParams = {
hideable: false,
autoPlace: false,
closeOnTop: false,
width: 250,
name: 'DAT Sample'
};
const model = {
rtl: true,
renderer: 'geras',
theme: 'dark',
toolbox: 'foundation',
toolboxPosition: 'left',
debug: {
alert: false
}
};
const foldersMetadata: EditorMetadata = [
{
title: 'Options',
tooltip: 'Options AA',
elements: [
{
title: 'rtl',
tooltip: 'RTL',
},
{
title: 'renderer',
tooltip: 'Renderer',
options: ['geras', 'thrasos', 'zelos']
},
{
title: 'theme',
tooltip: 'Theme',
options: ['classic', 'dark', 'deuteranopia', 'tritanopia', 'highcontrast']
},
{
title: 'toolbox',
tooltip: 'Toolbox',
options: ['foundation', 'platform', 'invoice']
},
{
title: 'toolboxPosition',
tooltip: 'Toolbox Position',
options: ['left', 'right', 'top', 'bottom']
},
],
folders: [
{
title: 'Debug',
property: 'debug',
tooltip: 'Debug mode',
elements: [
{
title: 'alert',
tooltip: 'Alert Tooltip',
},
]
}
]
},
];
My question is similar to Javascript circular dependency in GraphQL code but my problem is not on the structure and database level, but in javascript (ES6).
My schema definition is getting to grow too large but I don't see where I could cut the file into pieces. It seems to be logical to cut based on the different object types, but that brings to circular dependencies similarly to this very much simplified, non-working example:
// -- file A.js
import { bConnection, getBs } from 'B';
export class A { /*...*/ };
export var getA = (a) => { /*...*/ };
export var getAs = (array_of_as) => { /*...*/ };
export var aType = new GraphQLObjectType ({
name: 'A',
fields: () => ({
bs: {
type: bConnection,
/*...*/
},
resolve: (a, args) => connectionFromPromisedArray (
getBs (a.bs)
),
/*...*/
}),
interfaces: () => [ require ('./nodeDefs').nodeInterface ],
/*...*/
})
export var {
connectionType: aConnection,
edgeType: aEdge
} = connectionDefinitions ({
name: 'A',
nodeType: aType
});
// -- file B.js
import { aConnection, getAs } from 'A';
export class B { /*...*/ };
export var getB = (b) => { /*...*/ };
export var getBs = (array_of_bs) => { /*...*/ };
export var bType = new GraphQLObjectType ({
name: 'B',
fields: () => ({
as: {
type: aConnection,
/*...*/
},
resolve: (b, args) => connectionFromPromisedArray (
getAs (b.bs)
),
/*...*/
}),
interfaces: () => [ require ('./nodeDefs').nodeInterface ],
/*...*/
})
export var {
connectionType: bConnection,
edgeType: bEdge
} = connectionDefinitions ({
name: 'B',
nodeType: bType
});
// -- file nodeDefs.js
import {
fromGlobalId,
nodeDefinitions,
} from 'graphql-relay';
import { A, getA, aType } from 'A'
import { B, getB, bType } from 'B'
export var {nodeInterface, nodeField} = nodeDefinitions (
(globalId) => {
var {type, id} = fromGlobalId (globalId);
if (type === 'A') {
return getA (id);
} else if (type === 'B') {
return getB (id);
}
},
(obj) => {
if (obj instanceof A) {
return aType;
} else if (obj instanceof B) {
return bType;
}
}
)
// -- file schema.js
import {
GraphQLObjectType,
GraphQLSchema,
} from 'graphql';
import { nodeField } from './nodeDefs';
var queryType = new GraphQLObjectType ({
name: 'Query',
fields: () => ({
node: nodeField,
/*...*/
}),
});
Is there a common way or best practice for this?
I've the same problem. I think the cleaner solutions is about use gruntjs concat.
grunt.initConfig({
concat: {
js: {
src: ['lib/before.js', 'lib/*', 'lib/after.js'],
dest: 'schema.js',
}
}
});
With this config, you can split your schema in several files, creating a final schema.js.
before.js may be this way:
import {
GraphQLObjectType,
GraphQLInt,
GraphQLString,
GraphQLSchema,
GraphQLList,
GraphQLNonNull
} from 'graphql';
import db from '../models/index.js';
import Auth from '../classes/auth';
after.js may be this way:
const Schema = new GraphQLSchema({
query: Query,
mutation: Mutation
})
export default Schema;
The others files will contains Objects like:
const Funcionario = new GraphQLObjectType({
name: 'Funcionario',
description: 'This represent a Funcionario',
fields: () => {
return {
id: {
type: GraphQLInt,
resolve(funcionario, args) {
return funcionario.id;
}
},
CPF: {
type: GraphQLString,
resolve(funcionario, args) {
return funcionario.CPF;
}
},
nome: {
type: GraphQLString,
resolve(funcionario, args) {
return funcionario.nome;
}
},
sobrenome: {
type: GraphQLString,
resolve(funcionario, args) {
return funcionario.sobrenome;
}
},
sessions: {
type: new GraphQLList(Session),
resolve(funcionario, args) {
return funcionario.getSessions();
}
}
}
}
})
See https://github.com/francoisa/todo/tree/master/server/graphql/types
todoType.js has a reference to viewerType which is defined in viewerType.js
viewerType.js imports from todoType
I'm poking Angular2 and it's Routing system. I'm creating 'Project Wizard' #Component with 'child' #Components using #RouteConfig and it looks like this:
const enum State {
welcome, basicData, groupsData, overview
}
const enum Condition {
back
}
#Component({
selector: 'router-outlet',
templateUrl: '/app/templates/wizard/project/project-wizard-container.html',
directives: [
ROUTER_DIRECTIVES,
],
})
#RouteConfig([
{ path: '/', name: 'ProjectWizardWelcome', component: ProjectWizardWelcomeComponent, useAsDefault: true },
{ path: '/step2', name: 'ProjectWizardStep2', component: ProjectWizardStep2Component },
{ path: '/step3', name: 'ProjectWizardStep3', component: ProjectWizardStep3Component },
{ path: '/overview', name: 'ProjectWizardOverview', component: ProjectWizardOverviewComponent },
])
export class ProjectWizardComponent {
mock: Mock = new Mock();
private mapping: {key: State, value: string}[] = [
{ key: State.welcome, value: 'ProjectWizardWelcome' },
{ key: State.basicData, value: 'ProjectWizardStep2' },
{ key: State.groupsData, value: 'ProjectWizardStep3' },
{ key: State.overview, value: 'ProjectWizardOverview' },
];
private transitions: FSM.Transition<State, Condition>[] = [
{ from: State.welcome, conditions: [], to: State.basicData },
{ from: State.basicData, conditions: [Condition.back], to: State.welcome },
{ from: State.basicData, conditions: [], to: State.groupsData },
{ from: State.groupsData, conditions: [Condition.back], to: State.basicData },
{ from: State.groupsData, conditions: [], to: State.overview },
{ from: State.overview, conditions: [Condition.back], to: State.groupsData },
];
private fsm: FSM<State, Condition> = new FSM(State.welcome, this.transitions);
constructor(
private _router: Router,
private _routeParams: RouteParams) {
}
onPrev(): void {
var prevState = this.fsm.apply([Condition.back]).get();
var prevRoute = this.mapping[prevState].value;
this._router.navigateByInstruction(this._router.generate([prevRoute]), true);
}
onNext(): void {
var nextState: State = this.fsm.apply([]).get();
var nextRoute = this.mapping[nextState].value;
this._router.navigateByInstruction(this._router.generate([nextRoute]), true);
}
onCancel(): void {
this._router.navigate(['Welcome']);
}
}
I need to share a Mock object across 'child' components and I want to understand what my options are. My current understanding is that:
it can be shared using container object which is #Injectable like some Service.
using RouterData. In this case, I would need to unmarshal data from url.
But are there any other ways to pass this object to #Components directly using router?
No, these two are the available options. I'd suggest a shared service.