TwanoO67 / ngx-admin-lte

Admin LTE for Angular 2/4/6/8 as a NPM package
MIT License
109 stars 47 forks source link

UserBox Layout Customization #57

Open fabioformosa opened 6 years ago

fabioformosa commented 6 years ago

It needs to allow the customization of userBox.

Before to work to this issue, has someone done something about? Here @catull spoke about a hack...

catull commented 6 years ago

Here's the hack.

I only want the UserBox' HTML template to be

<div class='user-body'>
  {{ currentUser.email }}
</div>

To achieve this, I had to go really deep into Angular internals.

First, you have to add a very invasive piece of code to main.ts. You have to override the so called DirectResolver class used in Angular.

Second, you have to provide a customised DirectiveResolver, which when loading all components, modifies the HTML template of the component, whose selector is.userBox. This is all done in custom-directive.resolver.ts, see below.

main.ts now becomes like this:

import { enableProdMode } from '@angular/core';
import { DirectiveResolver, CompileReflector } from '@angular/compiler';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { CustomDirectiveResolver } from './custom-directive.resolver';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(
  AppModule, {
    providers: [
      {
        provide: DirectiveResolver,
        useClass: CustomDirectiveResolver,
        deps: [
          CompileReflector
        ]
      },
    ]
  })
  .then(ref => {
    // Ensure Angular destroys itself on hot reloads.
    if (window['ngRef']) {
      window['ngRef'].destroy();
    }

    window['ngRef'] = ref;

    // Otherise, log the boot error
  })
  .catch(err => console.error(err));

Finally custom-directive.resolver.ts, only replaces HTML code; it could also modify CSS configuration. Luckily, by this point, each component's template attribute contains the content of the file pointed to by templateUrl.

This is its content for my purposes:

import { Directive, Type, Component } from '@angular/core';
import { DirectiveResolver } from '@angular/compiler';

// import { environment } from '../environments/environment';

export class CustomDirectiveResolver extends DirectiveResolver {

  resolve (type: Type<any>, throwIfNotFound?: boolean): Directive {
    const view: Component = super.resolve(type, throwIfNotFound);

    if (!view) {
      return view;
    }

    // use this to inspect all components, it is quite interesting
    // console.log(JSON.stringify(view));

    if (view.selector === '.userBox') {
      view.template = `
        <div class='user-body'>
          {{ currentUser.email }}
        </div>
        `;
      view.templateUrl = null;
    }

    return view;
  }
}

I wish there was a much simpler way.

fabioformosa commented 6 years ago

A very clever solution, thank you to have shared. You achieved your goal without to touch the library, but I think:

@TwanoO67 it's better a structural change to ngx-admin-lte, isn't? What about a new attribute in app-header to pass the userBox template?

catull commented 6 years ago

This would be the proper way.

  1. All components have a simple, standard Angular selector, not CSS style selector.
  2. The components HTML template is simply containing this
    <ng-content></ng-content>
  3. You can now have your own HTML content. No more fix content you cannot change.

The fundamental problem with ngx-admin-lte is that it mixes many things into one.

A. It wants to be an Angular Library for AminLTE, offering natural Angular Components, Services, Pipes etc. This is what I call "core library".

B. It also wants to showcase AdminLTE, so as the beginner developer gets something up quickly. This is what I call "Demo" use case.

C. It does want to keep compatibility to very old APIs, such as before Angular 4. It also includes keeping the TypeScript language level way too low. This is what I call legacy.

It is high time the list gets reconsidered.

My plan is to concentrate on A and defining a lower boundary for Angular, addressing C. My preference is Angular 4+.

B is best addressed in a separate project, say ngx-admin-lte-demo or ngx-admin-lte-showcase or Bootstrapping Ngx-Admin-Lte.

TwanoO67 commented 6 years ago

ngx-admin-lte can't have in the userBox the hard-coded string 'web developer', the registration date in timestamp or a series of button (follower, etc) unhandled and not customizable. @TwanoO67 it's better a structural change to ngx-admin-lte, isn't?

I totally agree! Hard coded string shouldn't be in here (or at least in the translation files), and all parts of the app should be customisable.

What about a new attribute in app-header to pass the userBox template?

I would rather set a service for that, in the same way that menu, or control-sidebar works

I also agree with @catull, ngx-admin-lte have to stick to purpose A. Purpose B is adressed by https://github.com/TwanoO67/bootstraping-ngx-admin-lte, so we could move part of the project to that one.

I also agree with stopping backward compatibility to Angular4 level for this package version >2

catull commented 6 years ago

@TwanoO67 Then please, in the README.md state that this project is for "Angular 4+", drop "~2~".

MihaiHoriaPopescu commented 6 years ago

@TwanoO67 Is the UserBox Layout Customization change in plan? Or for now is better to use the hack @catull shared?