-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Refactor the hierarchy of NavigationMode classes. #166
Conversation
Shared code extracted to the base `abstract class NavigationMode`. This higlights differences between provided navigation modes.
Actually, I would also like to share a custom navigation mode we implemented in our project, in case you find it useful enough to be included in the library. However, I'd like this refactoring to be handled first, because (apart from other benefits it brings) it will make it easier for me to point out the differences between our custom nav. mode and the built-in ones. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the refactoring of the navigation modes.
In the following I've added some additional thoughts of mine:
NavigationMode
interface
What I'm not sure about is, whether we should really remove the NavigationMode
interface, or whether we should just add a new abstract class, which we add in-between, i.e. an abstract class which implements the NavigationMode
interface and is extended by all current concrete navigation mode implementations.
This should allow for a more free implementation of new navigation modes.
Injectability
Currently the navigation mode is created by the wizard itself (see https://github.com/madoar/angular-archwizard/blob/develop/src/lib/navigation/wizard-state.model.ts#L107).
Ideally I would like to change this to an injection based creation, i.e. angular itself creates the navigation mode based in the input to wizard.
Comment style
The current comment style is based on typedoc (if I remember correctly).
One of my plans for the library is to publish an API documentation for the wizard on the corresponding GitHub page.
For this I want to change the documentation style to compodoc, therefore it would be useful to modify the comment style during the refactoring to be compatible with the new documentation tool.
New navigation mode
I'm always open for new code additions like navigation modes!
The only important point is, that they shouldn't target a too limited audience, because otherwise I fear that too many such changes reduce the maintainability of the wizard code.
Regarding comment style, I am not sure the project can be migrated to compodoc because compodoc does not support some useful constructs used in the project (namely: |
As for separating a base abstract class and an interface, free implementation isn't a good purpose, imo. That is, if I had to implement my own navigation mode, I cannot imagine circumstances when I would choose the interface over the abstract class, because I would have to copy a lot of code ( However, there might by other purposes of extracting an interface. For example, maybe it could serve as a marker for Angular Injector (going to look through the Injectability section now). |
Now, the Injectability section. I see one problem with this: each wizard has to have its own instance of NavigationMode, which, in Angular, means that NavigationMode has to be provided in the WizardComponent (as long as we support having multiple wizard on a page, but I believe we do). So, that's the end of the way. Or we can inject a factory function, but we still need to provide a way to pass such a factory as an input to preserve an ability to set up several wizards on the same page differently. Existing Previous signature: This way it will be possible to either construct a brand new We also can provide an injection token which user could provide for setting default configuration for all affected angular-archwizard's, like, for example, in ngx-perfect-scrollbar. If a user wants all wizard to have the same navigation mode, he/she will be able to set it in a single place.
Does it make sense? |
About the interface vs abstract class topic: I'm not a friend of injection tokens/markers for interfaces. |
About the Injectability of the navigation mode: I like the idea to extend the About the injection token usage for the selection of a navigation mode: |
…and `abstract class BaseNavigationMode`.
* @param destinationIndex The index of the destination step | ||
* @returns A [[Promise]] containing `true`, if the destination step can be transitioned to and false otherwise | ||
*/ | ||
abstract canGoToStep(destinationIndex: number): Promise<boolean>; | ||
canGoToStep(destinationIndex: number): Promise<boolean>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an option, we could exclude this method from the interface, but in this case existing tests will have to be rewritten to test BaseNavigationMode
's instead of NavigationMode
's to be able to still call canGoToStep
. @madoar , what is your opinion on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kind of like the idea to move canGoToStep
from the interface to the base class, but I see a possible issue with this:
canGoToStep
is used to determine if an arbitrary given step can be entered.
This is also the main difference in my opinion between canGoToStep
and isNavigable
, because isNavigable
will always returns false for a future step in the strict navigation mode. The method can therefore be used by an application developer to query the wizard if an arbitrary step can be entered.
Such a query mechanism isn't provided by another method of the interface, because the "main" methods, i.e. the goTo methods don't provide a return value.
I'm not sure if we should remove this method without providing a replacement method to do the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, let's leave this method intact.
this.goToStep(this.wizardState.currentStepIndex - 1, preFinalize, postFinalize); | ||
} | ||
} | ||
goToPreviousStep(preFinalize?: EventEmitter<void>, postFinalize?: EventEmitter<void>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make the interface cleaner, we could exclude goToPrevious/NextStep
methods from it, but users will then have to call goToStep(wizardState.destinationIndex ± 1)
, which is inconvenient. Also, existing tests will have to be adapted. It feels bad either way. For now, I would choose having these methods in the interface. @madoar, your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every time I need to touch the interface/implementation classes I ask myself the same question^^
The big benefit of having the methods is the additional syntactic difference between + and - 1, i.e. got the next or previous step.
I think this additional safety measurement helps to prevent the introduction of bugs, so I previously decided to let the methods remain inside the interface.
If you have a better idea to preventing such bugs or make it harder for them to occur, I'm free with removing the methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I can't think of a better idea now. Let's leave the goToPrevious/NextStep
methods intact.
…as a function returning a navigation mode name or a created NavigationMode. Notes: - `WizardState.updateNavigationMode` method signature is left compatible with the old version. - A new set of tests is added in `navigation-mode-selection.spec.ts`. - The new ability should be documented in the 'navigationMode' section of the README.
I think we've got our wires crossed here :) I did not mean to use this approach inside the library. Instead, I meant that we could provide such option to the user for the purpose of global customization of aw. The
Illustration: // AppModule
providers: [
{ provide: WizardNavigationModeFactory, useClass: AppWizardNavigationModeFactory },
]
// wizard-navigation-mode-factory.service.ts
//export class AppWizardNavigationModeFactory implements WizardNavigationModeFactory {
export class AppWizardNavigationModeFactory extends WizardBaseNavigationModeFactory {
public createByName(wizard: WizardComponent, name: string) {
if (name == 'custom') {
// support custom navigation mode
return new CustomNavigationMode(wizard.model);
}
// support standard navigation modes like 'strict' etc.
return super().createByName(wizard, name);
}
} @madoar, do you consider this useful and worth implementing? |
I like it! export class AppWizardNavigationModeFactory extends WizardBaseNavigationModeFactory {
public createByName(wizard: WizardComponent) {
// always return the custom implementation independent of the input parameter
return new CustomNavigationMode(wizard.model);
}
} Alternatively the public createDefault(wizard: WizardComponent);
public createDefault(model: WizardState);
public createByName(wizard: WizardComponent, name: String);
... |
@madoar , I apologize again for the delays, it is crunch time at work :-( Will probably be able to return to archwizard this weekend. |
No problem :) |
@earshinov I hope you're still fine? |
@madoar , Hi! I am still a bit overworked, but I have reason to believe than I will be able return to archwizard in the middle of the upcoming week. |
Conflicts: src/lib/components/wizard.component.ts src/lib/navigation/free-navigation-mode.ts src/lib/navigation/index.ts src/lib/navigation/navigation-mode.provider.ts src/lib/navigation/semi-strict-navigation-mode.ts src/lib/navigation/strict-navigation-mode.ts src/lib/navigation/wizard-state.model.ts
@earshinov if you think this is ready to be merged I'll take a look at this PR the coming weekend.
|
@madoar , No, I don't think it is ready to be merged yet. I would like to first review our discussion and your comments, probably this weekend. |
… class. ArchwizardModule.forRoot() now takes an optional configuration object with an optional `navigationModeFactory` field. **BREAKING API CHANGES**: * WizardState.updateNavigationMode() no longer takes a navigation mode name. WizarcComponent.updateNavigationMode() should be used instead.
…'strict' so that the default navigation mode can be chosen by the configured NavigationModeFactory
Hello @madoar , I have extracted |
http://localhost:4200/#/custom-navigation-mode/ Requires 'refactor-nav-modes' branch of angular-archwizard from madoar/angular-archwizard#166
I have also created a new demo page in the angular-archwizard-demo project, see madoar/angular-archwizard-demo#31 |
src/lib/archwizard.module.ts
Outdated
return { | ||
ngModule: ArchwizardModule, | ||
providers: [ | ||
{ provide: NAVIGATION_MODE_FACTORY, useClass: config && config.navigationModeFactory || BaseNavigationModeFactory }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
config && config.navigationModeFactory
allows the library user to overwrite the default factory (i.e. BaseNavigationModeFactory
), right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this construction makes sure that user's setting only takes effect when config
is not falsy (defined and not null) and config.navigationModeFactory
is not falsy (defined and not null). Otherwise BaseNavigationModeFactory
will be used.
This expression can be rewritten as:
config && config.navigationModeFactory ? config.navigationModeFactory : BaseNavigationModeFactory
which is a little bit longer.
…ssed to [navigationMode]
…vigationModeFactory
…eFactory`. Rename the file to base-navigation-mode-factory.<...> to match the class name.
…tency and to match the doc comments
…vigationInput type
…t is not required.
…rather than a constructor, which is more flexible and easy for the end user
…o BaseNavigationMode class
* A demo for custom navigation mode http://localhost:4200/#/custom-navigation-mode/ Requires 'refactor-nav-modes' branch of angular-archwizard from madoar/angular-archwizard#166 * Update the usage of ArchwizardModuleConfig in connection with madoar/angular-archwizard@016b542
Shared code extracted to the base
abstract class NavigationMode
.This highlights differences between provided navigation modes.
Let us continue the refactoring marathon ;-)