Skip to content

Commit

Permalink
Merge pull request #17 from guillotinaweb/tutorial
Browse files Browse the repository at this point in the history
Tutorial
  • Loading branch information
ebrehault authored Jun 20, 2020
2 parents 33bfbc0 + 73c4c79 commit 7f2f8c2
Show file tree
Hide file tree
Showing 58 changed files with 1,443 additions and 17,533 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 1.3.2 (2020-06-20)

### Improvement
- Better demo and turorial
- More utilities (getContextAs, addInContext, and deleteContext)
- add refreshChildren method in folder view

# 1.3.1 (2020-05-28)

### Improvement
Expand Down
92 changes: 5 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Complex applications are supported too as the Grange is highly extensible and al

A Grange app is able to render any object served by the Guillotina backend.

It will use the path requested on the frontend to call the corresponding Guillotina object, like `http://localhost:4200/shoes/pink-boots` will produce a call to `` https://my-guillotina-server/db/app1/shoes/pink-boots` (assuming our Guillotina container supporting our app is named `app1`).
It will use the path requested on the frontend to call the corresponding Guillotina object, like `http://localhost:4200/shoes/pink-boots` will produce a call to `https://my-guillotina-server/db/app1/shoes/pink-boots` (assuming our Guillotina container supporting our app is named `app1`).

Let's assume the `pink-boots` object belongs to the `Shoes` type (a custom type defined in `app1`), it will be rendered using the default view (named `view`).

Expand Down Expand Up @@ -107,93 +107,11 @@ Run the application:
npm start
```

The Angular app is now offering all the Grange standard views (login, content creation, view, etc.).

### Create custom views

We have in our Guillotina `config.yaml` file a custom content type named `player`:
```yaml
player:
title: Player
inherited_interface: guillotina.interfaces.IItem
inherited_class: guillotina.content.Item
add_permission: guillotina.AddContent
properties:
team:
type: guillotina.schema.TextLine
title: Team
rank:
type: guillotina.schema.Int
title: Rank
```
We would like to use our own custom form to edit `player` contents.

We create regular Angular component named `PlayerComponent` and we declare it in our `AppComponent` to become the `player` edit view:

```typescript
this.grange.traverser.addView('edit', 'player', PlayerComponent);
```

The template is a simple form (based on Pastanaga UI elements, but any form elements would work):

```html
<pa-input [(value)]="title">Title</pa-input>
<pa-input [(value)]="team">Team</pa-input>
<pa-input [(value)]="rank" type="number">Rank</pa-input>
<pa-button (click)="save()">Save</pa-button>
```

In the component itself, we inject `grange` service:
```typescript
constructor(private grange: Grange) { }
```

Thank to this service, we can get the values we need from the context:
```typescript
ngOnInit() {
this.grange.getContext().subscribe(context => {
this.title = context.title;
this.team = context.team;
this.rank = context.rank;
});
}
```

The good thing about `getContext()` is it returns an Observable that will emit the context object everytime it changes in our state, so our form is always reflecting the current state values.

`grange` service also allows us to save the changes the user enters in the form:
```typescript
save() {
this.grange.updateContext({
title: this.title,
team: this.team,
rank: this.rank,
});
}
```
`updateContext()` updates the state (hence the form is immediately updated because `getContext()` will emit the new values), and it also updates Guillotina backend by doing a PATCH call.

If we want to take an action after saving – like redirecting to the home page – `updateContext()` has a `onComplete` property which is a boolean observable (returning `true` for success, and `false` if saving produced a backend error):

```typescript
save() {
this.grange.updateContext({
title: this.title,
team: this.team,
rank: this.rank,
}).onComplete.subscribe(success => {
if (success) {
this.grange.ui.toaster.open('Saved', 2000);
this.grange.traverser.traverse('/');
} else {
this.grange.ui.toaster.open('Error when saving.', 'common.dismiss');
}
});
}
```
Our Angular app is now offering all Grange standard views (login, content creation, view, etc.).
We can login as root/root. By clicking on the "Plus" button, we get the available content types and we can a new content.
Once created we can delete it or edit it. The edit form is automatically generated based on the content type schema provided by Guillotina.

See [the full code example](projects/demo/src/app).
See [Grange tutorial](./docs/TUTORIAL.md) for more advanced examples.

## Reference

Expand Down
121 changes: 114 additions & 7 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,25 +153,132 @@
"options": {
"tsConfig": [
"projects/demo/tsconfig.json",
"projects/demo/tsconfig.spec.json",
"projects/demo/e2e/tsconfig.json"
"projects/demo/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"simple-demo": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "projects/simple-demo",
"sourceRoot": "projects/simple-demo/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/simple-demo",
"index": "projects/simple-demo/src/index.html",
"main": "projects/simple-demo/src/main.ts",
"polyfills": "projects/simple-demo/src/polyfills.ts",
"tsConfig": "projects/simple-demo/tsconfig.app.json",
"aot": false,
"stylePreprocessorOptions": {
"includePaths": [
"./node_modules/@guillotinaweb/pastanaga-angular/lib/styles"
]
},
"assets": [
"projects/simple-demo/src/favicon.ico",
"projects/simple-demo/src/assets",
{
"glob": "**/*",
"input": "./node_modules/@guillotinaweb/pastanaga-angular/lib/assets",
"output": "assets"
}
],
"styles": [
"projects/demo/src/pastanaga.scss",
"projects/simple-demo/src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "projects/simple-demo/src/environments/environment.ts",
"with": "projects/simple-demo/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"protractorConfig": "projects/demo/e2e/protractor.conf.js",
"devServerTarget": "demo:serve"
"browserTarget": "simple-demo:build"
},
"configurations": {
"production": {
"devServerTarget": "demo:serve:production"
"browserTarget": "simple-demo:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "simple-demo:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/simple-demo/src/test.ts",
"polyfills": "projects/simple-demo/src/polyfills.ts",
"tsConfig": "projects/simple-demo/tsconfig.spec.json",
"karmaConfig": "projects/simple-demo/karma.conf.js",
"assets": [
"projects/simple-demo/src/favicon.ico",
"projects/simple-demo/src/assets"
],
"styles": [
"projects/simple-demo/src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/simple-demo/tsconfig.app.json",
"projects/simple-demo/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}},
Expand Down
Loading

0 comments on commit 7f2f8c2

Please sign in to comment.