IMPORTANT: Before starting a Winkit Angular project, make sure to read about the basics of Winkit CLI.
- Adding Winkit Angular plugin
- Getting help
- Quick start
- Server configuration
- Primary database key
- Winkit Angular commands
- Known issues
- What's next?
To add the Winkit Angular plugin to your project, run:
winkit add:plugin angular
To get help on Winkit Angular commands run winkit angular --help
- Configure your server
- Run
winkit angular init <projectName>
- Choose the server you want to work with (Firestore or Strapi / Http)
- Provide the primary key you want to use in the project (or skip this step to use the default key)
- Enjoy!
Out-of-the-box Winkit Angular supports two server platforms - Strapi (visit website) and Firestore (visit website). Learn how to prepare your server for work with Winkit Angular:
If you want to use Winkit with Firestore you must first configure your project in Firebase.
Once the project is created, open /src/environments/environment.ts
and update firebaseConfig
with the project info.
Do the same for /src/environments/environment.prod.ts
with info for production environment.
-
install strapi globally
npm install strapi@alpha -g
-
Run the following command line in your terminal:
strapi new strapi-winkit
-
Go to your project and launch the server:
cd strapi-winkit strapi start
-
Create your first admin user
-
Open
strapi-winkit/plugins/users-permissions/models/User.settings.json
-
Replace the content with the following:
{ "connection": "default", "info": { "name": "user", "description": "" }, "attributes": { "username": { "type": "string", "minLength": 3, "unique": true, "configurable": false, "required": true }, "email": { "type": "email", "minLength": 6, "configurable": false, "required": true }, "password": { "type": "password", "minLength": 6, "configurable": false, "private": true }, "confirmed": { "type": "boolean", "default": false, "configurable": false }, "blocked": { "type": "boolean", "default": false, "configurable": false }, "role": { "model": "role", "via": "users", "plugin": "users-permissions", "configurable": false }, "userRole": { "default": "", "type": "string" }, "firstName": { "default": "", "type": "string" }, "lastName": { "default": "", "type": "string" }, "description": { "default": "", "type": "string" }, "telephone": { "default": "", "type": "string" }, "profileImg": { "default": "", "type": "string" }, "dateOfBirth": { "default": "", "type": "integer" }, "registeredAt": { "default": "", "type": "integer" }, "isMale": { "default": false, "type": "boolean" }, "media": { "collection": "file", "via": "related", "plugin": "upload" } }, "collectionName": "users-permissions_user" }
-
Go to
http://localhost:1337/admin/plugins/content-manager/user?source=users-permissions
-
Open the admin detail and populate the userRole field with the value
ADMIN
then save. -
Now you can log into Winkit using these user credentials!
In your Winkit project you can either use the default primary database key or configure a custom primary database key.
The default key is 'id' (for the front-end) and '_id' (for the back-end). To use a different key provide it when prompted for the primary key upon initializing your project.
After initializing the project you can still change the primary key at any point in time by following these steps:
- In the <project folder>/winkit.conf.json file add or edit the
primaryKey
key by providing your value (minimum 2 characters). - Edit the <project folder>/src/app/@core/models/Mappable.ts file by providing the name of your primaryKey as the first key of the Mappable interface;
- Update all models in your project using the
winkit angular update model ...
command (more info);
Initializes a new WDK Angular application in a new folder.
winkit angular init myproject
The application works right out of the box. Included are: authentication, password recovery, user CRUD, profile page, file upload, etc.
Generate a new model and its associated server model, ready to be mapped.
NOTE: If you are using Strapi remember to also:
- create the model on the server-side (ex. using the Strapi dashboard)
- add the
wid: string
parameter for both models
(if you are using Firestore you don't have to do anything because everything will be managed by WDK Angular)
Let's explain with an example:
winkit angular generate model Foo
This command will generate the following file structure in the src/app/modules/ folder:
foo/
models/
Foo.ts
ServerFoo.ts
FooDataFactory.ts
foo.conf.json
foo.module.ts
foo.routing.ts
NOTE: The foo/ directory and the foo.conf.json, foo.module.ts and foo.routing.ts files are only generated if they don't exist yet.
-
map(obj: ServerFoo): Foo : maps the model starting from its server model.
Ex. const foo = new Foo().map(serverFoo);
-
mapReverse(): ServerFoo : maps the model starting from its server model.
Ex. const serverFoo = foo.mapReverse();
-
static map(obj: Foo): ServerFoo : this method is called by the model mapReverse function.
It returns a server model (in this case
ServerFoo
) with properties initialized using the logic provided in the privategetMappedAttribute
method (see below).Model properties which exist on the model only and don't exist on the server should have the
existsOnModelOnly
attribute in the <model>.conf.json file set totrue
. This way they will not be initialized on the server model returned by the method. -
static mapReverse(serverObject: ServerUser): User : this method is called by the model map function.
It returns a model (in this case
Foo
) with properties initialized using the logic provided in the privategetReverseMappedAttribute
method (see below).Model properties which exist on the model only and don't exist on the server should have the
existsOnServerOnly
attribute in the <model>.conf.json file set totrue
. This way they will not be initialized on the model returned by the method. -
private static getMappedAttribute(model: User, prop: ModelProperty): string : this method is used in the server model map function to compute the value of the server model attribute.
The default logic is:
typeof model[localName] !== 'undefined' ? model[localName] : defaultValue
To provide your own logic, use the
case
clause which corresponds to the attribute. -
private static getReverseMappedAttribute(serverObject: ServerUser, prop: ModelProperty): string : this method is called by the server model mapReverse function to compute the value of the model attribute.
The default logic is:
typeof serverObject[serverName] !== 'undefined' ? serverObject[serverName] : defaultValue
To provide your own logic, use the
case
clause which corresponds to the attribute.
File providing data used by the model's components. This file is updated by Winkit Angular based on the htmlConfig
configuration in model properties.
The model configuration file. For more information on using the file, see the update model documentation.
The model module file - a standard Angular module which handles all the data and dependencies related to the model.
The model routing file. It exports an object with 2 properties:
componentRoutes
(required): an array of Angular type RoutesrouteInfo
(optional): an object of type RouteInfo
Generate a new service for given model, so you'll be ready to implement CRUD that works with the server chosen in the initialization.
NOTE: If the associated model does not exist yet, it is generated together with all necessary files (for more info see generate model section), before the the service file is generated.
Let's explain with an example:
winkit angular generate|g service Foo
This command will generate the service and add it to the foo.module.ts:
on creation the service includes methods that allow you to:
- Create model
- Update model
- Delete model
- Get model by id
- Get paginated list
Generate a new detail component for given model and implement its routing, so you'll be ready to display model info.
Let's explain with an example:
winkit angular generate|g detail Foo
This command will generate:
- foo-detail.component.ts
- foo-detail.component.html
- foo-detail.component.scss
This component implements the CRUD of the Model, including validation.
By default the generated route is accessible just by authenticated user with ADMIN role.
Thanks to this route you can:
- Create new instance of this model:
localhost:5000/foo/new
- Detail / Update the instance with given id, ex. #1:
localhost:5000/foo/1
NOTE: The model info will be displayed in the detail component inside a <form>
element. You can manage the form control elements inside the form by:
- editing the
<modelName>-detail.component.html
file (make sure to add"skipUpdate": true
to the property's config in <model>.conf.json) - editing the
<modelName>.conf.json
configuration file and updating the model and detail (more info) - passing an array of type FormControlList as the 2nd argument in the
<modelName>DataFactory.getFormControls()
call in<modelName>-detail.component.ts
(make sure to add"skipUpdate": true
to the property's config in <model>.conf.json). For example:
.../someModel-detail.component.ts
this.formControlList = ZdueDataFactory.getFormControls(this, [
{name: 'sometext', type: FormControlType.TEXT}
]);
.../someModel.conf.json
"properties": [
{"name": "someText", "skipUpdate": true},
...
]
Generates a new list component for given model, implements its routing and adds the link to the sidebar, so the list is ready to be displayed, including pagination and filtering.
Let's explain with an example:
winkit angular generate|g list Foo
This command will generate:
- foo-list.component.ts
- foo-list.component.html
- foo-list.component.scss
This component includes the paginated table list and the filter component.
By default the generated route is accessible just by authenticated user with ADMIN role.
Every element in the table includes a link to its detail page.
By default the link is visible just to ADMIN users.
Updates a model based on the configuration in the <name>.conf.json file.
The schema of the <name>.conf.json configuration file is the following:
- "properties" (
Array<ModelProperty>
): an array of ModelProperty objects.
IMPORTANT: To exclude a ModelProperty from being updated by Winkit (ex. because you want to something custom with it), set its skipUpdate
property to true
(see below for more info);
The structure of the ModelProperty object is the following:
- name (
string
: required): the name of the model property; - serverName (
string
: optional): name of the corresponding server model property, if different from ModelProperty.name; - isOptional (
boolean
: optional): adds TypeScript's optional class marker to a property (more info); - type (
string
: optional): a string containing a typescript type (more info); - serverType (
string
: optional): name of the corresponding server model type, if different from ModelProperty.type; - value (
any
: optional): the default value assigned to the model on initialization. Setting this key results in ignoring the type and optional keys; - isManuallyUpdated (
boolean
: optional): setting this value totrue
results in the model property being skipped if it already exists and is initialized on the model, when the model is updated usingwinkit angular update|u ...
. It also results in the property not being added to the model detail. For info on rectifying this, see this section; - mapReverseName (
string
: optional): The name of the model property to which the server model property value should be assigned in the mapReverse method of the server model; - relationship (
string
: optional): Maps the value of the provided model property to the current property, e.g. the following configuration:{"name": "wid", "relationship": "id", ...}
will result in mapping the value of theid
property to thewid
property in the model constructor and the server model map method; - mapReverseRelationship (
string
: optional): Maps the value of the provided server model property to the current property, e.g. the following configuration:{"name": "wid", "mapReverseRelationship": "_id", ...}
will result in mapping the value of the_id
property to thewid
property in the server model mapReverse method; - existsOnModelOnly (
boolean
: optional): Excludes the model property from being initialized inside the Server<Model>.map method; - existsOnServerOnly (
boolean
: optional): Excludes the model property from being initialized inside the Server<Model>.mapReverse method; - htmlConfig (
object
: optional): An object containing configuration of a single form control element. Must be set for the form control element to be displayed in the detail component of a given model. For more information see Structure of the htmlConfig object section below;
NOTE: The primary key property settings are not affected by the <name>.conf.json configuration.
The structure of htmlConfig
object mostly reflects attributes of an HTMLInputElement but also has some additional settings:
GENERAL
- type (
FormControlType | HTMLInputElement.type
): a HTMLInputElement type or one of the special types listed in the FormControlType enum; - ngIf (
boolean
: optional): sets the value of the angularngIf
directive (more info) of the Form Element; - required (
boolean
: optional): sets therequired
attribute of the input element; - disabled (
boolean
: optional): sets thedisabled
attribute of the input element; - readonly (
boolean
: optional): sets thereadonly
attribute of the input element; - pattern (
boolean
: optional): sets thepattern
attribute of the input element; - wrapperClass (
string
: optional): The class of the generated HTML element. Default value:col-sm-6 mb-3
- innerWrapperClass (
string
: optional): The class of the<div>
element that wraps the contents of the generated HTML element. - order (
number | string
: optional): Sets the CSS order property of the form element. - inputFeedbackText (
string
: optional): The text inside the<small>
element, which is displayed when the value of the input is invalid. NOTE: Setting theinputFeedbackText
attribute is the condition for displaying the<small>
feedback element. - inputFeedbackExample (
string
: optional): The italicized text displayed in round brackets afterinputFeedbackText
inside the<small>
element.
FORM ELEMENT TYPE: SELECT
- options (
Array<string | {name: string, value: any}>
: required): The list of<option>
elements that will be generated inside the<select>
element.
FORM ELEMENT TYPE: MEDIA
- allowedTypes (
Array<MediaType>
: required): Array of MediaType enum values.
FORM ELEMENT TYPE: TEXTAREA
- rows (
number
: optional): sets therows
attribute of the<textarea>
element. Default value:6
IMPORTANT:
- Inside htmlConfig you can use the
that
keyword to reference an external object. By default this object is the current model's detail component class (ex. FooDetailComponent). This is achieved by passingthis
as the 1st argument in the<modelName>DataFactory.getFormControls()
call in<modelName>-detail.component.ts
. To reference a different object pass it as the 1st argument instead ofthis
. - To set a string literal as a value in htmlConfig wrap it in additional quotes, ex.:
"'example string literal'"
Let's explain with an example:
src/app/modules/foo/foo.conf.json
{
"properties": [
{"name": "first", "optional": true},
{"name": "second", "type": "{id: number, [key: string]: any, someKey: SomeClass[]}"},
{"name": "third", "value": "['some string', 1.2]"},
{"name": "fourth", "skipUpdate": true},
{"name": "fifth", "type": "string", "htmlConfig": {
"type": "FormControlType.SELECT", "required": true, "options": "that.fifthPropOptions"
}}
]
}
With the above configuration, running this command:
winkit angular update|u model Foo
will result in the following property settings in Foo.ts
and ServerFoo.ts
models:
...
id: string;
wid: string;
first?;
second: {id: number, [key: string]: any, someKey: SomeClass[]};
third = ['some string', 1.2];
fifth: string;
...
constructor()
constructor(id?: string,
first?,
second?: {id: number, [key: string]: any, someKey: SomeClass[]},
third?: any,
fifth?: string) {
this.id = typeof id !== 'undefined' ? id : null;
this.wid = typeof id !== 'undefined' ? id : null;
this.first = typeof first !== 'undefined' ? first : null;
this.second = typeof second !== 'undefined' ? second : null;
this.third = typeof third !== 'undefined' ? third : ['some string', 1.2];
this.fifth = typeof fifth !== 'undefined' ? fifth : null;
}
...
...
_id?: string;
wid?: string;
first?;
second: {id: number, [key: string]: any, someKey: SomeClass[]};
third: any = ['some string', 1.2];
fifth: string;
...
static map(obj: Zuno): ServerZuno {
const o = {} as ServerZuno;
o._id = typeof obj.id !== 'undefined' ? obj.id : null;
o.wid = typeof obj.id !== 'undefined' ? obj.id : null;
o.first = typeof obj.first !== 'undefined' ? obj.first : null;
o.second = typeof obj.second !== 'undefined' ? obj.second : null;
o.third = typeof obj.third !== 'undefined' ? obj.third : null;
o.fifth = typeof obj.fifth !== 'undefined' ? obj.fifth : null;
return o;
}
...
static mapReverse(serverObject: ServerZuno): Zuno {
const o = {} as Zuno;
o.id = typeof serverObject._id !== 'undefined' ? serverObject._id : null;
o.wid = typeof serverObject._id !== 'undefined' ? serverObject._id : null;
o.first = typeof serverObject.first !== 'undefined' ? serverObject.first : null;
o.second = typeof serverObject.second !== 'undefined' ? serverObject.second : null;
o.third = typeof serverObject.third !== 'undefined' ? serverObject.third : null;
o.fifth = typeof serverObject.fifth !== 'undefined' ? serverObject.fifth : null;
return o;
}
...
...
static getFormControls = (that: any, customControlList: FormControlList = []): FormControlList => {
const generatedFormControls: FormControlList = [
{name: 'fifth', required: true, type: FormControlType.SELECT, options: that.fifthPropOptions}
];
...
- Strapi has been reported to not support Node.js >9 versions on certain versions of Windows. Link to issue
- In Strapi 3.0.0-alpha.15 and 3.0.0-alpha.16 there is a bug causing update actions of User objects to fail. For possible solutions see this Strapi issue report
- In Strapi 3.0.0-alpha.15 and 3.0.0-alpha.16 there is a bug causing create requests for non-User models to return a 404 error, even though instances of models are correctly created. See this Strapi issue report
- In Firestore the filter feature is case sensitive and must match the whole value
- Add the generated parameter to the table header in the list component
This command will delete the model and all its associated files.