Skip to content

Commit

Permalink
Beta 12 - fix AOT build problem with utils services (#144)
Browse files Browse the repository at this point in the history
closes #135

See CHANGELOG.md
  • Loading branch information
wardbell authored May 4, 2018
1 parent a492b6d commit 0abab7d
Show file tree
Hide file tree
Showing 38 changed files with 2,817 additions and 1,870 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ The ngrx-data library has its own [CHANGELOG.md](lib/CHANGELOG.md) and versionin
Please look there.

**_This_** Changelog covers changes to the repository and the demo applications.
<a name="0.2.13"></a>

# 0.2.13 (2018-05-04)

* modified app to provide app-specific Pluralizer and Logger to prove that works.
These versions merely inherit from the ngrx-data versions, making no changes.

* ngrx-data paths in all `tsconfig.json` point to `dist/ngrx-data` instead of `lib/src`, on Filipe Silva' recommendation.
This means that the app points to the result of the library build, not the library source
and `npm run build-all` does a production build against the library output just as an app
would build against the installed npm packages.

The downside is that "Go to definition" takes you to the `d.ts` file rather than library source file.
That's inconvenient. But the benefit is that the routine build process reflects what apps will experience.
This is the approach followed by the Angular CLI's library support.

<a name="0.2.12"></a>

# 0.2.12 (2018-03-25)
Expand Down
31 changes: 14 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,36 +247,33 @@ The one you want for library and app files ends in `/lib/src/file-name.ts` and `
> Hope to solve the _two file_ problem.
## Build the app against the npm package
## Build the app against the source
The demo app is setup to build and run against the version of the library in `dist/ngrx-data`.
The demo app is setup to build and run against the ngPackagr artifacts in `dist/ngrx-data`,
the same artifacts delivered in the npm package.
That's convenient when you're evolving the library code and
re-building as you go with `npm run build-lib` or `npm run build-setup`.
> Re-build the library `npm run build-lib` or `npm run build-setup` or `npm run build-all`
> to update these artifacts.
The version in `dist/ngrx-data` will reflect your latest changes.
Obviously the package deployed in `node_modules` would not.
This approach, while safe, can be inconvenient when you're evolving the library code because
"Go to definition" takes you to the `d.ts` files in `dist/ngrx-data` rather than
the source files in `lib/src`.

If you want to see how the demo app runs against the published package, you'll have to make **a few temporary changes** to the TypeScript configuration.
If you want to "Go to definition" to take you to the source files,
make the following **\*temporary changes** to the TypeScript configuration.

1. **_Remove_** the following from root `tsconfig.json` so that the IDE (e.g., VS Code) looks for `ngrx-data` in `node_modules/ngrx-data` instead of `src/lib`.
1. **_Replace_** the paths target in the root `tsconfig.json` so that the IDE (e.g., VS Code) looks for `ngrx-data` in `src/lib`.

```bash
"paths": {
"ngrx-data": ["lib/src"]
},
```

2. **_Remove_** _that same setting_ from the `src/` config at `src/tsconfig.json`.
2. **_Replace_** _that same setting_ in the config at `src/tsconfig.json`.

3. **_Remove_** _that same setting_ from the `src/client/tsconfig.app.json`.
Now `ng build` references `node_modules/ngrx-data` instead of `src/lib` when it builds the demo app.

4. Now install the `ngrx-data` package _without touching the `package.json`_ as follows:

```bash
npm install ngrx-data --no-save --no-lock
```
3. **_Replace_** _that same setting_ in `src/client/tsconfig.app.json`.
Now `ng build` references `src/lib` when it builds the demo app.

> **Remember to _restore the `tsconfig` settings_ when you're done. Do not commit those changes!**
Expand Down
54 changes: 31 additions & 23 deletions docs/entity-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Each _entity type_ appears as named instance of the _ngrx-data_ [**`EntityMetada

You can specify metadata for several entities at the same time in an **`EntityMetadataMap`**.

Here is an example `EntityMetadataMap` similar to the one in the demo app
Here is an example `EntityMetadataMap` similar to the one in the demo app
that defines metadata for two entities, `Hero` and `Villain`.

```javascript
Expand Down Expand Up @@ -44,13 +44,13 @@ The easiest way to register metadata is to define a single `EntityMetadataMap` f
})
```

If you define entities in several, different _eagerly-loaded_ Angular modules, you can add the metadata for each module with the multi-provider.
If you define entities in several, different _eagerly-loaded_ Angular modules, you can add the metadata for each module with the multi-provider.

```javascript
{ provide: ENTITY_METADATA_TOKEN, multi: true, useValue: someEntityMetadata }
```

This technique won't work for a _lazy-loaded_ module.
This technique won't work for a _lazy-loaded_ module.
The `ENTITY_METADATA_TOKEN` provider was already set and consumed by the time the _lazy-loaded_ module arrives.

The module should inject the `EntityDefinitionService`
Expand All @@ -67,48 +67,50 @@ class LazyModule {
```

<a name="entity-metadata-interface"></a>

## Metadata Properties

The `EntityMedata<T>` interface describes aspects of an entity type that tell the _ngrx-data_ library how to manage collections of entity data of type `T`.
The `EntityMedata<T>` interface describes aspects of an entity type that tell the _ngrx-data_ library how to manage collections of entity data of type `T`.

Type `T` is your application's TypeScript representation of that entity; it can be an interface or a class.

### _entityName_

The `entityName` of the type is the only **required metadata property**.
It's the unique _key_ of the entity type's metadata in cache.
The `entityName` of the type is the only **required metadata property**.
It's the unique _key_ of the entity type's metadata in cache.

It _must_ be specified for individual `EntityMetadata` instances.
If you omit it in an `EntityMetadataMap`, the map _key_ becomes the `entityName` as in this example.

```javascript
const map = {
Hero: {} // "Hero" becomes the entityName
}
};
```

The spelling and case (typically PascalCase) of the `entityName` is important for _ngrx-data_ conventions. It appears in the generated [_entity actions_](docs/entity-actions), in error messages, and in the persistence operations.

Importantly, the default [_entity dataservice_](docs/entity-dataservice.md) creates HTTP resource URLs from the lowercase version of this name. For example, if the `entityName` is "Hero", the default data service will POST to a URL such as `'api/hero'`.

>By default it generates the _plural_ of the entity name when preparing a _collection_ resource URL.
> By default it generates the _plural_ of the entity name when preparing a _collection_ resource URL.
>
>It isn't good at pluralization.
>It would produce `'api/heros'` for the URL to fetch _all heroes_ because it blindly adds an `'s'` to the end of the lowercase entity name.
> It isn't good at pluralization.
> It would produce `'api/heros'` for the URL to fetch _all heroes_ because it blindly adds an `'s'` to the end of the lowercase entity name.
>
>Of course the proper plural of "hero" is "hero**es**", not "hero**s**".
>You'll see how to correct this problem [below](#plurals).
> Of course the proper plural of "hero" is "hero**es**", not "hero**s**".
> You'll see how to correct this problem [below](#plurals).
<a name=filterfn></a>

### _filterFn_

Many applications allow the user to filter a cached entity collection.
Many applications allow the user to filter a cached entity collection.

In the accompanying demonstration app, the user can filter _heroes_ by name and can filter _villains_ by name or the villain's _saying_.

We felt this common scenario is worth building into the _ngrx-data_ library. So every entity can have an _optional_ filter function.

Each collection's `filteredEntities` selector applies the filter function to the collection, based on the user's filtering criteria, which are held in the the stored entity collection's `filter` property.
Each collection's `filteredEntities` selector applies the filter function to the collection, based on the user's filtering criteria, which are held in the the stored entity collection's `filter` property.

If there is no filter function, the `filteredEntities` selector is the same as the `selectAll` selector, which returns all entities in the collection.

Expand All @@ -117,27 +119,28 @@ A filter function (see [`EntityFilterFn<T>`](../lib/src/entity-metadata/entity-f
Here's an example that filters for entities with a `name` property whose value contains the search string.

```javascript
export function nameFilter(entities: {name: string}[], search: string) {
export function nameFilter(entities: { name: string }[], search: string) {
return entities.filter(e => -1 < e.name.indexOf(search));
}
```

The _ngrx-data_ library includes a helper function, `PropsFilterFnFactory<T>`, that creates an entity filter function which will treat the user's input
The _ngrx-data_ library includes a helper function, `PropsFilterFnFactory<T>`, that creates an entity filter function which will treat the user's input
as a case-insensitive, regular expression and apply it to one or more properties of the entity.

The demo uses this helper to create hero and villain filters. Here's how the app creates the `nameAndSayingFilter` function for villains.

```javascript
/**
/**
* Filter for entities whose name or saying
* matches the case-insensitive pattern.
*/
export function nameAndSayingFilter(entities: Villain[], pattern: string) {
return PropsFilterFnFactory<Villain>(['name', 'saying'])(entities, pattern);
return PropsFilterFnFactory < Villain > ['name', 'saying'](entities, pattern);
}
```

<a name=selectid></a>

### _selectId_

Every _entity type_ must have a _primary key_ whose value is an integer or a string.
Expand All @@ -152,15 +155,16 @@ In the [entity reducer tests](../lib/src/reducers/entity-reducer.spec.ts), the `
The `selectorId` function is this:

```javascript
selectId: (villain: Villain) => villain.key
selectId: (villain: Villain) => villain.key;
```

<a name=sortcomparer></a>

### _sortComparer_

The _ngrx-data_ library keeps the collection entities in a specific order.

>This is actually a feature of the underlying `@ngrx/entity` library.
> This is actually a feature of the underlying `@ngrx/entity` library.
The default order is the order in which the entities arrive from the server.
The entities you add are pushed to the end of the collection.
Expand All @@ -177,11 +181,13 @@ export function sortByName(a: { name: string }, b: { name: string }): number {
return a.name.localeCompare(b.name);
}
```
Run the demo app and try changing existing hero names or adding new heroes.

Run the demo app and try changing existing hero names or adding new heroes.

Your app can call the `selectKey` selector to see the collection's `ids` property, which returns an array of the collection's primary key values in sorted order.

<a name="entity-dispatcher-options"></a>

### _entityDispatcherOptions_

These options determine the default behavior of the collection's _dispatcher_ which sends actions to the reducers and effects.
Expand All @@ -195,6 +201,7 @@ The _default_ defaults are the safe ones: _optimistic_ for delete and _pessimist
You can override those choices here.

<a name=additional-collection-state></a>

### _additionalCollectionState_

Each _ngrx-data_ entity collection in the the store has
Expand All @@ -216,6 +223,7 @@ The property values become the initial collection values for those properties wh
The _ngrx-data_ library generates selectors for these properties but has no way to update them. You'll have to create or extend the existing reducers to do that yourself.

<a name="plurals"></a>

## Pluralizing the entity name

The _ngrx-data_ [`DefaultDataService`](docs/entity-dataservice.md) relies on the `HttpUrlGenerator` to create conventional HTTP resource names (URLs) for each entity type.
Expand All @@ -228,7 +236,7 @@ The `HttpUrlGenerator` can't pluralize the entity type name on its own. It deleg

The `Pluralizer` class has a _pluralize()_ method that takes the singular string and returns the plural string.

The default `Pluralizer` simply appends an `'s'`. That's fine for the `Villain` type (which becomes "villains").
The default `Pluralizer` handles many of the common English pluralization rules such as appending an `'s'`. That's fine for the `Villain` type (which becomes "villains").
That's the wrong technique for pluralizing the `Hero` type (which becomes "heros").

Fortunately, the default `Pluralizer` also injects a map of singular to plural strings (with the `PLURAL_NAMES_TOKEN`).
Expand Down Expand Up @@ -261,6 +269,6 @@ If you define your _entity model_ in separate Angular modules, you can increment

If this scheme isn't working for you, replace the `Pluralizer` class with your own invention.


```javascript
{ provide: Pluralizer, useClass: MyPluralizer }
```
24 changes: 24 additions & 0 deletions lib/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Angular ngrx-data library ChangeLog

<a name="1.0.0-beta.13"></a>

# 1.0.0-beta.13 (COMING SOON)

**BREAKING CHANGE: Update to Angular/RxJS/NgRX v6**

* Converted to RxJS v6.0.1 _without the compat library_
* Converted to Angular v6.0.0
* Converted to NgRX v6.0.0-beta.1

<a name="1.0.0-beta.12"></a>

# 1.0.0-beta.12 (2018-05-04)

### Utils and Pluralizer

Fix AOT build error (#135) by moving `export * from './utils';` to the top of `/lib/src/index.ts`.

These fixes should not break library consumers because they cannot reach into these folders.

* More English-capable `DefaultPluralizer`.
* Refactoring of internal utils files to put the interfaces in their own file
* Update packages to the last v5 versions.

<a name="1.0.0-beta.11"></a>

# 1.0.0-beta.11 (2018-04-29)
Expand Down
2 changes: 1 addition & 1 deletion lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngrx-data",
"version": "1.0.0-beta.11",
"version": "1.0.0-beta.12",
"repository": "https://github.com/johnpapa/angular-ngrx-data.git",
"license": "MIT",
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/actions/entity-action-guard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EntityAction } from './entity-action';
import { IdSelector, Update } from '../utils';
import { IdSelector, Update } from '../utils/ngrx-entity-models';

export class EntityActionGuard {
constructor(private selectId: IdSelector<any>) {}
Expand Down
14 changes: 10 additions & 4 deletions lib/src/actions/entity-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,26 @@ import { filter, takeUntil } from 'rxjs/operators';

import { EntityAction } from './entity-action';
import { EntityOp } from './entity-op';
import { flattenArgs } from '../utils';
import { flattenArgs } from '../utils/utilities';

/**
* Observable of entity actions dispatched to the store.
* EntityAction-oriented filter operators for ease-of-use.
* Imitates `Actions.ofType()` in ngrx/entity.
*/
@Injectable()
export class EntityActions<V extends EntityAction = EntityAction> extends Observable<V> {
export class EntityActions<
V extends EntityAction = EntityAction
> extends Observable<V> {
// Inject the ngrx/effect Actions observable that watches dispatches to the store
constructor(source?: Actions) {
super();

if (source) {
// ONLY look at EntityActions
this.source = source.pipe(filter((action: any) => action.op && action.entityName));
this.source = source.pipe(
filter((action: any) => action.op && action.entityName)
);
}
}

Expand Down Expand Up @@ -71,7 +75,9 @@ export class EntityActions<V extends EntityAction = EntityAction> extends Observ
const name = names[0];
return this.where(ea => name === ea.entityName);
default:
return this.where(ea => ea.entityName && names.some(n => n === ea.entityName));
return this.where(
ea => ea.entityName && names.some(n => n === ea.entityName)
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/dataservices/default-data.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ describe('DefaultDataServiceFactory', () => {
expect(heroDS.name).toBe('Hero DefaultDataService');
});

it("should produce hero data service that gets heroes with config's hero HttpResourceUrls", () => {
it('should produce hero data service that gets heroes via hero HttpResourceUrls', () => {
const newHeroesUrl = 'some/other/api/heroes';
const config: DefaultDataServiceConfig = {
root: 'api',
Expand Down
2 changes: 1 addition & 1 deletion lib/src/dataservices/default-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { HttpMethods, QueryParams, RequestData } from './interfaces';
import { HttpUrlGenerator, EntityHttpResourceUrls } from './http-url-generator';

import { EntityCollectionDataService } from './entity-data.service';
import { Update } from '../utils';
import { Update } from '../utils/ngrx-entity-models';

// Pass the observable straight through
export const noDelay = <K>(source: Observable<K>) => source;
Expand Down
Loading

0 comments on commit 0abab7d

Please sign in to comment.