Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] Angular2 and app modularity #5063

Closed
artaommahe opened this issue Nov 2, 2015 · 9 comments
Closed

[question] Angular2 and app modularity #5063

artaommahe opened this issue Nov 2, 2015 · 9 comments

Comments

@artaommahe
Copy link
Contributor

Angular1 allows to build flexible modular app structure, e.x.(part of current project)

├── admin-panel-module
├── auth-module
├── chat-module
│   ├── channels-module
│   ├── history-module
│   │   ├── filter-addressed-module
│   │   ├── filter-user-module
│   ├── text-input-module
│   ├── thirdparty-module
│   │   ├── goodgame-module
│   │   └── twitch-module
│   └── tools-module
├── donate-module
├── stream-module
├── subscribe-module
├── support-module

where each module contains directives, services and any other things(e.x. react components). Also each module has index.js file where angular module is declared and directives/services assigned. And this module uses as dependency for another.

// smth-module/index.js
var angular = require('angular');

var smthFactory = require('./provider/smth.factory');
var smthDirective = require('./directive/smth.directive');

var SmthModule = angular.module('SmthModule', []);

SmthModule.factory('smthFactory', smthFactory);
SmthModule.directive('smth', smthDirective);

module.exports = SmthModule;

// app.js
var angular = require('angular');

var SmthModule = require('./smth-module');

var app = angular.module('app', [
  SmthModule,
  ...
]);

So we declare all module items in this module, add it as dependency for top level module and can use this module directives/services anywhere in our app.
Bun what with this thing in Angular2? I tried to build small side project with alpha45 and can't find anything about modular structure. For simple two modules

main-module
   component
    main
      main.ts
chat-module
  component
    chat
      chat.ts
  service
    chat.ts

if i want to use chat component or service in main, i need to import it by whole relative path

import {ChatCmp} from '../../chat-module/component/chat/chat'
import {Chat} from '../../chat-module/service/chat'

and so with each component from another module. If modules will be reallocated, i must fix each import to new relative path.
Also if service requires smth like Http

import {Injectable} from 'angular2/angular2';
import {Http} from 'angular2/http';

@Injectable()
export class Chat {
  constructor(private http: Http) {
    //
  }
}

it's necessary to inject HTTP_PROVIDERS services on top level of app(bootstrap.js) or for each component that uses this service(or depend on components inheritance).
Again this thing breaks modular structure - components/services from ona module knows about path to components/services from other modules and must watch their dependencies. And this easily solves via modules system of Angular1.

Am i missed something or there is any other thing in plans for this cases? I don't see comfortable use of current ng2 app structure anythere except small project without need of modulaity. Thx )

@evanplaice
Copy link

If you're using System.js you can map an alias to a particular component/directory.

With the directory tree:

main-module
   component
    main
      main.ts
chat-module
  component
    chat
      chat.ts
  service
    chat.ts

Lets say you want to add aliases for:

import {ChatCmp} from '../../chat-module/component/chat/chat'
import {Chat} from '../../chat-module/service/chat'

config.js

...
map: {
    "chat-component": "/chat-module/component/chat/chat",
    "chat-service": "/chat-module/service/chat",
...

So your imports would be:

import {ChatCmp} from 'chat-component'
import {Chat} from 'chat-service'

Alternatively, you could do:

config.js

...
map: {
    "chat": "chat-module",
...

So your imports would be:

import {ChatCmp} from 'chat/component/chat/chat'
import {Chat} from 'chat/service/chat'

In addition, if you install a component from an external repository/package-manager via JSPM. The aliases will automatically be mapped to the the correct location within the /jspm_packages folder

@nschipperbrainsmith
Copy link

@evanplaice This does not answer the final part of @artaommahe question:

it's necessary to inject HTTP_PROVIDERS services on top level of app(bootstrap.js) or for each component that uses this service(or depend on components inheritance).
Again this thing breaks modular structure - components/services from ona module knows about path to components/services from other modules and must watch their dependencies. And this easily solves via modules system of Angular1.

Which is also a problem i am running into immediately: http://stackoverflow.com/questions/33584763/hot-to-avoid-declaring-all-services-in-the-bootstrap-function

It all comes down to the fact that everything has to be within the bootstrap function.
I just did some tests and even using the providers on sub-components creates a new instance of the specific @Injectable class.

This also becomes really annoying when you want to share packages. Lets say i build a module, that module has 10 services those are all nested one below the other or something. The person who grabs my package has to include each and every one of those Classes within the import of his "app"(base) component and add them to the bootstrap function as well.
If they want to even use the modules.

Please correct me if i am wrong about it, because this change breaks the way i used to work with Angular1 in which i saw each service as a Singleton that i used throughout my controllers / other services and even sometimes project specific directives. While this is still possible i would now be forced to specify all those services within the bootstrap method to make them available application wide.

@alexpods
Copy link

alexpods commented Nov 7, 2015

@schippie Just create index.js files for your modules and export all "public" providers from them. Then import these providers into a main file and use them in bootstrap function. Basically it's almost exactly like what you did in angular1.

// smth-module/index.js

import { SmthFactory } from './smth.factory';
import { SmthService } from './smth.service';

export const SMTH_MODULE = [
    SmthFactory,
    SmthService
];

// app.js

import { SMTH_MODULE } from './smth-module';
// other imports...

bootstrap(AppComponent, [SMTH_MODULE /*, other providers */]);

@nschipperbrainsmith
Copy link

@alexpods Okay, I got the same answer on stackoverflow the with Core directives example.
I guess while I have to adjust myself to this new way of writing, it will be very similar to dealing with Polymer components.

I guess a good way to deal with it is make a base file for each directory (model/ , utils/ , components/) in which you specify this all

Thank you 👍

@alexpods
Copy link

alexpods commented Nov 7, 2015

@schippie IMHO, I think it's better to have only one base file for a module. This file is a single public interface of your module.

// smth-module/index.js

import { SmthFactory } from './smth.factory';
import { SmthService } from './smth.service';

import { SmthComponent } from './smth.component';
import { SmthDirective } from './smth.directive';

export { SmthDirective }

export const SMTH_MODULE_PROVIDERS = [
    SmthFactory,
    SmthService,
    // ...
];

export const SMTH_MODULE_DIRECTIVES = [
    SmthComponent,
    SmthDirective,
    // ...
]

// 'app-component.js'

import { Component } from 'angular2/core';
import { SmthDirective } from '../smth-module';
// or import { SMTH_MODULE_DIRECTIVES } from '../smth-module';

@Component({
    // ...
    directives: [SmthDirective], // or directives: [SMTH_MODULE_DIRECTIVES]
    template: `<smth-directive></smth-directive>`
})
export class AppComponent() {}

// 'app.js'
import { SMTH_MODULE_PROVIDERS } from '../smth-module';
import { AppComponent } from './app-component.js';

bootstrap(AppComponent, [SMTH_MODULE_PROVIDERS /*, other providers */]);

@nschipperbrainsmith
Copy link

@alexpods Oh no no, my situation that I was describing at the end with the sub-directories had more to do with the fact: That within a single project you could run into large file base as well. At that point since its purely for a project (non public) it would be the way I would handle it.

For modules that I or anybody would write, having a single file is the only correct way to prevent confusion.

@artaommahe
Copy link
Contributor Author

Ok.. i decied to try again.
Used angular2-seed with typescript and babel.

Test app sctructure

├── bootstrap.ts
├── graph-module
│   ├── component
│   │   └── line-graph
│   │       └── line-graph.ts
│   └── index.ts
└── page-module
    ├── component
    │   └── page
    │       └── page.ts
    └── index.ts
// bootstrap.ts
...
import {PageCmp} from 'page-module/index';

bootstrap(PageCmp, [
  ...
]);
// page.ts
...
import {LineGraphCmp} from 'graph-module/index';

...
export class PageCmp {}
// line-graph.ts
...
export class LineGraphCmp {}
// graph and page modules indexes like this

export {PageCmp} from './component/page/page';
// also added to System.config()
map: {
    'graph-module': '/graph-module',
    'page-module': '/page-module'
}

So i can use export Smth from 'module/index' syntax and this is almost what i need. But there is one problem

app/bootstrap.ts(4,23): error TS2307: Cannot find module 'page-module/index'.
app/page-module/component/page/page.ts(8,28): error TS2307: Cannot find module 'graph-module/index'.

How can i tell Typescript compiler about this modules? Or can i achive this modules usage via other way or modules system?
Also can i somehow achive this usage import {PageCmp} from 'page-module' without direct '/index' reference?

@artaommahe
Copy link
Contributor Author

this issue solves my problem(when Type script will be updated)
microsoft/TypeScript#5039

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants