简体   繁体   中英

One project, multiple clients. Angular 2

Oky so I have a question which I seem not able to find the answer to.

Let say I have a project x, which will be used by multiple clients, each with there own functionality that either might be shared or not as well as there own custom style sheets and html layouts.

So following the normal angular 2 click pattern I am wondering if this would be possible.

Example

foo
--foo.component.css
--foo.component.html
--foo.component.spec.ts
--foo.component.ts
--client-x
----foo.component.css
----foo.component.html
--client-y
----foo.component.css
----foo.component.html
----foo.comoonent.ts

As you can see the first level would be the base implementation. Then client x uses the same functionality but just overrides the styles and html, while client y overrides all functionality as well as the styles and css.

Does anybody know if this is possible as well as whether this is a good idea. The reason for this is, having multiple projects that contain basically the same code, we constantly have to update all projects if a bug is found in the code base or we are adding a feature. I know this was possible in angular 1 as I have seen a project structure like this. I believe they used a gulp job to do a post build build, but I am not sure if this would suffice and also don't know how to approach it.

Your inputs would be appreciated.

Currently it's impossible to override component styles/templates by means of the framework.

Application Angular modules should be structured in a way that minimizes the efforts to provide custom component implementation. If there's a chance that the feature (typically a component with its dependencies) will be overridden, it should be included to its own Angular module.

All customizable modules are not included to shared module, so the latter contains no customizable units and can be imported as is.

Default app main module is just a wrapper for these modules:

@NgModule({
  imports: [
    GenericFooModule, // customizable FooComponent
    GenericBarModule, // BarService and customizable BarComponent
    GenericSharedModule // everything else
  ],
  exports: [/* same */]
})
export class GenericAppModule {}

And entry file:

platformBrowser().bootstrapModuleFactory(GenericAppModuleNgFactory)

Then clients can redefine them with a minimum of WET code. Due to the fact that class annotation inheritance is not supported , this means that @Component should be pasted and modified:

Customized component:

@Component({
  selector: '...', // same as original
  templateUrl: '...' // same as original, but relative path refers to different file
})
export class XFooComponent extends FooComponent {}

@NgModule({
  declarations: [XFooComponent],
  exports: [/* same */]
})
export class XFooModule {}

Customized main module:

@NgModule({
  imports: [
    XFooModule, // customized
    GenericBarModule,
    GenericSharedModule 
  ],
  exports: [/* same */]
})
export class XAppModule {}

Customized entry:

platformBrowser().bootstrapModuleFactory(XAppModuleNgFactory)

This keeps client projects as small and DRY as possible, thus leaving very little place for inconsistencies and human mistakes.

Otherwise the only option is to tamper client-specific asset files by means of the bundler in use (as existing answer suggests). This practice isn't supported by Angular CLI, and there's always a chance that the project will have to be modified for the client beyond custom styles and templates.

We have the same problem and we're planning to use the following approach, which worked for small proof of concept project

  • Have a Core module containing all generic components and templates.
  • Each client app is implemented as a child module
  • For each child module, a watch script copies the core components to a "base" folder into that child module. By default, the child module declares and uses the components in the base folder.

Component inheritance

To override a child component's logic, declare a new component that inherits from the Core component. Modify the child module to declare that inherited component, rather than the one in the base folder

Template inheritance

We use nunjucks to that component templates have inherited blocks.

So for each component that can have its template overriden by child components, we have a template.html.nunj which is compiled to template.html by nunjucks.

This is done via a watch script. The watch script also checks if there is a html file for each component in the child module. If there is one, the templateUrl linked is changed in the corresponding component in the base folder to point to that file. This allows us to override just the template, without having to redeclare the component.ts file

Style inheritance

With sass, declare variables in separate files and reference them with relative paths. If needed, you can modify an inherited component sass file to import the core components's sass, then override the style below

The way I solved a situation similar to this one was by creating a webpack configuration that has multiple entry points . Use one entry point for client 1 and the other for client 2. The code in the entry points can perform the necessary configuration per client and call the boot function afterwards, webpack will take care of excluding the code that is not needed. I like this setup as all code is 'in use' from the point of view of my IDE, making refactoring and other system wide adjustments easier.

If you are using angular-cli I think the webpack configuration is not directly accessible, but you can use 'eject' to generate one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM