├── .dockerignore
├── .editorconfig
├── .github
└── workflows
│ └── pip-rating.yml
├── .gitignore
├── .idea
└── angular-django.iml
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── angular_django.svg
├── compose
└── gunicorn
│ └── entrypoint.sh
├── conf
└── nginx
│ └── conf.d
│ └── demo.conf
├── demo
├── angular
│ ├── .browserslistrc
│ ├── .editorconfig
│ ├── .gitignore
│ ├── README.md
│ ├── angular.json
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── package-lock.json
│ ├── package.json
│ ├── proxy.conf.json
│ ├── src
│ │ ├── app
│ │ │ ├── api-guide
│ │ │ │ ├── api-guide.component.html
│ │ │ │ ├── api-guide.component.scss
│ │ │ │ ├── api-guide.component.spec.ts
│ │ │ │ ├── api-guide.component.ts
│ │ │ │ └── api-guide.module.ts
│ │ │ ├── app-routing.module.ts
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── code-highlight
│ │ │ │ ├── code-highlight.component.html
│ │ │ │ ├── code-highlight.component.scss
│ │ │ │ ├── code-highlight.component.spec.ts
│ │ │ │ ├── code-highlight.component.ts
│ │ │ │ └── code-highlight.module.ts
│ │ │ ├── external-html
│ │ │ │ ├── external-html.component.html
│ │ │ │ ├── external-html.component.scss
│ │ │ │ ├── external-html.component.spec.ts
│ │ │ │ ├── external-html.component.ts
│ │ │ │ └── external-html.module.ts
│ │ │ ├── first-steps
│ │ │ │ ├── first-steps.component.html
│ │ │ │ ├── first-steps.component.scss
│ │ │ │ ├── first-steps.component.spec.ts
│ │ │ │ ├── first-steps.component.ts
│ │ │ │ └── first-steps.module.ts
│ │ │ ├── form-api-service
│ │ │ │ ├── form-api-service.component.html
│ │ │ │ ├── form-api-service.component.scss
│ │ │ │ ├── form-api-service.component.spec.ts
│ │ │ │ ├── form-api-service.component.ts
│ │ │ │ ├── form-api-service.module.ts
│ │ │ │ └── form-pokemon
│ │ │ │ │ ├── form-pokemon.component.html
│ │ │ │ │ ├── form-pokemon.component.scss
│ │ │ │ │ ├── form-pokemon.component.spec.ts
│ │ │ │ │ └── form-pokemon.component.ts
│ │ │ ├── github-code
│ │ │ │ ├── github-code.component.html
│ │ │ │ ├── github-code.component.scss
│ │ │ │ ├── github-code.component.ts
│ │ │ │ └── github-code.module.ts
│ │ │ ├── index
│ │ │ │ ├── form
│ │ │ │ │ ├── form.component.html
│ │ │ │ │ ├── form.component.scss
│ │ │ │ │ ├── form.component.spec.ts
│ │ │ │ │ └── form.component.ts
│ │ │ │ ├── index.component.html
│ │ │ │ ├── index.component.scss
│ │ │ │ ├── index.component.spec.ts
│ │ │ │ ├── index.component.ts
│ │ │ │ ├── index.module.ts
│ │ │ │ └── list
│ │ │ │ │ ├── list.component.html
│ │ │ │ │ ├── list.component.scss
│ │ │ │ │ ├── list.component.spec.ts
│ │ │ │ │ └── list.component.ts
│ │ │ ├── installation
│ │ │ │ ├── installation.component.html
│ │ │ │ ├── installation.component.scss
│ │ │ │ ├── installation.component.spec.ts
│ │ │ │ ├── installation.component.ts
│ │ │ │ └── installation.module.ts
│ │ │ ├── list-api-service
│ │ │ │ ├── all-shape
│ │ │ │ │ ├── all-shape.component.html
│ │ │ │ │ ├── all-shape.component.scss
│ │ │ │ │ ├── all-shape.component.spec.ts
│ │ │ │ │ └── all-shape.component.ts
│ │ │ │ ├── list-api-service.component.html
│ │ │ │ ├── list-api-service.component.scss
│ │ │ │ ├── list-api-service.component.spec.ts
│ │ │ │ ├── list-api-service.component.ts
│ │ │ │ ├── list-api-service.module.ts
│ │ │ │ ├── list-pokemon
│ │ │ │ │ ├── list-pokemon.component.html
│ │ │ │ │ ├── list-pokemon.component.scss
│ │ │ │ │ ├── list-pokemon.component.spec.ts
│ │ │ │ │ └── list-pokemon.component.ts
│ │ │ │ └── paginate-pokemon
│ │ │ │ │ ├── paginate-pokemon.component.html
│ │ │ │ │ ├── paginate-pokemon.component.scss
│ │ │ │ │ ├── paginate-pokemon.component.spec.ts
│ │ │ │ │ └── paginate-pokemon.component.ts
│ │ │ ├── material-components
│ │ │ │ ├── material-components.component.html
│ │ │ │ ├── material-components.component.scss
│ │ │ │ ├── material-components.component.spec.ts
│ │ │ │ ├── material-components.component.ts
│ │ │ │ ├── material-components.module.ts
│ │ │ │ └── table
│ │ │ │ │ └── table.module.ts
│ │ │ ├── retrieve-api-service
│ │ │ │ ├── retrieve-api-service.component.html
│ │ │ │ ├── retrieve-api-service.component.scss
│ │ │ │ ├── retrieve-api-service.component.spec.ts
│ │ │ │ ├── retrieve-api-service.component.ts
│ │ │ │ ├── retrieve-api-service.module.ts
│ │ │ │ ├── retrieve-pokemon
│ │ │ │ │ ├── retrieve-pokemon.component.html
│ │ │ │ │ ├── retrieve-pokemon.component.scss
│ │ │ │ │ ├── retrieve-pokemon.component.spec.ts
│ │ │ │ │ └── retrieve-pokemon.component.ts
│ │ │ │ └── retrieve-user
│ │ │ │ │ ├── retrieve-user.component.html
│ │ │ │ │ ├── retrieve-user.component.scss
│ │ │ │ │ ├── retrieve-user.component.spec.ts
│ │ │ │ │ └── retrieve-user.component.ts
│ │ │ └── shared
│ │ │ │ ├── api.service.spec.ts
│ │ │ │ ├── api.service.ts
│ │ │ │ ├── autocomplete-type.component.ts
│ │ │ │ ├── interceptor.ts
│ │ │ │ └── shared.module.ts
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ └── angular_django.svg
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── jekyll-github.scss
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
└── django
│ ├── demo
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│ ├── dev-requirements.in
│ ├── dev-requirements.txt
│ ├── manage.py
│ ├── pokedex
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── filters.py
│ ├── import_pokedex.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ └── import_pokedex.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── viewsets.py
│ ├── requirements.in
│ └── requirements.txt
├── docker-compose.yml
├── docs
├── Makefile
├── api_guide.rst
├── build_docs.py
├── conf.py
├── first_steps.rst
├── index.rst
├── installation.rst
├── make.bat
├── requirements.in
└── requirements.txt
├── pypi_readme.rst
├── requirements.in
├── setup.cfg
├── setup.py
└── src
├── angular
├── .gitignore
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── projects
│ └── angular-django
│ │ ├── README.md
│ │ ├── karma.conf.js
│ │ ├── material
│ │ ├── package.json
│ │ └── src
│ │ │ ├── angular-django-material-table
│ │ │ ├── angular-django-material-table.component.css
│ │ │ ├── angular-django-material-table.component.html
│ │ │ ├── angular-django-material-table.component.spec.ts
│ │ │ ├── angular-django-material-table.component.ts
│ │ │ ├── angular-django-material-table.directive.ts
│ │ │ ├── angular-django-material-table.interface.ts
│ │ │ ├── angular-django-material-table.module.ts
│ │ │ └── shift-click-directive.ts
│ │ │ ├── angular-django-material.module.ts
│ │ │ └── public_api.ts
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src
│ │ ├── lib
│ │ │ ├── angular-django.component.spec.ts
│ │ │ ├── angular-django.component.ts
│ │ │ ├── angular-django.module.ts
│ │ │ ├── angular-django.service.spec.ts
│ │ │ ├── angular-django.service.ts
│ │ │ ├── api.service.spec.ts
│ │ │ ├── api.service.ts
│ │ │ ├── form.ts
│ │ │ ├── get-display.pipe.ts
│ │ │ ├── serializer.service.spec.ts
│ │ │ ├── serializer.service.ts
│ │ │ ├── utility-types.ts
│ │ │ ├── utils.ts
│ │ │ └── widgets.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.lib.prod.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
├── tsconfig.json
└── tslint.json
└── django
└── angular_django
├── __init__.py
├── management
├── __init__.py
└── commands
│ ├── __init__.py
│ └── angular_classes.py
├── metadata.py
├── pagination.py
├── rest_framework.py
└── typescript.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !docs/*.txt
3 | !docs/*.py
4 | !docs/*.rst
5 | !docs/Makefile
6 | !docs/_static
7 | !demo/angular/src
8 | !demo/angular/*.json
9 | !src/angular/projects
10 | !src/angular/*.json
11 | !LICENSE
12 | !*.in
13 | !setup.*
14 | !src/django/angular_django
15 | !demo/django/*.txt
16 | !demo/django/*.py
17 | !demo/django/demo
18 | !demo/django/pokedex
19 | !compose/
20 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 4
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.{yml,yaml,ts}]
13 | indent_size = 2
14 | quote_type = single
15 |
16 | [*.py]
17 | line_length=120
18 | known_first_party=customer_panel
19 | multi_line_output=3
20 | default_section=THIRDPARTY
21 |
22 | [*.md]
23 | max_line_length = off
24 | trim_trailing_whitespace = false
25 |
26 | [Makefile]
27 | indent_style = tab
28 |
--------------------------------------------------------------------------------
/.github/workflows/pip-rating.yml:
--------------------------------------------------------------------------------
1 | name: Pip-rating
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | schedule:
8 | - cron: '0 0 * * SUN'
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | permissions: write-all
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Run pip-rating
17 | uses: Nekmo/pip-rating@master
18 | with:
19 | create_badge: true
20 | badge_style: flat-square
21 | badge_branch: pip-rating-badge
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Python template
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 | *$py.class
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | .eggs/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98 | __pypackages__/
99 |
100 | # Celery stuff
101 | celerybeat-schedule
102 | celerybeat.pid
103 |
104 | # SageMath parsed files
105 | *.sage.py
106 |
107 | # Environments
108 | .env
109 | .venv
110 | env/
111 | venv/
112 | ENV/
113 | env.bak/
114 | venv.bak/
115 |
116 | # Spyder project settings
117 | .spyderproject
118 | .spyproject
119 |
120 | # Rope project settings
121 | .ropeproject
122 |
123 | # mkdocs documentation
124 | /site
125 |
126 | # mypy
127 | .mypy_cache/
128 | .dmypy.json
129 | dmypy.json
130 |
131 | # Pyre type checker
132 | .pyre/
133 |
134 | # pytype static type analyzer
135 | .pytype/
136 |
137 | # Cython debug symbols
138 | cython_debug/
139 |
140 | node_modules
141 |
--------------------------------------------------------------------------------
/.idea/angular-django.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.8 as gunicorn-build
2 | ENV PYTHONUNBUFFERED 1
3 |
4 | RUN mkdir -p /app /tmp/django/src/django
5 | WORKDIR /app
6 | COPY demo/django/requirements.txt .
7 | COPY setup.* *.in LICENSE /tmp/django/
8 | RUN pip install -r requirements.txt
9 | COPY compose/gunicorn/entrypoint.sh /
10 | RUN chmod +x "/entrypoint.sh"
11 | COPY src/django/ /tmp/django/src/django
12 | RUN cd /tmp/django && python setup.py install
13 | COPY demo/django ./
14 |
15 | ENTRYPOINT ["/entrypoint.sh"]
16 | CMD ["/usr/local/bin/gunicorn", "-b", "0.0.0.0:8000", "demo.wsgi:application"]
17 |
18 |
19 | FROM python:3.8 as docs
20 | ENV OUTPUT_DOCS_DIRECTORY _build/docs
21 | WORKDIR /docs
22 | COPY docs/ .
23 | RUN pip install -r requirements.txt
24 | RUN make docs
25 |
26 |
27 | FROM node:14.12 as angular-src-build
28 | ENV PATH /angular-django/node_modules/.bin:$PATH
29 | RUN mkdir /angular-django
30 | WORKDIR /angular-django
31 | COPY src/angular/package.json src/angular/package-lock.json ./
32 | RUN npm i && ngcc
33 | COPY src/angular ./
34 | RUN ng build --prod
35 | RUN ln -s /angular-django/dist/angular-django /angular-django/node_modules/angular-django
36 |
37 |
38 | FROM node:14.12 as angular-demo-build
39 | ENV PATH /app/node_modules/.bin:$PATH
40 | RUN mkdir /app
41 | WORKDIR /app
42 | COPY demo/angular/package.json demo/angular/package-lock.json ./
43 | RUN npm ci && ngcc
44 | COPY --from=angular-src-build /angular-django/dist/ /app/node_modules/
45 | COPY demo/angular ./
46 | COPY --from=docs /docs/_build/ /app/src/assets/
47 | RUN ng build --prod
48 |
49 |
50 | FROM nginx:1.19 as nginx-build
51 |
52 | COPY --from=angular-demo-build /app/dist/angular-demo/ /angular/
53 | ENTRYPOINT ["nginx", "-g", "daemon off;"]
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2020, Nekmo
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
12 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include *.rst
3 | include requirements.in
4 |
5 | recursive-exclude * __pycache__
6 | recursive-exclude * *.py[co]
7 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | export VERSION := $(version)
3 |
4 | publish-angular:
5 | ifndef VERSION
6 | $(error version variable is not set. Use make version=major|minor|patch publish-angular)
7 | endif
8 | cd src/angular/projects/angular-django && npm version $(version) && cd ../../ && ng build --prod && cd dist/angular-django && npm publish
9 |
10 | publish-django:
11 | ifndef VERSION
12 | $(error version variable is not set. Use make version=major|minor|patch publish-django)
13 | endif
14 | rm -f dist/* && bumpversion $(VERSION) && python setup.py sdist bdist_wheel && twine check dist/* && twine upload dist/*
15 |
16 |
17 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ##############
2 | angular-django
3 | ##############
4 |
5 |
6 | .. image:: https://raw.githubusercontent.com/Nekmo/angular-django/pip-rating-badge/pip-rating-badge.svg
7 | :target: https://github.com/Nekmo/angular-django/actions/workflows/pip-rating.yml
8 | :alt: pip-rating badge
9 |
10 | .. image:: https://img.shields.io/github/actions/workflow/status/Nekmo/angular-django/test.yml?style=flat-square&maxAge=2592000&branch=master
11 | :target: https://github.com/Nekmo/angular-django/actions?query=workflow%3ATests
12 | :alt: Latest Tests CI build status
13 |
14 | .. image:: https://img.shields.io/pypi/v/angular-django.svg?style=flat-square
15 | :target: https://pypi.org/project/angular-django/
16 | :alt: Latest PyPI version
17 |
18 | .. image:: https://img.shields.io/pypi/pyversions/angular-django.svg?style=flat-square
19 | :target: https://pypi.org/project/angular-django/
20 | :alt: Python versions
21 |
22 | .. image:: https://img.shields.io/codeclimate/maintainability/Nekmo/angular-django.svg?style=flat-square
23 | :target: https://codeclimate.com/github/Nekmo/angular-django
24 | :alt: Code Climate
25 |
26 | .. image:: https://img.shields.io/codecov/c/github/Nekmo/angular-django/master.svg?style=flat-square
27 | :target: https://codecov.io/github/Nekmo/angular-django
28 | :alt: Test coverage
29 |
30 |
31 | .. raw:: html
32 |
33 |
34 |
36 |
37 | Work in Angular as in Django
38 |
39 |
40 | **Angular Django** is a framework to work in *Angular* as in *Django*. Use the Django classes in Angular to build
41 | **forms** and **data** grids in minutes. `A demo is available on the website `_.
42 |
43 | Angular-django consists of **two packages**: a package for *Angular* and an optional package for *Django*. To install
44 | the Angular package:
45 |
46 | .. code-block:: shell
47 |
48 | $ npm i angular-django
49 |
50 |
51 | To install the Django package:
52 |
53 | .. code-block:: shell
54 |
55 | $ pip install -U angular-django
56 |
57 | Full instructions are available `on the website `_.
58 |
59 |
60 | Features
61 | ========
62 | Some features available:
63 |
64 | * Use the methods and filters available in the Django Rest Framework to work with the API.
65 | * Build forms in minutes. Includes validation on frontend and backend. Selector choices are built with the server.
66 | * Easy-to-implement filtering, paging, and searching listings.
67 | * Use your Django classes and types in Angular. The library will transform the API values to the correct types.
68 |
--------------------------------------------------------------------------------
/angular_django.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
36 |
43 |
45 |
49 |
53 |
54 |
65 |
66 |
--------------------------------------------------------------------------------
/compose/gunicorn/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | set -o pipefail
5 | set -o nounset
6 |
7 | python manage.py collectstatic --no-input
8 | python manage.py migrate --no-input
9 | python manage.py import_pokedex
10 | python manage.py createsuperuser --username demo --email demo@nekmo.org --no-input || true
11 |
12 | exec "$@"
13 |
--------------------------------------------------------------------------------
/conf/nginx/conf.d/demo.conf:
--------------------------------------------------------------------------------
1 | upstream demo-upstream {
2 | ip_hash;
3 | server gunicorn:8000;
4 | }
5 |
6 |
7 | server {
8 | listen 80;
9 |
10 | set_real_ip_from 172.16.0.0/12;
11 | real_ip_header X-Forwarded-For;
12 | real_ip_recursive on;
13 |
14 | proxy_http_version 1.1;
15 | proxy_set_header Connection "";
16 | proxy_redirect off;
17 | proxy_set_header Host $http_host;
18 | proxy_set_header X-Real-IP $http_x_real_ip;
19 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
20 | proxy_set_header X-Forwarded-Host $server_name;
21 | proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
22 | proxy_connect_timeout 300s;
23 | proxy_read_timeout 300s;
24 |
25 | location @backend {
26 | proxy_pass http://demo-upstream;
27 | }
28 |
29 |
30 | location ^~ /static/ {
31 | alias /static/;
32 | }
33 |
34 | location /media/ {
35 | internal;
36 | alias /media/; # note the trailing slash
37 | }
38 |
39 | location /api/ {
40 | try_files $uri $uri/ @backend;
41 | }
42 |
43 | location / {
44 | root /angular;
45 | try_files $uri $uri/ /index.html;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/demo/angular/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # For the full list of supported browsers by the Angular framework, please see:
6 | # https://angular.io/guide/browser-support
7 |
8 | # You can see what browsers were selected by your queries by running:
9 | # npx browserslist
10 |
11 | last 1 Chrome version
12 | last 1 Firefox version
13 | last 2 Edge major versions
14 | last 2 Safari major versions
15 | last 2 iOS major versions
16 | Firefox ESR
17 | not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
18 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
19 |
--------------------------------------------------------------------------------
/demo/angular/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/demo/angular/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | yarn-error.log
41 | testem.log
42 | /typings
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/demo/angular/README.md:
--------------------------------------------------------------------------------
1 | # AngularDemo
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.3.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/demo/angular/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({
31 | spec: {
32 | displayStacktrace: StacktraceOption.PRETTY
33 | }
34 | }));
35 | }
36 | };
--------------------------------------------------------------------------------
/demo/angular/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('angular-demo app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/demo/angular/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo/angular/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../out-tsc/e2e",
6 | "module": "commonjs",
7 | "target": "es2018",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/angular/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, './coverage/angular-demo'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/demo/angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-demo",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "~11.0.5",
15 | "@angular/cdk": "^11.0.3",
16 | "@angular/common": "~11.0.5",
17 | "@angular/compiler": "~11.0.5",
18 | "@angular/core": "~11.0.5",
19 | "@angular/flex-layout": "^11.0.0-beta.33",
20 | "@angular/forms": "^11.0.5",
21 | "@angular/material": "^11.0.3",
22 | "@angular/platform-browser": "~11.0.5",
23 | "@angular/platform-browser-dynamic": "~11.0.5",
24 | "@angular/router": "~11.0.5",
25 | "@ngx-formly/core": "^5.10.3",
26 | "@ngx-formly/material": "^5.10.3",
27 | "ngx-highlightjs": "^4.1.2",
28 | "reflect-metadata": "^0.1.13",
29 | "rxjs": "~6.6.0",
30 | "tslib": "^2.0.0",
31 | "zone.js": "~0.10.2"
32 | },
33 | "devDependencies": {
34 | "@angular-devkit/build-angular": "~0.1100.5",
35 | "@angular/cli": "~11.0.5",
36 | "@angular/compiler-cli": "~11.0.5",
37 | "@types/node": "^12.11.1",
38 | "@types/jasmine": "~3.6.0",
39 | "@types/jasminewd2": "~2.0.3",
40 | "codelyzer": "^6.0.0",
41 | "jasmine-core": "~3.6.0",
42 | "jasmine-spec-reporter": "~5.0.0",
43 | "karma": "~5.1.1",
44 | "karma-chrome-launcher": "~3.1.0",
45 | "karma-coverage-istanbul-reporter": "~3.0.2",
46 | "karma-jasmine": "~4.0.0",
47 | "karma-jasmine-html-reporter": "^1.5.0",
48 | "protractor": "~7.0.0",
49 | "ts-node": "~8.3.0",
50 | "tslint": "~6.1.0",
51 | "typescript": "~4.0.2"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/demo/angular/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api/": {
3 | "target": "http://localhost:8000",
4 | "secure": false
5 | },
6 | "/static": {
7 | "target": "http://localhost:8000",
8 | "secure": false
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/demo/angular/src/app/api-guide/api-guide.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/angular/src/app/api-guide/api-guide.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/api-guide/api-guide.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/api-guide/api-guide.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ApiGuideComponent } from './api-guide.component';
4 |
5 | describe('ApiGuideComponent', () => {
6 | let component: ApiGuideComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ ApiGuideComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ApiGuideComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/api-guide/api-guide.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-api-guide',
5 | templateUrl: './api-guide.component.html',
6 | styleUrls: ['./api-guide.component.scss']
7 | })
8 | export class ApiGuideComponent implements OnInit {
9 |
10 | constructor() { }
11 |
12 | ngOnInit(): void {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/demo/angular/src/app/api-guide/api-guide.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ApiGuideComponent } from './api-guide.component';
4 | import {RouterModule, Routes} from '@angular/router';
5 | import {SharedModule} from '../shared/shared.module';
6 |
7 |
8 | const routes: Routes = [
9 | {
10 | path : '',
11 | component: ApiGuideComponent,
12 | },
13 | ];
14 |
15 |
16 | @NgModule({
17 | declarations: [ApiGuideComponent],
18 | imports: [
19 | CommonModule,
20 | RouterModule.forChild(routes),
21 | SharedModule,
22 | ]
23 | })
24 | export class ApiGuideModule { }
25 |
--------------------------------------------------------------------------------
/demo/angular/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | const routes: Routes = [
5 | {
6 | path : '',
7 | loadChildren : () => import('./index/index.module').then(m => m.IndexModule),
8 | },
9 | {
10 | path : 'installation',
11 | loadChildren : () => import('./installation/installation.module').then(m => m.InstallationModule),
12 | },
13 | {
14 | path : 'first-steps',
15 | loadChildren : () => import('./first-steps/first-steps.module').then(m => m.FirstStepsModule),
16 | },
17 | {
18 | path : 'api-guide',
19 | loadChildren : () => import('./api-guide/api-guide.module').then(m => m.ApiGuideModule),
20 | },
21 | {
22 | path : 'retrieve-api-service',
23 | loadChildren : () => import('./retrieve-api-service/retrieve-api-service.module').then(m => m.RetrieveApiServiceModule),
24 | },
25 | {
26 | path : 'list-api-service',
27 | loadChildren : () => import('./list-api-service/list-api-service.module').then(m => m.ListApiServiceModule),
28 | },
29 | {
30 | path : 'form-api-service',
31 | loadChildren : () => import('./form-api-service/form-api-service.module').then(m => m.FormApiServiceModule),
32 | },
33 | {
34 | path : 'material-components',
35 | loadChildren : () => import('./material-components/material-components.module').then(m => m.MaterialComponentsModule),
36 | },
37 | ];
38 |
39 | @NgModule({
40 | imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
41 | exports: [RouterModule]
42 | })
43 | export class AppRoutingModule { }
44 |
--------------------------------------------------------------------------------
/demo/angular/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
29 |
30 |
--------------------------------------------------------------------------------
/demo/angular/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | //:host,
2 | //.wrapper {
3 | // flex: 1;
4 | // display: flex;
5 | // flex-direction: column;
6 | //}
7 |
8 | mat-toolbar {
9 | a {
10 | color: white;
11 | text-decoration: none;
12 | }
13 | }
14 |
15 | .container {
16 | margin: 1em auto;
17 | padding: 0 1em;
18 | position: relative;
19 | max-width: 1024px;
20 | width: 100%;
21 | }
22 |
--------------------------------------------------------------------------------
/demo/angular/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async () => {
7 | await TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | });
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'angular-demo'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.componentInstance;
26 | expect(app.title).toEqual('angular-demo');
27 | });
28 |
29 | it('should render title', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.nativeElement;
33 | expect(compiled.querySelector('.content span').textContent).toContain('angular-demo app is running!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/demo/angular/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {ChangeDetectorRef, Component, OnDestroy} from '@angular/core';
2 | import {MediaMatcher} from '@angular/cdk/layout';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.scss']
8 | })
9 | export class AppComponent implements OnDestroy {
10 | title = 'angular-demo';
11 |
12 | mobileQuery: MediaQueryList;
13 |
14 | // tslint:disable-next-line:variable-name
15 | private _mobileQueryListener: () => void;
16 | openedByDefault = false;
17 |
18 | constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) {
19 | this.mobileQuery = media.matchMedia('(max-width: 600px)');
20 | this._mobileQueryListener = () => changeDetectorRef.detectChanges();
21 | this.mobileQuery.addListener(this._mobileQueryListener);
22 |
23 | }
24 |
25 | ngOnDestroy(): void {
26 | this.mobileQuery.removeListener(this._mobileQueryListener);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demo/angular/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { AppRoutingModule } from './app-routing.module';
5 | import { AppComponent } from './app.component';
6 | import {AngularDjangoModule} from 'angular-django';
7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8 | import {MatToolbarModule} from '@angular/material/toolbar';
9 | import {MatIconModule} from '@angular/material/icon';
10 | import {MatButtonModule} from '@angular/material/button';
11 | import {MatSidenavModule} from '@angular/material/sidenav';
12 | import {MatListModule} from '@angular/material/list';
13 | import {HIGHLIGHT_OPTIONS} from 'ngx-highlightjs';
14 | import {HighlightPlusModule} from 'ngx-highlightjs/plus';
15 | import {FormlyModule} from '@ngx-formly/core';
16 | import {AutocompleteTypeComponent} from './shared/autocomplete-type.component';
17 | import {MatAutocompleteModule} from '@angular/material/autocomplete';
18 | import {FormlyMaterialModule} from '@ngx-formly/material';
19 |
20 | @NgModule({
21 | declarations: [
22 | AppComponent
23 | ],
24 | imports: [
25 | BrowserModule,
26 | AppRoutingModule,
27 | AngularDjangoModule,
28 | BrowserAnimationsModule,
29 |
30 |
31 | MatAutocompleteModule,
32 | MatIconModule,
33 | MatButtonModule,
34 | MatToolbarModule,
35 | MatSidenavModule,
36 | MatListModule,
37 | FormlyMaterialModule,
38 | FormlyModule.forRoot({
39 | types: [
40 | {name: 'autocomplete', component: AutocompleteTypeComponent, wrappers: ['form-field']},
41 | ],
42 | }),
43 |
44 | // HighlightModule,
45 | HighlightPlusModule,
46 |
47 | ],
48 | providers: [
49 | {
50 | provide: HIGHLIGHT_OPTIONS,
51 | useValue: {
52 | coreLibraryLoader: () => import('highlight.js/lib/core'),
53 | lineNumbersLoader: () => import('highlightjs-line-numbers.js'), // Optional, only if you want the line numbers
54 | languages: {
55 | typescript: () => import('highlight.js/lib/languages/typescript'),
56 | python: () => import('highlight.js/lib/languages/python'),
57 | 'python-repl': () => import('highlight.js/lib/languages/python-repl'),
58 | shell: () => import('highlight.js/lib/languages/shell'),
59 | json: () => import('highlight.js/lib/languages/json'),
60 | xml: () => import('highlight.js/lib/languages/xml'),
61 | }
62 |
63 | // fullLibraryLoader: () => import('highlight.js'),
64 | }
65 | },
66 | ],
67 | bootstrap: [AppComponent]
68 | })
69 | export class AppModule { }
70 |
--------------------------------------------------------------------------------
/demo/angular/src/app/code-highlight/code-highlight.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/demo/angular/src/app/code-highlight/code-highlight.component.scss:
--------------------------------------------------------------------------------
1 | mat-card {
2 | margin-bottom: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/demo/angular/src/app/code-highlight/code-highlight.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CodeHighlightComponent } from './code-highlight.component';
4 |
5 | describe('CodeHighlightComponent', () => {
6 | let component: CodeHighlightComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ CodeHighlightComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(CodeHighlightComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/code-highlight/code-highlight.component.ts:
--------------------------------------------------------------------------------
1 | import {AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-code-highlight',
5 | // template: ` `
6 | templateUrl: './code-highlight.component.html',
7 | styleUrls: ['./code-highlight.component.scss']
8 | })
9 | export class CodeHighlightComponent implements OnInit {
10 |
11 | constructor() { }
12 |
13 | @Input() code: string;
14 |
15 | ngOnInit(): void {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/demo/angular/src/app/code-highlight/code-highlight.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { CodeHighlightComponent } from './code-highlight.component';
4 | import {HighlightPlusModule} from 'ngx-highlightjs/plus';
5 | import {MatCardModule} from '@angular/material/card';
6 |
7 |
8 |
9 | @NgModule({
10 | declarations: [CodeHighlightComponent],
11 | imports: [
12 | CommonModule,
13 | HighlightPlusModule,
14 | MatCardModule,
15 | ],
16 | exports: [
17 | CodeHighlightComponent,
18 | ]
19 | })
20 | export class CodeHighlightModule { }
21 |
--------------------------------------------------------------------------------
/demo/angular/src/app/external-html/external-html.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/demo/angular/src/app/external-html/external-html.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/external-html/external-html.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/external-html/external-html.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ExternalHtmlComponent } from './external-html.component';
4 |
5 | describe('ExternalHtmlComponent', () => {
6 | let component: ExternalHtmlComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ ExternalHtmlComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ExternalHtmlComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/external-html/external-html.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ChangeDetectionStrategy,
3 | ChangeDetectorRef,
4 | Component,
5 | Input,
6 | OnChanges,
7 | OnInit,
8 | SimpleChanges
9 | } from '@angular/core';
10 | import {HttpClient} from '@angular/common/http';
11 | import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
12 |
13 | @Component({
14 | selector: 'app-external-html',
15 | templateUrl: './external-html.component.html',
16 | styleUrls: ['./external-html.component.scss'],
17 | changeDetection: ChangeDetectionStrategy.OnPush
18 | })
19 | export class ExternalHtmlComponent implements OnInit, OnChanges {
20 |
21 | @Input() htmlUrl: string;
22 | htmlContent: SafeHtml;
23 |
24 | constructor(public http: HttpClient, private ref: ChangeDetectorRef) {
25 | }
26 |
27 | ngOnInit(): void {
28 | }
29 |
30 | ngOnChanges(changes: SimpleChanges): void {
31 | if (changes.htmlUrl) {
32 | this.loadHtml();
33 | }
34 | }
35 |
36 | loadHtml(): void {
37 | this.http.get(this.htmlUrl, {responseType: 'text'})
38 | .subscribe((html: string) => {
39 | this.htmlContent = html;
40 | this.ref.markForCheck();
41 | });
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/demo/angular/src/app/external-html/external-html.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ExternalHtmlComponent } from './external-html.component';
4 |
5 |
6 | @NgModule({
7 | declarations: [ExternalHtmlComponent],
8 | imports: [
9 | CommonModule
10 | ],
11 | exports: [
12 | ExternalHtmlComponent,
13 | ]
14 | })
15 | export class ExternalHtmlModule { }
16 |
--------------------------------------------------------------------------------
/demo/angular/src/app/first-steps/first-steps.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | This demo
4 | The demo of this documentation uses the following models, viewsets and serializers:
5 |
6 |
7 |
--------------------------------------------------------------------------------
/demo/angular/src/app/first-steps/first-steps.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/first-steps/first-steps.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/first-steps/first-steps.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FirstStepsComponent } from './first-steps.component';
4 |
5 | describe('FirstStepsComponent', () => {
6 | let component: FirstStepsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ FirstStepsComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(FirstStepsComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/first-steps/first-steps.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {GithubFile} from '../github-code/github-code.component';
3 |
4 | @Component({
5 | selector: 'app-first-steps',
6 | templateUrl: './first-steps.component.html',
7 | styleUrls: ['./first-steps.component.scss']
8 | })
9 | export class FirstStepsComponent implements OnInit {
10 |
11 | tutorialFiles: GithubFile[] = [
12 | {name: 'models.py', directory: 'pokedex'},
13 | {name: 'serializers.py', directory: 'pokedex'},
14 | {name: 'viewsets.py', directory: 'pokedex'},
15 | ];
16 |
17 | constructor() { }
18 |
19 | ngOnInit(): void {
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/demo/angular/src/app/first-steps/first-steps.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { FirstStepsComponent } from './first-steps.component';
4 | import {RouterModule, Routes} from '@angular/router';
5 | import {SharedModule} from '../shared/shared.module';
6 |
7 |
8 | const routes: Routes = [
9 | {
10 | path : '',
11 | component: FirstStepsComponent,
12 | },
13 | ];
14 |
15 |
16 | @NgModule({
17 | declarations: [FirstStepsComponent],
18 | imports: [
19 | CommonModule,
20 | RouterModule.forChild(routes),
21 | SharedModule,
22 | ]
23 | })
24 | export class FirstStepsModule { }
25 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-api-service.component.html:
--------------------------------------------------------------------------------
1 | Create or update
2 |
3 | The create
and update
/ partial_update
/ save
methods allow you to
4 | create and update objects. The getFormFields
method allows to get the fields for
5 | formly-form , a popular library for generating forms.
6 |
7 |
8 | The getFormFields
method creates the fields including the options of the select input.
9 | In this example the habitat field is autocompleted via api.
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-api-service.component.scss:
--------------------------------------------------------------------------------
1 | mat-card {
2 | margin-bottom: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-api-service.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FormApiServiceComponent } from './form-api-service.component';
4 |
5 | describe('FormApiServiceComponent', () => {
6 | let component: FormApiServiceComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ FormApiServiceComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(FormApiServiceComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-api-service.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {GithubFile} from '../github-code/github-code.component';
3 |
4 |
5 | @Component({
6 | // tslint:disable-next-line:component-selector
7 | selector: 'django-select',
8 | templateUrl: './form-api-service.component.html',
9 | styleUrls: ['./form-api-service.component.scss']
10 | })
11 | export class FormApiServiceComponent implements OnInit {
12 |
13 | formPokemonFiles: GithubFile[] = [
14 | {name: 'form-pokemon.component.html', directory: 'form-api-service/form-pokemon'},
15 | {name: 'form-pokemon.component.ts', directory: 'form-api-service/form-pokemon'},
16 | {name: 'form-api-service.module.ts', directory: 'form-api-service'},
17 | {name: 'api.service.ts', directory: 'shared'},
18 | ];
19 |
20 | constructor() { }
21 |
22 | ngOnInit(): void {
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-api-service.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import {FormApiServiceComponent} from './form-api-service.component';
4 | import {FormlyMaterialModule} from '@ngx-formly/material';
5 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
6 | import {RouterModule, Routes} from '@angular/router';
7 | import {SharedModule} from '../shared/shared.module';
8 | import {MatButtonModule} from '@angular/material/button';
9 | import {FlexLayoutModule} from '@angular/flex-layout';
10 | import {MatFormFieldModule} from '@angular/material/form-field';
11 | import {MatIconModule} from '@angular/material/icon';
12 | import {MatInputModule} from '@angular/material/input';
13 | import {MatAutocompleteModule} from '@angular/material/autocomplete';
14 | import {MatNativeDateModule} from '@angular/material/core';
15 | import {FormlyMatDatepickerModule} from '@ngx-formly/material/datepicker';
16 | import { FormPokemonComponent } from './form-pokemon/form-pokemon.component';
17 | import {MatCardModule} from '@angular/material/card';
18 | import {HighlightPlusModule} from 'ngx-highlightjs/plus';
19 | import {FormlyModule} from '@ngx-formly/core';
20 | import {AutocompleteTypeComponent} from '../shared/autocomplete-type.component';
21 |
22 |
23 | const routes: Routes = [
24 | {
25 | path : '',
26 | component: FormApiServiceComponent,
27 | },
28 | ];
29 |
30 |
31 | @NgModule({
32 | declarations: [
33 | FormApiServiceComponent,
34 | FormPokemonComponent,
35 | ],
36 | imports: [
37 | FormlyModule.forChild({
38 | types: [
39 | {name: 'autocomplete', component: AutocompleteTypeComponent, wrappers: ['form-field']},
40 | ],
41 | }),
42 | CommonModule,
43 | SharedModule,
44 | MatButtonModule,
45 | MatFormFieldModule,
46 | MatIconModule,
47 | MatAutocompleteModule,
48 | FlexLayoutModule,
49 | MatNativeDateModule,
50 | FormlyMatDatepickerModule,
51 | FormsModule,
52 | ReactiveFormsModule,
53 | HighlightPlusModule,
54 | // FormlyModule.forRoot(),
55 | FormlyMaterialModule,
56 |
57 | RouterModule.forChild(routes),
58 | MatInputModule,
59 | MatCardModule,
60 | ],
61 | })
62 | export class FormApiServiceModule { }
63 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-pokemon/form-pokemon.component.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-pokemon/form-pokemon.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .display-flex {
3 | display: flex;
4 |
5 | [class*="flex-"] {
6 | padding-left: 10px;
7 | }
8 |
9 | [class*="flex-"]:first-child {
10 | padding-left: 0;
11 | }
12 | }
13 |
14 | .flex-1 { flex: 1; }
15 |
16 | .flex-2 { flex: 2; }
17 |
18 | .flex-3 { flex: 3; }
19 |
20 | .flex-4 { flex: 4; }
21 |
22 | .flex-5 { flex: 5; }
23 |
24 | .flex-6 { flex: 6; }
25 | }
26 |
27 | code {
28 | margin-top: 20px;
29 | }
30 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-pokemon/form-pokemon.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FormPokemonComponent } from './form-pokemon.component';
4 |
5 | describe('FormPokemonComponent', () => {
6 | let component: FormPokemonComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ FormPokemonComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(FormPokemonComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/form-api-service/form-pokemon/form-pokemon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {FormGroup} from '@angular/forms';
3 | import {FormlyFieldConfig} from '@ngx-formly/core';
4 | import {SpecieApi} from '../../shared/api.service';
5 | import {catchFormError} from 'angular-django';
6 |
7 | @Component({
8 | selector: 'app-form-pokemon',
9 | templateUrl: './form-pokemon.component.html',
10 | styleUrls: ['./form-pokemon.component.scss']
11 | })
12 | export class FormPokemonComponent implements OnInit {
13 |
14 | form = new FormGroup({});
15 | model = {};
16 | fields: FormlyFieldConfig[];
17 |
18 | constructor(public specieApi: SpecieApi) { }
19 |
20 | ngOnInit(): void {
21 | this.fields = this.specieApi.getFormFields([
22 | 'identifier', 'habitat', ['color', 'gender_rate', 'capture_rate', 'is_baby']
23 | ]);
24 | }
25 |
26 | submit(model): void {
27 | this.specieApi.create(model)
28 | .pipe(catchFormError(this.form)).subscribe();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/demo/angular/src/app/github-code/github-code.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | link
30 | Open on Github
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/demo/angular/src/app/github-code/github-code.component.scss:
--------------------------------------------------------------------------------
1 | .loading-label {
2 | margin: 2em 0;
3 | text-align: center;
4 | font-weight: 300;
5 | }
6 |
7 | .mat-card {
8 | padding: 0;
9 | margin-bottom: 3em;
10 | }
11 |
12 | .mat-card-header {
13 | margin: 0 !important;
14 | padding: 0 !important;
15 | display: flex;
16 | align-items: center;
17 | height: 40px;
18 | }
19 |
20 | .mat-card-content {
21 | position: relative;
22 | overflow: hidden;
23 | padding: 1px;
24 | margin-bottom: 0;
25 | }
26 |
27 | .mat-card-footer {
28 | padding: 10px !important;
29 | margin: 0 !important;
30 | display: flex;
31 | flex-direction: row-reverse;
32 |
33 | .gist-url {
34 | display: flex;
35 | align-items: center;
36 | font-weight: 300;
37 | font-size: 13px;
38 | // color: white;
39 | text-decoration: unset;
40 | }
41 |
42 | .mat-icon {
43 | font-size: 18px;
44 | width: 18px;
45 | height: 18px;
46 | //color: #d32f2f;
47 | }
48 | }
49 |
50 | .mat-icon {
51 | margin: 0 3px;
52 | }
53 |
54 | .code-wrapper {
55 | border-radius: 3px 3px 0 0;
56 | }
57 |
58 | ::ng-deep {
59 | .hljs {
60 | padding: 0;
61 | border: none;
62 | transition: border ease 1s;
63 | }
64 |
65 | .hljs-ln {
66 | padding: 10px 0;
67 |
68 | tr {
69 | &:first-child td {
70 | padding-top: 10px !important;
71 | }
72 |
73 | &:last-child td {
74 | padding-bottom: 10px !important;
75 | }
76 | }
77 | }
78 |
79 | /* for block of numbers */
80 | td.hljs-ln-numbers {
81 | user-select: none;
82 | text-align: center;
83 | color: #cccccc6b;
84 | border-right: 1px solid #cccccc1c;
85 | vertical-align: top;
86 | padding-right: 10px !important;
87 | padding-left: 10px !important;
88 | }
89 |
90 | /* for block of code */
91 | td.hljs-ln-code {
92 | padding-left: 10px !important;
93 | }
94 | }
95 |
96 | pre {
97 | margin: 0;
98 | max-height: 600px;
99 | overflow-y: auto;
100 | }
101 |
--------------------------------------------------------------------------------
/demo/angular/src/app/github-code/github-code.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit, ChangeDetectionStrategy, Input, ViewChild} from '@angular/core';
2 | import { BehaviorSubject } from 'rxjs';
3 | import { map } from 'rxjs/operators';
4 | import {HighlightLoader} from 'ngx-highlightjs';
5 | import {Gist} from 'ngx-highlightjs/plus';
6 | import {strict} from 'assert';
7 | import {MatTabGroup} from '@angular/material/tabs';
8 |
9 |
10 | export interface GithubFile {
11 | name: string;
12 | directory: string;
13 | }
14 |
15 | export interface GithubRoot {
16 | url: string;
17 | previewUrl: string;
18 | }
19 |
20 | export interface Dictionary {
21 | [Key: string]: T;
22 | }
23 |
24 | const GITHUB_ROOT: Dictionary = {
25 | angularDemo: {
26 | url: 'https://raw.githubusercontent.com/Nekmo/angular-django/master/demo/angular/src/app/',
27 | previewUrl: 'https://github.com/Nekmo/angular-django/blob/master/demo/angular/src/app/',
28 | },
29 | djangoDemo: {
30 | url: 'https://raw.githubusercontent.com/Nekmo/angular-django/master/demo/django/',
31 | previewUrl: 'https://github.com/Nekmo/angular-django/blob/master/demo/django',
32 | },
33 | };
34 |
35 |
36 | @Component({
37 | selector: 'app-github-code',
38 | templateUrl: './github-code.component.html',
39 | styleUrls: ['./github-code.component.scss'],
40 | changeDetection: ChangeDetectionStrategy.OnPush
41 | })
42 | export class GithubCodeComponent implements OnInit {
43 |
44 | private _stateSource = new BehaviorSubject({
45 | libLoaded: false,
46 | gistLoaded: false,
47 | text: ''
48 | });
49 |
50 | state$ = this._stateSource.pipe(
51 | map((state) => ({
52 | loaded: state.gistLoaded && state.libLoaded,
53 | text: state.text,
54 | gist: state.gist
55 | }))
56 | );
57 |
58 | @Input() files: GithubFile[];
59 | @Input() root = 'angularDemo';
60 | @ViewChild(MatTabGroup) tab: MatTabGroup;
61 | GITHUB_ROOT: Dictionary;
62 |
63 | constructor(public hljsLoader: HighlightLoader) {
64 | this.GITHUB_ROOT = GITHUB_ROOT;
65 | }
66 |
67 | ngOnInit() {
68 | this.setState({ libLoaded: false, gistLoaded: false, text: 'Loading gist...' });
69 | this.hljsLoader.ready.subscribe(() => this.setState({ libLoaded: true, text: '' }));
70 | }
71 |
72 | onGistLoad(gist: Gist) {
73 | this.setState({ gist, gistLoaded: true, text: 'Loading highlight.js library...' });
74 | }
75 |
76 | private setState(state: GistState) {
77 | this._stateSource.next({ ...this._stateSource.value, ...state });
78 | }
79 |
80 | get currentFileLink(): string {
81 | if (!this.tab) {
82 | return;
83 | }
84 | const file: GithubFile = this.files[this.tab.selectedIndex];
85 | return GITHUB_ROOT[this.root].previewUrl + '/' + file.directory + '/' + file.name;
86 | }
87 | }
88 |
89 | interface GistState {
90 | libLoaded?: boolean;
91 | gistLoaded?: boolean;
92 | text?: string;
93 | gist?: Gist;
94 | }
95 |
--------------------------------------------------------------------------------
/demo/angular/src/app/github-code/github-code.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import {GithubCodeComponent} from './github-code.component';
4 | import {MatCardModule} from '@angular/material/card';
5 | import {MatTabsModule} from '@angular/material/tabs';
6 | import {MatIconModule} from '@angular/material/icon';
7 | import {MatProgressBarModule} from '@angular/material/progress-bar';
8 | import {HighlightPlusModule} from 'ngx-highlightjs/plus';
9 |
10 |
11 |
12 | @NgModule({
13 | declarations: [GithubCodeComponent],
14 | imports: [
15 | CommonModule,
16 | MatCardModule,
17 | MatTabsModule,
18 | MatIconModule,
19 | MatProgressBarModule,
20 | HighlightPlusModule,
21 | ],
22 | exports: [
23 | GithubCodeComponent,
24 | ],
25 | })
26 | export class GithubCodeModule { }
27 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/form/form.component.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/form/form.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/index/form/form.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/index/form/form.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FormComponent } from './form.component';
4 |
5 | describe('FormComponent', () => {
6 | let component: FormComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ FormComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(FormComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/form/form.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {FormGroup} from '@angular/forms';
3 | import {FormlyFieldConfig} from '@ngx-formly/core';
4 | import {catchFormError} from 'angular-django';
5 | import {SpecieApi} from '../../shared/api.service';
6 |
7 | @Component({
8 | selector: 'app-form',
9 | templateUrl: './form.component.html',
10 | styleUrls: ['./form.component.scss']
11 | })
12 | export class FormComponent implements OnInit {
13 |
14 | form = new FormGroup({});
15 | model = {};
16 | fields: FormlyFieldConfig[];
17 |
18 | constructor(public specieApi: SpecieApi) { }
19 |
20 | ngOnInit(): void {
21 | this.fields = this.specieApi.getFormFields([
22 | 'identifier', 'habitat', ['color', 'is_baby']
23 | ]);
24 | }
25 |
26 | submit(model): void {
27 | this.specieApi.create(model)
28 | .pipe(catchFormError(this.form)).subscribe();
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/index.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Angular Django
4 |
Work in Angular as in Django
5 |
6 |
7 |
8 |
Use the methods and filters available in the Django Rest Framework to work with the API.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Build forms in minutes. Includes
20 | validation on frontend and
21 | backend . Selector choices are built with the server.
22 |
23 |
24 |
25 |
26 |
Easy-to-implement filtering , paging , and searching listings .
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Use your Django classes and types in Angular . The library will transform
38 | the API values to the correct types.
39 |
40 |
41 |
42 | How does it work?
43 |
44 |
45 |
Angular Django generates typescript classes
46 | from your Django Rest Framework viewsets and serializers classes .
47 |
51 |
52 |
53 |
54 |
In Angular Django the viewsets are called Apis and the
55 | serializers are called serializers too.
56 |
57 |
58 |
59 |
60 |
61 |
62 | Use APIs to make requests to the API such as listing items. The elements will be instantiated using
63 | the serializers .
64 |
65 |
66 |
67 |
68 | This project is under development. Play the demo on your machine, it is available with docker-compose.
69 |
70 |
71 |
72 | You can contribute to this project on Github .
73 |
74 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/index.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .display-flex {
3 | display: flex;
4 |
5 | [class*="flex-"] {
6 | padding-left: 10px;
7 | }
8 |
9 | [class*="flex-"]:first-child {
10 | padding-left: 0;
11 | }
12 | }
13 |
14 | .flex-1 { flex: 1; }
15 |
16 | .flex-2 { flex: 2; }
17 |
18 | .flex-3 { flex: 3; }
19 |
20 | .flex-4 { flex: 4; }
21 |
22 | .flex-5 { flex: 5; }
23 |
24 | .flex-6 { flex: 6; }
25 |
26 | .list {
27 | mat-card {
28 | padding-top: 0;
29 | padding-bottom: 0;
30 | }
31 | }
32 | }
33 |
34 | strong {
35 | color: #3F51B5;
36 | }
37 |
38 | h1 {
39 | font-size: 400%;
40 | }
41 |
42 | h2 {
43 | font-size: 300%;
44 | }
45 |
46 | .subtitle {
47 | font-size: 150%;
48 | }
49 |
50 |
51 | .flex {
52 | display: flex;
53 | align-items: center;
54 | margin-top: 80px;
55 | margin-bottom: 80px;
56 |
57 | p {
58 | font-size: 150%;
59 | line-height: 1.5;
60 | }
61 |
62 | .left {
63 | text-align: right;
64 | padding-right: 20px;
65 | }
66 |
67 | .right {
68 | text-align: left;
69 | padding-left: 20px;
70 | }
71 | }
72 |
73 |
74 | .example-3 {
75 | flex-direction: column;
76 | align-items: flex-start;
77 | }
78 |
79 | @media screen and (max-width: 1024px) {
80 | .flex {
81 | flex-direction: column;
82 |
83 | .left, .right {
84 | text-align: left;
85 | padding-left: 0;
86 | padding-right: 0;
87 | }
88 |
89 | p {
90 | order: 1;
91 | margin-right: 20px;
92 | }
93 | }
94 |
95 | .example-3 {
96 | align-items: center;
97 | > div {
98 | flex-direction: column;
99 |
100 | app-code-highlight {
101 | width: 100%;
102 | }
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/index.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { IndexComponent } from './index.component';
4 |
5 | describe('IndexComponent', () => {
6 | let component: IndexComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ IndexComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(IndexComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/index.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | const CODE_EXAMPLE_1 = '' +
4 | 'const users: Page = await UserApi.filter({is_active: true}).list();\n' +
5 | 'const user: User = users.first();\n' +
6 | 'await user.setPassword(\'newPassword\'):';
7 |
8 | const CODE_EXAMPLE_2 = '' +
9 | 'const specie: Specie = await SpecieApi.retrieve(1);\n' +
10 | '// Nested objects are instantiated using their serializers.\n' +
11 | 'specie.habitat.getName();';
12 |
13 | const CODE_EXAMPLE_3_1 = '' +
14 | '# models.py\n' +
15 | '# ---------\n' +
16 | 'class Pokemon(models.Model):\n' +
17 | ' identifier = models.CharField(max_length=50)\n' +
18 | ' specie = models.ForeignKey(Specie, on_delete=models.CASCADE)\n' +
19 | ' height = models.PositiveSmallIntegerField()\n' +
20 | ' weight = models.PositiveSmallIntegerField()\n' +
21 | ' base_experience = models.PositiveSmallIntegerField()\n' +
22 | ' order = models.PositiveSmallIntegerField()\n' +
23 | ' is_default = models.BooleanField()\n' +
24 | ' added_at = models.DateTimeField()\n' +
25 | '\n' +
26 | '# viewsets.py\n' +
27 | '# -----------\n' +
28 | 'class PokemonViewSet(viewsets.ModelViewSet):\n' +
29 | ' queryset = Pokemon.objects.all()\n' +
30 | ' serializer_class = PokemonSerializer\n' +
31 | '\n' +
32 | '# serializers.py\n' +
33 | '# --------------\n' +
34 | 'class PokemonSerializer(serializers.HyperlinkedModelSerializer):\n' +
35 | ' specie = SpecieSerializer()\n' +
36 | '\n' +
37 | ' class Meta:\n' +
38 | ' model = Pokemon\n' +
39 | ' exclude = ()';
40 |
41 | const CODE_EXAMPLE_3_2 = '' +
42 | '// Pokemon API\n' +
43 | 'export class Pokemon extends SerializerService {\n' +
44 | ' @Field() url: string;\n' +
45 | ' @Field() specie: Specie;\n' +
46 | ' @Field() identifier: string;\n' +
47 | ' @Field() height: number;\n' +
48 | ' @Field() weight: number;\n' +
49 | ' @Field() base_experience: number;\n' +
50 | ' @Field() order: number;\n' +
51 | ' @Field() is_default: boolean;\n' +
52 | ' @Field() id: number;\n' +
53 | '}\n' +
54 | '\n' +
55 | '@Api(Pokemon)\n' +
56 | '@Injectable({\n' +
57 | ' providedIn: \'root\'\n' +
58 | '})\n' +
59 | 'export class PokemonApi extends ApiService {\n' +
60 | '\n' +
61 | ' url = \'/api/pokemon/\';\n' +
62 | ' serializer = Pokemon;\n' +
63 | '\n' +
64 | ' constructor(injector: Injector) {\n' +
65 | ' super(injector);\n' +
66 | ' }\n' +
67 | '}\n';
68 |
69 | const CODE_EXAMPLE_4 = '' +
70 | 'Django Angular\n' +
71 | '----------------- ----------\n' +
72 | 'PokemonSerializer --> Pokemon\n' +
73 | 'PokemonViewSet --> PokemonApi\n';
74 |
75 | const CODE_EXAMPLE_5 = '' +
76 | 'const pokemons: Page = await PokemonApi.list();\n' +
77 | 'const specie: Specie = pokemons[0].specie;\n' +
78 | '// The methods in the serializer Specie are available\n' +
79 | 'specie.getName()';
80 |
81 | @Component({
82 | selector: 'app-index',
83 | templateUrl: './index.component.html',
84 | styleUrls: ['./index.component.scss']
85 | })
86 | export class IndexComponent implements OnInit {
87 |
88 | CODE_EXAMPLE_1 = CODE_EXAMPLE_1;
89 | CODE_EXAMPLE_2 = CODE_EXAMPLE_2;
90 | CODE_EXAMPLE_3_1 = CODE_EXAMPLE_3_1;
91 | CODE_EXAMPLE_3_2 = CODE_EXAMPLE_3_2;
92 | CODE_EXAMPLE_4 = CODE_EXAMPLE_4;
93 | CODE_EXAMPLE_5 = CODE_EXAMPLE_5;
94 |
95 | constructor() { }
96 |
97 | ngOnInit(): void {
98 | }
99 |
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/index.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { IndexComponent } from './index.component';
4 | import {RouterModule, Routes} from '@angular/router';
5 | import {SharedModule} from '../shared/shared.module';
6 | import {MatButtonModule} from '@angular/material/button';
7 | import {MatFormFieldModule} from '@angular/material/form-field';
8 | import {ReactiveFormsModule} from '@angular/forms';
9 | import { FormComponent } from './form/form.component';
10 | import {ListComponent, MaterialComponentsDialogComponent} from './list/list.component';
11 | import {AngularDjangoMaterialModule} from 'angular-django/material';
12 | import {MatDialogModule} from '@angular/material/dialog';
13 | import {MatTableModule} from '@angular/material/table';
14 | import {MatBadgeModule} from '@angular/material/badge';
15 | import {MatPaginatorModule} from '@angular/material/paginator';
16 | import {AngularDjangoModule} from 'angular-django';
17 | import {MatIconModule} from '@angular/material/icon';
18 | import {MatInputModule} from '@angular/material/input';
19 | import {MatCardModule} from '@angular/material/card';
20 |
21 | const routes: Routes = [
22 | {
23 | path : '',
24 | component: IndexComponent,
25 | },
26 | ];
27 |
28 |
29 | @NgModule({
30 | declarations: [
31 | IndexComponent,
32 | FormComponent,
33 | ListComponent,
34 | MaterialComponentsDialogComponent,
35 | ],
36 | imports: [
37 | CommonModule,
38 | SharedModule,
39 | MatButtonModule,
40 | MatFormFieldModule,
41 | ReactiveFormsModule,
42 | MatDialogModule,
43 | MatCardModule,
44 |
45 | MatTableModule,
46 | MatBadgeModule,
47 | MatPaginatorModule,
48 | MatIconModule,
49 | MatInputModule,
50 |
51 |
52 | RouterModule.forChild(routes),
53 | AngularDjangoModule,
54 | AngularDjangoMaterialModule,
55 |
56 | ]
57 | })
58 | export class IndexModule { }
59 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/list/list.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Search
4 |
5 | search
6 |
7 |
8 |
10 | filter_alt
11 |
12 |
13 |
14 |
18 |
19 |
20 | {{ row.generation.identifier }}
21 |
22 |
23 |
24 |
25 | {{ row|getDisplay:'color'|async }}
26 |
27 |
28 |
29 |
49 |
--------------------------------------------------------------------------------
/demo/angular/src/app/index/list/list.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/index/list/list.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/index/list/list.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ListComponent } from './list.component';
4 |
5 | describe('ListComponent', () => {
6 | let component: ListComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ ListComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ListComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/installation/installation.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/angular/src/app/installation/installation.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/installation/installation.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/installation/installation.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { InstallationComponent } from './installation.component';
4 |
5 | describe('TutorialComponent', () => {
6 | let component: InstallationComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ InstallationComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(InstallationComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/installation/installation.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {GithubFile} from '../github-code/github-code.component';
3 |
4 | @Component({
5 | selector: 'app-tutorial',
6 | templateUrl: './installation.component.html',
7 | styleUrls: ['./installation.component.scss']
8 | })
9 | export class InstallationComponent implements OnInit {
10 |
11 | constructor() { }
12 |
13 | ngOnInit(): void {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/demo/angular/src/app/installation/installation.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { InstallationComponent } from './installation.component';
4 | import {HIGHLIGHT_OPTIONS, HighlightModule} from 'ngx-highlightjs';
5 | import { HighlightPlusModule } from 'ngx-highlightjs/plus';
6 | import {RouterModule, Routes} from '@angular/router';
7 | import {HttpClientModule} from '@angular/common/http';
8 | import {SharedModule} from '../shared/shared.module';
9 |
10 |
11 | const routes: Routes = [
12 | {
13 | path : '',
14 | component: InstallationComponent,
15 | },
16 | ];
17 |
18 |
19 | @NgModule({
20 | declarations: [InstallationComponent],
21 | imports: [
22 | CommonModule,
23 | // HighlightModule,
24 | HighlightPlusModule,
25 | HttpClientModule,
26 | SharedModule,
27 | RouterModule.forChild(routes),
28 | ],
29 | })
30 | export class InstallationModule { }
31 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/all-shape/all-shape.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{ shapeNames.join(", ") }}
3 |
4 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/all-shape/all-shape.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/app/list-api-service/all-shape/all-shape.component.scss
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/all-shape/all-shape.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AllShapeComponent } from './all-shape.component';
4 |
5 | describe('AllShapeComponent', () => {
6 | let component: AllShapeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ AllShapeComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(AllShapeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/all-shape/all-shape.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {Shape, ShapeApi} from '../../shared/api.service';
3 |
4 | @Component({
5 | selector: 'app-all-shape',
6 | templateUrl: './all-shape.component.html',
7 | styleUrls: ['./all-shape.component.scss']
8 | })
9 | export class AllShapeComponent implements OnInit {
10 |
11 | shapeNames: string[] = [];
12 |
13 | constructor(public shapeApi: ShapeApi) { }
14 |
15 | ngOnInit(): void {
16 | this.shapeApi.all().subscribe((shape: Shape) => {
17 | this.shapeNames.push(shape.identifier);
18 | });
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-api-service.component.html:
--------------------------------------------------------------------------------
1 | List api service
2 |
3 | To get a page of objects use the list
method of the Api class. The objects returned by the subscriber
4 | use your model's serializer. This method can be used in conjunction with other methods like filter
,
5 | page
, search
, and more.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Working with the pages
14 |
15 | The list method returns the first page by default. The returned object is of type Page
and has methods to
16 | go to the previous page and to the next page.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Get all items from server
25 |
26 | The all
method returns an observer to get all the items from the server. New pages are only loaded when
27 | requested using the subscriber.
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-api-service.component.scss:
--------------------------------------------------------------------------------
1 | mat-card {
2 | margin-bottom: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-api-service.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ListApiServiceComponent } from './list-api-service.component';
4 |
5 | describe('ListApiServiceComponent', () => {
6 | let component: ListApiServiceComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ ListApiServiceComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ListApiServiceComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-api-service.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {GithubFile} from '../github-code/github-code.component';
3 |
4 | @Component({
5 | selector: 'app-list-api-service',
6 | templateUrl: './list-api-service.component.html',
7 | styleUrls: ['./list-api-service.component.scss']
8 | })
9 | export class ListApiServiceComponent implements OnInit {
10 |
11 | listPokemonFiles: GithubFile[] = [
12 | {name: 'list-pokemon.component.html', directory: 'list-api-service/list-pokemon'},
13 | {name: 'list-pokemon.component.ts', directory: 'list-api-service/list-pokemon'},
14 | {name: 'api.service.ts', directory: 'shared'},
15 | ];
16 |
17 | paginatePokemonFiles: GithubFile[] = [
18 | {name: 'paginate-pokemon.component.html', directory: 'list-api-service/paginate-pokemon'},
19 | {name: 'paginate-pokemon.component.ts', directory: 'list-api-service/paginate-pokemon'},
20 | {name: 'api.service.ts', directory: 'shared'},
21 | ];
22 |
23 | allShapeFiles: GithubFile[] = [
24 | {name: 'all-shape.component.html', directory: 'list-api-service/all-shape'},
25 | {name: 'all-shape.component.ts', directory: 'list-api-service/all-shape'},
26 | {name: 'api.service.ts', directory: 'shared'},
27 | ];
28 |
29 | constructor() {
30 | }
31 |
32 | ngOnInit(): void {
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-api-service.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ListApiServiceComponent } from './list-api-service.component';
4 | import {RouterModule, Routes} from '@angular/router';
5 | import {SharedModule} from '../shared/shared.module';
6 | import {AngularDjangoModule} from 'angular-django';
7 | import {HttpClientModule} from '@angular/common/http';
8 | import {FormsModule} from '@angular/forms';
9 | import { ListPokemonComponent } from './list-pokemon/list-pokemon.component';
10 | import {MatCardModule} from '@angular/material/card';
11 | import { PaginatePokemonComponent } from './paginate-pokemon/paginate-pokemon.component';
12 | import { AllShapeComponent } from './all-shape/all-shape.component';
13 |
14 |
15 | const routes: Routes = [
16 | {
17 | path : '',
18 | component: ListApiServiceComponent,
19 | },
20 | ];
21 |
22 |
23 | @NgModule({
24 | declarations: [
25 | ListApiServiceComponent,
26 | ListPokemonComponent,
27 | PaginatePokemonComponent,
28 | AllShapeComponent
29 | ],
30 | imports: [
31 | CommonModule,
32 | MatCardModule,
33 | SharedModule,
34 | HttpClientModule,
35 | AngularDjangoModule,
36 | RouterModule.forChild(routes),
37 | FormsModule,
38 | ]
39 | })
40 | export class ListApiServiceModule { }
41 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-pokemon/list-pokemon.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Page {{item}}
4 |
5 |
6 | Search:
7 |
8 |
9 | {{ specie.identifier }}
10 |
11 |
Count: {{ count }}
12 |
13 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-pokemon/list-pokemon.component.scss:
--------------------------------------------------------------------------------
1 | hr {
2 | margin-top: 10px;
3 | margin-bottom: 10px;
4 | }
5 |
6 | label {
7 | margin-right: 10px;
8 | }
9 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-pokemon/list-pokemon.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ListPokemonComponent } from './list-pokemon.component';
4 |
5 | describe('ListPokemonComponent', () => {
6 | let component: ListPokemonComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ ListPokemonComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ListPokemonComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/list-pokemon/list-pokemon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {SpecieApi} from '../../shared/api.service';
3 | import {map} from 'rxjs/operators';
4 |
5 | @Component({
6 | selector: 'app-list-pokemon',
7 | templateUrl: './list-pokemon.component.html',
8 | styleUrls: ['./list-pokemon.component.scss']
9 | })
10 | export class ListPokemonComponent implements OnInit {
11 |
12 | species: any;
13 | page = 1;
14 | search = '';
15 | count: number;
16 | pagesCount: number;
17 |
18 | constructor(public specieApi: SpecieApi) {
19 | }
20 |
21 | get pagesArray(): number[] {
22 | return Array.from(Array(this.pagesCount).keys()).map((i) => i + 1);
23 | }
24 |
25 | setSpecies(resetPage: boolean = false): void {
26 | if (resetPage) {
27 | this.page = 1;
28 | }
29 | this.species = this.specieApi.page(this.page).search(this.search).list().pipe(map((items: any) => {
30 | this.count = items.count;
31 | this.pagesCount = items.pagesCount;
32 | return items;
33 | }));
34 | }
35 |
36 | ngOnInit(): void {
37 | this.setSpecies();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/paginate-pokemon/paginate-pokemon.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ specie.identifier }}
4 |
5 |
6 | Previous
8 | Page {{ page.currentPage }}
9 | Next
11 |
12 |
13 | Loading species...
14 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/paginate-pokemon/paginate-pokemon.component.scss:
--------------------------------------------------------------------------------
1 | .change-page > button, span {
2 | margin-right: 10px;
3 | }
4 |
5 | .loading {
6 | height: 250px;
7 | }
8 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/paginate-pokemon/paginate-pokemon.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { PaginatePokemonComponent } from './paginate-pokemon.component';
4 |
5 | describe('PaginatePokemonComponent', () => {
6 | let component: PaginatePokemonComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ PaginatePokemonComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(PaginatePokemonComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/list-api-service/paginate-pokemon/paginate-pokemon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {Specie, SpecieApi} from '../../shared/api.service';
3 | import {Page} from 'angular-django';
4 | import {Observable} from 'rxjs';
5 |
6 | @Component({
7 | selector: 'app-paginate-pokemon',
8 | templateUrl: './paginate-pokemon.component.html',
9 | styleUrls: ['./paginate-pokemon.component.scss'],
10 | })
11 | export class PaginatePokemonComponent implements OnInit {
12 |
13 | speciePage$: Observable>;
14 |
15 | constructor(public specieApi: SpecieApi) { }
16 |
17 | ngOnInit(): void {
18 | this.speciePage$ = this.specieApi.list();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/demo/angular/src/app/material-components/material-components.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Search
4 |
5 | search
6 |
7 |
8 |
10 | filter_alt
11 |
12 |
13 |
14 |
18 |
19 |
20 | {{ row.generation.identifier }}
21 |
22 |
23 |
24 |
25 | {{ row|getDisplay:'color'|async }}
26 |
27 |
28 |
29 |
49 |
--------------------------------------------------------------------------------
/demo/angular/src/app/material-components/material-components.component.scss:
--------------------------------------------------------------------------------
1 | .paginator {
2 | display: flex;
3 | flex-direction: row;
4 | background: #fff;
5 | align-items: baseline;
6 | justify-content: space-between;
7 | padding-left: 24px;
8 |
9 | .selected {
10 | color: rgba(0,0,0,.54);
11 | font-size: 12px;
12 |
13 | a {
14 | padding-left: 5px;
15 | }
16 | }
17 | }
18 |
19 |
20 | :host ::ng-deep {
21 | .filter-button {
22 | .mat-badge-content {
23 | top: -5px;
24 | right: -5px;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/demo/angular/src/app/material-components/material-components.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MaterialComponentsComponent } from './material-components.component';
4 |
5 | describe('MaterialComponentsComponent', () => {
6 | let component: MaterialComponentsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ MaterialComponentsComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MaterialComponentsComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/material-components/material-components.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { TableModule } from './table/table.module';
4 | import {AngularDjangoMaterialModule} from 'angular-django/material';
5 | import {RouterModule, Routes} from '@angular/router';
6 | import {MaterialComponentsComponent, MaterialComponentsDialogComponent} from './material-components.component';
7 | import {AngularDjangoModule} from 'angular-django';
8 | import {MatFormFieldModule} from '@angular/material/form-field';
9 | import {MatIconModule} from '@angular/material/icon';
10 | import {MatInputModule} from '@angular/material/input';
11 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
12 | import {MatPaginatorModule} from '@angular/material/paginator';
13 | import {FormlyModule} from '@ngx-formly/core';
14 | import {MatButtonModule} from '@angular/material/button';
15 | import {FormlyMaterialModule} from '@ngx-formly/material';
16 | import {MatDialogModule} from '@angular/material/dialog';
17 | import {MatBadgeModule} from '@angular/material/badge';
18 | import {AutocompleteTypeComponent} from '../shared/autocomplete-type.component';
19 |
20 |
21 | const routes: Routes = [
22 | {
23 | path : '',
24 | component: MaterialComponentsComponent,
25 | },
26 | ];
27 |
28 |
29 | @NgModule({
30 | declarations: [
31 | MaterialComponentsComponent,
32 | MaterialComponentsDialogComponent,
33 | ],
34 | imports: [
35 | CommonModule,
36 | TableModule,
37 | MatFormFieldModule,
38 | MatDialogModule,
39 | MatIconModule,
40 | MatInputModule,
41 | MatBadgeModule,
42 | MatPaginatorModule,
43 | MatButtonModule,
44 | FormlyModule.forChild({
45 | types: [
46 | {name: 'autocomplete', component: AutocompleteTypeComponent, wrappers: ['form-field']},
47 | ],
48 | }),
49 | FormlyMaterialModule,
50 | ReactiveFormsModule,
51 | FormsModule,
52 | AngularDjangoMaterialModule,
53 | RouterModule.forChild(routes),
54 | AngularDjangoModule,
55 | ]
56 | })
57 | export class MaterialComponentsModule { }
58 |
--------------------------------------------------------------------------------
/demo/angular/src/app/material-components/table/table.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 |
5 |
6 | @NgModule({
7 | declarations: [],
8 | imports: [
9 | CommonModule
10 | ]
11 | })
12 | export class TableModule { }
13 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-api-service.component.html:
--------------------------------------------------------------------------------
1 | Retrieve api service
2 |
3 | To get an object by its identifier use the retrieve
method of the Api class. The object returned by the
4 | subscriber is of the serializer type of your model.
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Nested serializers
14 |
15 | Nested objects are also cast to the serializer serializer type. In this example the Pokemon object has a
16 | Specie object and its methods and api are available.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-api-service.component.scss:
--------------------------------------------------------------------------------
1 | mat-card {
2 | margin-bottom: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-api-service.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RetrieveApiServiceComponent } from './retrieve-api-service.component';
4 |
5 | describe('RetrieveApiServiceComponent', () => {
6 | let component: RetrieveApiServiceComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ RetrieveApiServiceComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RetrieveApiServiceComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-api-service.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {User, UserApi} from '../shared/api.service';
3 | import {Observable} from 'rxjs';
4 | import {GithubFile} from '../github-code/github-code.component';
5 |
6 |
7 | @Component({
8 | selector: 'app-retrieve-api-service',
9 | templateUrl: './retrieve-api-service.component.html',
10 | styleUrls: ['./retrieve-api-service.component.scss']
11 | })
12 | export class RetrieveApiServiceComponent implements OnInit {
13 |
14 | retrieveUserFiles: GithubFile[] = [
15 | {name: 'retrieve-user.component.html', directory: 'retrieve-api-service/retrieve-user'},
16 | {name: 'retrieve-user.component.ts', directory: 'retrieve-api-service/retrieve-user'},
17 | {name: 'api.service.ts', directory: 'shared'},
18 | ];
19 |
20 | retrievePokemonFiles: GithubFile[] = [
21 | {name: 'retrieve-pokemon.component.html', directory: 'retrieve-api-service/retrieve-pokemon'},
22 | {name: 'retrieve-pokemon.component.ts', directory: 'retrieve-api-service/retrieve-pokemon'},
23 | {name: 'api.service.ts', directory: 'shared'},
24 | ];
25 |
26 | constructor() { }
27 |
28 | ngOnInit(): void {
29 | }
30 |
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-api-service.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { RetrieveApiServiceComponent } from './retrieve-api-service.component';
4 | import {SharedModule} from '../shared/shared.module';
5 | import {RouterModule, Routes} from '@angular/router';
6 | import {AngularDjangoModule} from 'angular-django';
7 | import { RetrieveUserComponent } from './retrieve-user/retrieve-user.component';
8 | import {MatCardModule} from '@angular/material/card';
9 | import { RetrievePokemonComponent } from './retrieve-pokemon/retrieve-pokemon.component';
10 |
11 |
12 | const routes: Routes = [
13 | {
14 | path : '',
15 | component: RetrieveApiServiceComponent,
16 | },
17 | ];
18 |
19 |
20 | @NgModule({
21 | declarations: [RetrieveApiServiceComponent, RetrieveUserComponent, RetrievePokemonComponent],
22 | imports: [
23 | CommonModule,
24 | SharedModule,
25 | MatCardModule,
26 | AngularDjangoModule,
27 | RouterModule.forChild(routes),
28 | ]
29 | })
30 | export class RetrieveApiServiceModule { }
31 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-pokemon/retrieve-pokemon.component.html:
--------------------------------------------------------------------------------
1 |
2 | Identifier:
3 | {{ pokemon.identifier }}
4 | Number:
5 | #{{ pokemon.id }}
6 | Habitat:
7 | {{ pokemon.specie.habitat.identifier }}
8 | Color:
9 |
10 | {{ pokemon.specie | getDisplay:'color' | async }}
11 |
12 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-pokemon/retrieve-pokemon.component.scss:
--------------------------------------------------------------------------------
1 | dd, dt {
2 | display: inline-block;
3 | }
4 |
5 | dt {
6 | font-weight: bold;
7 | width: 15%;
8 | }
9 |
10 | dd {
11 | width: 75%;
12 | margin-inline-start: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-pokemon/retrieve-pokemon.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RetrievePokemonComponent } from './retrieve-pokemon.component';
4 |
5 | describe('RetrievePokemonComponent', () => {
6 | let component: RetrievePokemonComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ RetrievePokemonComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RetrievePokemonComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-pokemon/retrieve-pokemon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {Observable} from 'rxjs';
3 | import {Pokemon, PokemonApi} from '../../shared/api.service';
4 |
5 | @Component({
6 | selector: 'app-retrieve-pokemon',
7 | templateUrl: './retrieve-pokemon.component.html',
8 | styleUrls: ['./retrieve-pokemon.component.scss']
9 | })
10 | export class RetrievePokemonComponent implements OnInit {
11 |
12 | pokemon$: Observable;
13 |
14 | constructor(public apiPokemon: PokemonApi) { }
15 |
16 | ngOnInit(): void {
17 | this.pokemon$ = this.apiPokemon.retrieve(1);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-user/retrieve-user.component.html:
--------------------------------------------------------------------------------
1 |
2 | User:
3 | {{ user.getName() }}
4 | Joined at:
5 | {{ user.date_joined.toLocaleString() }}
6 | Email:
7 | {{ user.email }}
8 | Is active:
9 | {{ user.is_active }}
10 |
11 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-user/retrieve-user.component.scss:
--------------------------------------------------------------------------------
1 | dd, dt {
2 | display: inline-block;
3 | }
4 |
5 | dt {
6 | font-weight: bold;
7 | width: 15%;
8 | }
9 |
10 | dd {
11 | width: 75%;
12 | margin-inline-start: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-user/retrieve-user.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { RetrieveUserComponent } from './retrieve-user.component';
4 |
5 | describe('RetrieveUserComponent', () => {
6 | let component: RetrieveUserComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ RetrieveUserComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RetrieveUserComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/demo/angular/src/app/retrieve-api-service/retrieve-user/retrieve-user.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import {Observable} from 'rxjs';
3 | import {User, UserApi} from '../../shared/api.service';
4 |
5 | @Component({
6 | selector: 'app-retrieve-user',
7 | templateUrl: './retrieve-user.component.html',
8 | styleUrls: ['./retrieve-user.component.scss']
9 | })
10 | export class RetrieveUserComponent implements OnInit {
11 |
12 | user$: Observable;
13 |
14 | constructor(public apiUser: UserApi) { }
15 |
16 | ngOnInit(): void {
17 | this.user$ = this.apiUser.retrieve(1);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/demo/angular/src/app/shared/api.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { ApiService } from './api.service';
4 |
5 | describe('ApiService', () => {
6 | let service: ApiService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(ApiService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/demo/angular/src/app/shared/autocomplete-type.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewChild, OnInit, AfterViewInit, ElementRef} from '@angular/core';
2 | import { FieldType } from '@ngx-formly/material';
3 | import { MatInput } from '@angular/material/input';
4 | import {MatAutocompleteSelectedEvent, MatAutocompleteTrigger} from '@angular/material/autocomplete';
5 | import { Observable } from 'rxjs';
6 | import { startWith, switchMap } from 'rxjs/operators';
7 | import {FormControl} from '@angular/forms';
8 |
9 | @Component({
10 | selector: 'formly-autocomplete-type',
11 | template: `
12 |
19 |
21 |
22 | {{ value.getName() }}
23 |
24 |
25 | `,
26 | })
27 | export class AutocompleteTypeComponent extends FieldType implements OnInit, AfterViewInit {
28 | @ViewChild(MatInput) formFieldControl: MatInput;
29 | @ViewChild('inputElement') inputElement: ElementRef;
30 | @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger;
31 |
32 | formControl: FormControl;
33 | filter: Observable;
34 |
35 | ngOnInit() {
36 | super.ngOnInit();
37 | this.filter = this.formControl.valueChanges
38 | .pipe(
39 | startWith(''),
40 | switchMap(term => this.to.filter(term)),
41 | );
42 | }
43 |
44 | displayName(option: { getName: () => string }): string {
45 | if (!option) {
46 | return '';
47 | }
48 | return option.getName();
49 | }
50 |
51 | closed(): void {
52 | if (typeof this.formControl.value === 'string') {
53 | // Reset invalid choice
54 | this.formControl.setValue(null);
55 | }
56 | }
57 |
58 | blur(): void {
59 | setTimeout(() => {
60 | // there is a delay between blurring and selecting the element
61 | this.closed();
62 | }, 100);
63 | }
64 |
65 | focusOut(): void {
66 | this.inputElement.nativeElement.blur();
67 | }
68 |
69 | ngAfterViewInit() {
70 | super.ngAfterViewInit();
71 | // temporary fix for https://github.com/angular/material2/issues/6728
72 | (this.autocomplete as any)._formField = this.formField;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/demo/angular/src/app/shared/interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import {
3 | HttpInterceptor,
4 | HttpRequest,
5 | HttpErrorResponse,
6 | HttpHandler,
7 | HttpEvent,
8 | } from '@angular/common/http';
9 |
10 | import {Observable, EMPTY, throwError} from 'rxjs';
11 | import { catchError } from 'rxjs/operators';
12 | import {MatSnackBar} from '@angular/material/snack-bar';
13 |
14 |
15 | @Injectable()
16 | export class HttpErrorInterceptor implements HttpInterceptor {
17 | durationInSeconds = 5;
18 |
19 | constructor(private _snackBar: MatSnackBar) { }
20 |
21 | intercept(request: HttpRequest, next: HttpHandler): Observable {
22 |
23 | return next.handle(request).pipe(
24 | catchError((response: HttpErrorResponse) => {
25 | if (response.error instanceof Error) {
26 | // A client-side or network error occurred. Handle it accordingly.
27 | this.logError(`An error occurred: ${response.error.message}`);
28 | } else {
29 | // The backend returned an unsuccessful response code.
30 | // The response body may contain clues as to what went wrong,
31 | this.logError(`Backend returned code ${response.status}, body was: ${response.error}`);
32 | }
33 |
34 | return throwError(response);
35 | })
36 | );
37 | }
38 |
39 | logError(message): void {
40 | console.error(message);
41 | this.openSnackBar(message, 'Dismiss');
42 | }
43 |
44 | openSnackBar(message, action): void {
45 | this._snackBar.open(message, action, {
46 | duration: this.durationInSeconds * 1000,
47 | });
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/demo/angular/src/app/shared/shared.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
4 | import {
5 | GenerationApi,
6 | GrowthRateApi,
7 | HabitatApi,
8 | PokemonApi,
9 | RegionApi,
10 | ShapeApi,
11 | SpecieApi,
12 | UserApi
13 | } from './api.service';
14 | import {GithubCodeModule} from '../github-code/github-code.module';
15 | import {CodeHighlightModule} from '../code-highlight/code-highlight.module';
16 | import {HttpErrorInterceptor} from './interceptor';
17 | import {MatSnackBar, MatSnackBarModule} from '@angular/material/snack-bar';
18 | import {FormlyModule} from '@ngx-formly/core';
19 | import {AutocompleteTypeComponent} from './autocomplete-type.component';
20 | import {MatAutocompleteModule} from '@angular/material/autocomplete';
21 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
22 | import {MatInputModule} from '@angular/material/input';
23 | import {ExternalHtmlModule} from '../external-html/external-html.module';
24 |
25 |
26 |
27 | @NgModule({
28 | declarations: [
29 | AutocompleteTypeComponent
30 | ],
31 | providers: [
32 | RegionApi,
33 | GenerationApi,
34 | HabitatApi,
35 | ShapeApi,
36 | GrowthRateApi,
37 | SpecieApi,
38 | PokemonApi,
39 | UserApi,
40 | {
41 | provide: HTTP_INTERCEPTORS,
42 | useClass: HttpErrorInterceptor,
43 | multi: true,
44 | deps: [MatSnackBar]
45 | }
46 | ],
47 | imports: [
48 | FormsModule,
49 | FormlyModule.forChild({
50 | types: [
51 | {name: 'autocomplete', component: AutocompleteTypeComponent, wrappers: ['form-field']},
52 | ],
53 | }),
54 | ReactiveFormsModule,
55 | FormlyModule,
56 | CommonModule,
57 | HttpClientModule,
58 | GithubCodeModule,
59 | ExternalHtmlModule,
60 | CodeHighlightModule,
61 | MatInputModule,
62 | MatAutocompleteModule,
63 | MatSnackBarModule,
64 | ],
65 | exports: [
66 | FormlyModule,
67 | FormsModule,
68 | ReactiveFormsModule,
69 | MatInputModule,
70 | AutocompleteTypeComponent,
71 | HttpClientModule,
72 | GithubCodeModule,
73 | ExternalHtmlModule,
74 | CodeHighlightModule,
75 | ]
76 | })
77 | export class SharedModule { }
78 |
--------------------------------------------------------------------------------
/demo/angular/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/assets/.gitkeep
--------------------------------------------------------------------------------
/demo/angular/src/assets/angular_django.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
36 |
43 |
45 |
49 |
53 |
54 |
65 |
66 |
--------------------------------------------------------------------------------
/demo/angular/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/demo/angular/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/demo/angular/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/angular/src/favicon.ico
--------------------------------------------------------------------------------
/demo/angular/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AngularDemo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/demo/angular/src/jekyll-github.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * GitHub style for Pygments syntax highlighter, for use with Jekyll
3 | * Courtesy of GitHub.com
4 | */
5 |
6 | app-external-html {
7 | .highlight pre, pre, .highlight .hll { background-color: #f8f8f8; border: 1px solid #ccc; padding: 6px 10px; border-radius: 3px; }
8 | .highlight .c { color: #999988; font-style: italic; }
9 | .highlight .err { color: #a61717; background-color: #e3d2d2; }
10 | .highlight .k { font-weight: bold; }
11 | .highlight .o { font-weight: bold; }
12 | .highlight .cm { color: #999988; font-style: italic; }
13 | .highlight .cp { color: #999999; font-weight: bold; }
14 | .highlight .c1 { color: #999988; font-style: italic; }
15 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic; }
16 | .highlight .gd { color: #000000; background-color: #ffdddd; }
17 | .highlight .gd .x { color: #000000; background-color: #ffaaaa; }
18 | .highlight .ge { font-style: italic; }
19 | .highlight .gr { color: #aa0000; }
20 | .highlight .gh { color: #999999; }
21 | .highlight .gi { color: #000000; background-color: #ddffdd; }
22 | .highlight .gi .x { color: #000000; background-color: #aaffaa; }
23 | .highlight .go { color: #888888; }
24 | .highlight .gp { color: #555555; }
25 | .highlight .gs { font-weight: bold; }
26 | .highlight .gu { color: #800080; font-weight: bold; }
27 | .highlight .gt { color: #aa0000; }
28 | .highlight .kc { font-weight: bold; }
29 | .highlight .kd { font-weight: bold; }
30 | .highlight .kn { font-weight: bold; }
31 | .highlight .kp { font-weight: bold; }
32 | .highlight .kr { font-weight: bold; }
33 | .highlight .kt { color: #445588; font-weight: bold; }
34 | .highlight .m { color: #009999; }
35 | .highlight .s { color: #dd1144; }
36 | .highlight .n { color: #333333; }
37 | .highlight .na { color: teal; }
38 | .highlight .nb { color: #0086b3; }
39 | .highlight .nc { color: #445588; font-weight: bold; }
40 | .highlight .no { color: teal; }
41 | .highlight .ni { color: purple; }
42 | .highlight .ne { color: #990000; font-weight: bold; }
43 | .highlight .nf { color: #990000; font-weight: bold; }
44 | .highlight .nn { color: #555555; }
45 | .highlight .nt { color: navy; }
46 | .highlight .nv { color: teal; }
47 | .highlight .ow { font-weight: bold; }
48 | .highlight .w { color: #bbbbbb; }
49 | .highlight .mf { color: #009999; }
50 | .highlight .mh { color: #009999; }
51 | .highlight .mi { color: #009999; }
52 | .highlight .mo { color: #009999; }
53 | .highlight .sb { color: #dd1144; }
54 | .highlight .sc { color: #dd1144; }
55 | .highlight .sd { color: #dd1144; }
56 | .highlight .s2 { color: #dd1144; }
57 | .highlight .se { color: #dd1144; }
58 | .highlight .sh { color: #dd1144; }
59 | .highlight .si { color: #dd1144; }
60 | .highlight .sx { color: #dd1144; }
61 | .highlight .sr { color: #009926; }
62 | .highlight .s1 { color: #dd1144; }
63 | .highlight .ss { color: #990073; }
64 | .highlight .bp { color: #999999; }
65 | .highlight .vc { color: teal; }
66 | .highlight .vg { color: teal; }
67 | .highlight .vi { color: teal; }
68 | .highlight .il { color: #009999; }
69 | .highlight .gc { color: #999; background-color: #EAF2F5; }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/demo/angular/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/demo/angular/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | /***************************************************************************************************
56 | * Zone JS is required by default for Angular itself.
57 | */
58 | import 'zone.js/dist/zone'; // Included with Angular CLI.
59 |
60 |
61 | /***************************************************************************************************
62 | * APPLICATION IMPORTS
63 | */
64 |
--------------------------------------------------------------------------------
/demo/angular/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | html, body { height: 100%; }
4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
5 |
6 | @import "jekyll-github";
7 |
--------------------------------------------------------------------------------
/demo/angular/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting()
21 | );
22 | // Then we find all the tests.
23 | const context = require.context('./', true, /\.spec\.ts$/);
24 | // And load the modules.
25 | context.keys().map(context);
26 |
--------------------------------------------------------------------------------
/demo/angular/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/demo/angular/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "downlevelIteration": true,
10 | "experimentalDecorators": true,
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "emitDecoratorMetadata": true,
15 | "module": "es2020",
16 | "lib": [
17 | "es2018",
18 | "dom"
19 | ]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/demo/angular/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/demo/django/demo/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/django/demo/__init__.py
--------------------------------------------------------------------------------
/demo/django/demo/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for demo project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/demo/django/demo/urls.py:
--------------------------------------------------------------------------------
1 | """demo URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/3.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import path, include
18 |
19 | urlpatterns = [
20 | path('admin/', admin.site.urls),
21 | path(r'', include('pokedex.urls')),
22 | ]
23 |
--------------------------------------------------------------------------------
/demo/django/demo/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for demo project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/demo/django/dev-requirements.in:
--------------------------------------------------------------------------------
1 | requests
2 | djangorestframework
3 | drf-writable-nested
4 | Django
5 | django-filter
6 |
--------------------------------------------------------------------------------
/demo/django/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile dev-requirements.in
6 | #
7 | asgiref==3.2.10 # via django
8 | certifi==2020.6.20 # via requests
9 | chardet==3.0.4 # via requests
10 | django-filter==2.3.0 # via -r dev-requirements.in
11 | django==3.1.1 # via -r dev-requirements.in, django-filter, djangorestframework
12 | djangorestframework==3.11.1 # via -r dev-requirements.in
13 | drf-writable-nested==0.6.1 # via -r dev-requirements.in
14 | idna==2.10 # via requests
15 | pytz==2020.1 # via django
16 | requests==2.24.0 # via -r dev-requirements.in
17 | sqlparse==0.3.1 # via django
18 | urllib3==1.25.10 # via requests
19 |
--------------------------------------------------------------------------------
/demo/django/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/demo/django/pokedex/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/django/pokedex/__init__.py
--------------------------------------------------------------------------------
/demo/django/pokedex/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 | from pokedex.models import Specie
5 |
6 |
7 | @admin.register(Specie)
8 | class SpecieAdmin(admin.ModelAdmin):
9 | pass
10 |
--------------------------------------------------------------------------------
/demo/django/pokedex/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class PokedexConfig(AppConfig):
5 | name = 'pokedex'
6 |
--------------------------------------------------------------------------------
/demo/django/pokedex/filters.py:
--------------------------------------------------------------------------------
1 | import django_filters
2 |
3 | from pokedex.models import Pokemon, Specie
4 |
5 |
6 | class PokemonFilter(django_filters.FilterSet):
7 | specie__identifier = django_filters.CharFilter(lookup_expr='icontains')
8 |
9 |
10 | class Meta:
11 | model = Pokemon
12 | fields = {
13 | 'id': ['exact'],
14 | 'identifier': ['exact'],
15 | 'specie__identifier': ['exact'],
16 | 'specie__generation': ['exact'],
17 | 'specie': ['exact'],
18 | 'height': ['exact'],
19 | 'weight': ['exact'],
20 | 'base_experience': ['exact'],
21 | 'is_default': ['exact']
22 | }
23 |
24 |
25 | class SpecieFilter(django_filters.FilterSet):
26 |
27 |
28 | class Meta:
29 | model = Specie
30 | fields = {
31 | 'identifier': ['exact'],
32 | 'generation': ['exact'],
33 | 'evolves_from_specie': ['exact'],
34 | 'color': ['exact'],
35 | 'shape': ['exact'],
36 | 'habitat': ['exact'],
37 | 'gender_rate': ['exact'],
38 | 'capture_rate': ['exact'],
39 | 'base_happiness': ['exact'],
40 | 'is_baby': ['exact'],
41 | 'hatch_counter': ['exact'],
42 | 'has_gender_differences': ['exact'],
43 | 'growth_rate': ['exact'],
44 | 'forms_switchable': ['exact'],
45 | 'conquest_order': ['exact']
46 | }
47 |
--------------------------------------------------------------------------------
/demo/django/pokedex/import_pokedex.py:
--------------------------------------------------------------------------------
1 | import csv
2 |
3 | import requests
4 | from django.db.transaction import atomic
5 | from django.db.models import fields
6 | from django.db.models.fields import related
7 |
8 | from . import models
9 |
10 |
11 | URL = 'https://raw.githubusercontent.com/veekun/pokedex/master/pokedex/data/csv/{file}.csv'
12 | FIELD_VALUES = {
13 | fields.BooleanField: lambda x: {'1': True, '0': False}[x],
14 | fields.IntegerField: lambda x: int(x) if x else None,
15 | fields.PositiveSmallIntegerField: lambda x: int(x) if x else None,
16 | fields.SmallIntegerField: lambda x: int(x) if x else None,
17 | fields.PositiveIntegerField: lambda x: int(x) if x else None,
18 | related.ForeignKey: lambda x: int(x) if x else None,
19 | None: lambda x: x,
20 | }
21 | FIELD_KEYS = {
22 | related.ForeignKey: lambda x: '{}_id'.format(x.name),
23 | None: lambda x: x.name,
24 | }
25 |
26 |
27 | def retrieve_data(file):
28 | r = requests.get(URL.format(file=file))
29 | return csv.DictReader(r.text.split('\n'), delimiter=',')
30 |
31 |
32 | def get_key(field, import_opts):
33 | fields_map = getattr(import_opts, 'fields_map', {})
34 | if field.name in fields_map:
35 | return fields_map[field.name]
36 | return FIELD_KEYS.get(field.__class__, FIELD_KEYS[None])(field)
37 |
38 |
39 | def get_value(field):
40 | return FIELD_VALUES.get(field.__class__, FIELD_VALUES[None])
41 |
42 |
43 | def get_fields(model, import_opts):
44 | fields = model._meta.get_fields()
45 | return {get_key(field, import_opts): (field, get_value(field)) for field in fields}
46 |
47 |
48 | def import_model(model):
49 | import_opts = model.Import
50 | data = retrieve_data(import_opts.file)
51 | fields = get_fields(model, import_opts)
52 | for row in data:
53 | # get_key(model_field[key], import_opts)
54 | row = {'{}{}'.format(fields[key][0].name, '_id' if isinstance(fields[key][0], related.ForeignKey) else ''):
55 | fields[key][1](value) for key, value in row.items() if key in
56 | fields}
57 | # print(row)
58 | model.objects.get_or_create(defaults=row, id=row['id'])
59 |
60 |
61 | def import_all():
62 | with atomic():
63 | for model in filter(lambda x: hasattr(x, 'Import'), vars(models).values()):
64 | import_model(model)
65 |
--------------------------------------------------------------------------------
/demo/django/pokedex/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/django/pokedex/management/__init__.py
--------------------------------------------------------------------------------
/demo/django/pokedex/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/django/pokedex/management/commands/__init__.py
--------------------------------------------------------------------------------
/demo/django/pokedex/management/commands/import_pokedex.py:
--------------------------------------------------------------------------------
1 | """Import Pokedex data.
2 | """
3 | from django.core.management.base import BaseCommand, CommandError
4 | from pokedex.import_pokedex import import_all
5 |
6 | class Command(BaseCommand):
7 | help = globals()['__doc__']
8 |
9 | def handle(self, *args, **options):
10 | import_all()
11 |
--------------------------------------------------------------------------------
/demo/django/pokedex/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/demo/django/pokedex/migrations/__init__.py
--------------------------------------------------------------------------------
/demo/django/pokedex/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils.translation import ugettext_lazy as _
3 |
4 | # Create your models here.
5 |
6 |
7 | # class Color(models.Model):
8 | # identifier = models.CharField(max_length=20)
9 | #
10 | # class Import:
11 | # file = 'pokemon_colors'
12 |
13 |
14 | COLORS = [
15 | ('1', 'black'),
16 | ('2', 'blue'),
17 | ('3', 'brown'),
18 | ('4', 'gray'),
19 | ('5', 'green'),
20 | ('6', 'pink'),
21 | ('7', 'purple'),
22 | ('8', 'red'),
23 | ('9', 'white'),
24 | ('10', 'yellow'),
25 |
26 | ]
27 |
28 |
29 | class Shape(models.Model):
30 | identifier = models.CharField(max_length=20)
31 |
32 | class Import:
33 | file = 'pokemon_shapes'
34 |
35 |
36 | class Habitat(models.Model):
37 | identifier = models.CharField(max_length=20)
38 |
39 | class Import:
40 | file = 'pokemon_habitats'
41 |
42 |
43 | class GrowthRate(models.Model):
44 | identifier = models.CharField(max_length=20)
45 | formula = models.TextField()
46 |
47 | class Import:
48 | file = 'growth_rates'
49 |
50 |
51 | class Region(models.Model):
52 | identifier = models.CharField(max_length=30)
53 |
54 | class Import:
55 | file = 'regions'
56 |
57 |
58 | class Generation(models.Model):
59 | main_region = models.ForeignKey(Region, on_delete=models.PROTECT)
60 | identifier = models.CharField(max_length=30)
61 |
62 | class Import:
63 | file = 'generations'
64 |
65 |
66 | class Specie(models.Model):
67 | identifier = models.CharField(_('Specie identifier'), max_length=50)
68 | generation = models.ForeignKey(Generation, on_delete=models.PROTECT)
69 | evolves_from_specie = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True)
70 | # evolution_chain =
71 | color = models.CharField(max_length=8, choices=COLORS)
72 | shape = models.ForeignKey(Shape, on_delete=models.PROTECT, blank=True, null=True)
73 | habitat = models.ForeignKey(Habitat, on_delete=models.PROTECT, blank=True, null=True)
74 | gender_rate = models.SmallIntegerField()
75 | capture_rate = models.PositiveSmallIntegerField()
76 | base_happiness = models.PositiveSmallIntegerField()
77 | is_baby = models.BooleanField()
78 | hatch_counter = models.PositiveSmallIntegerField()
79 | has_gender_differences = models.BooleanField()
80 | growth_rate = models.ForeignKey(GrowthRate, on_delete=models.PROTECT)
81 | forms_switchable = models.BooleanField()
82 | order = models.PositiveSmallIntegerField()
83 | conquest_order = models.PositiveSmallIntegerField(null=True, blank=True)
84 |
85 | class Import:
86 | file = 'pokemon_species'
87 | fields_map = {'color': 'color_id'}
88 |
89 |
90 | class Pokemon(models.Model):
91 | identifier = models.CharField(max_length=50, help_text=_('Pokemon name'))
92 | specie = models.ForeignKey(Specie, on_delete=models.CASCADE)
93 | height = models.PositiveSmallIntegerField()
94 | weight = models.PositiveSmallIntegerField()
95 | base_experience = models.PositiveSmallIntegerField()
96 | order = models.PositiveSmallIntegerField()
97 | is_default = models.BooleanField()
98 |
99 | class Import:
100 | file = 'pokemon'
101 | fields_map = {'specie': 'species_id'}
102 |
--------------------------------------------------------------------------------
/demo/django/pokedex/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import get_user_model
2 | from drf_writable_nested import NestedUpdateMixin, NestedCreateMixin
3 | from rest_framework import serializers
4 |
5 | from pokedex.models import Pokemon, Specie, Generation, Habitat, Shape, GrowthRate, Region
6 |
7 |
8 | class DemoSerializerMixin(object):
9 | def get_field_names(self, declared_fields, info):
10 | expanded_fields = super(DemoSerializerMixin, self).get_field_names(declared_fields, info)
11 |
12 | if getattr(self.Meta, 'extra_fields', None):
13 | fields = expanded_fields + self.Meta.extra_fields
14 | else:
15 | fields = expanded_fields
16 | if 'id' not in fields:
17 | fields += ['id']
18 | return fields
19 |
20 |
21 | class RegionSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
22 | serializers.HyperlinkedModelSerializer):
23 | class Meta:
24 | model = Region
25 | exclude = ()
26 |
27 |
28 | class GenerationSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
29 | serializers.HyperlinkedModelSerializer):
30 | class Meta:
31 | model = Generation
32 | exclude = ()
33 |
34 |
35 | class HabitatSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
36 | serializers.HyperlinkedModelSerializer):
37 | class Meta:
38 | model = Habitat
39 | exclude = ()
40 |
41 |
42 | class ShapeSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
43 | serializers.HyperlinkedModelSerializer):
44 | class Meta:
45 | model = Shape
46 | exclude = ()
47 |
48 |
49 | class GrowthRateSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
50 | serializers.HyperlinkedModelSerializer):
51 | class Meta:
52 | model = GrowthRate
53 | exclude = ()
54 |
55 |
56 | class SpecieSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
57 | serializers.HyperlinkedModelSerializer):
58 | growth_rate = GrowthRateSerializer()
59 | shape = ShapeSerializer()
60 | habitat = HabitatSerializer()
61 | generation = GenerationSerializer()
62 | # evolves_from_specie = SpecieSerializer()
63 |
64 | class Meta:
65 | model = Specie
66 | exclude = ()
67 |
68 |
69 | class PokemonSerializer(DemoSerializerMixin, NestedCreateMixin, NestedUpdateMixin,
70 | serializers.HyperlinkedModelSerializer):
71 | specie = SpecieSerializer()
72 |
73 | class Meta:
74 | model = Pokemon
75 | exclude = ()
76 |
77 |
78 | class SimpleUserSerializer(serializers.HyperlinkedModelSerializer):
79 |
80 | class Meta:
81 | model = get_user_model()
82 | fields = ('url', 'id', 'username', 'email', 'is_active', 'date_joined')
83 |
84 |
85 | class UserSerializer(SimpleUserSerializer):
86 | pass
87 |
--------------------------------------------------------------------------------
/demo/django/pokedex/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/demo/django/pokedex/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include, url
2 | from rest_framework.routers import DefaultRouter
3 |
4 |
5 | # Create a router and register our viewsets with it.
6 | from pokedex import viewsets
7 |
8 |
9 | router = DefaultRouter()
10 | router.register(r'pokemon', viewsets.PokemonViewSet)
11 | router.register(r'species', viewsets.SpecieViewSet)
12 | router.register(r'growth_rates', viewsets.GrowthRateViewSet)
13 | router.register(r'shapes', viewsets.ShapeViewSet)
14 | router.register(r'habitats', viewsets.HabitatViewSet)
15 | router.register(r'generations', viewsets.GenerationViewSet)
16 | router.register(r'regions', viewsets.RegionViewSet)
17 | router.register(r'users', viewsets.UserViewSet)
18 |
19 | # The API URLs are now determined automatically by the router.
20 | # Additionally, we include the login URLs for the browsable API.
21 | urlpatterns = [
22 | url(r'^api/', include(router.urls))
23 | ]
24 |
--------------------------------------------------------------------------------
/demo/django/requirements.in:
--------------------------------------------------------------------------------
1 | Django
2 | django-rest-framework
3 | django-filter
4 | drf-writable-nested
5 | gunicorn
6 | requests
7 |
--------------------------------------------------------------------------------
/demo/django/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile requirements.in
6 | #
7 | asgiref==3.2.10 # via django
8 | certifi==2020.6.20 # via requests
9 | chardet==3.0.4 # via requests
10 | django-filter==2.4.0 # via -r requirements.in
11 | django-rest-framework==0.1.0 # via -r requirements.in
12 | django==3.1.1 # via -r requirements.in, django-filter, djangorestframework
13 | djangorestframework==3.11.1 # via django-rest-framework
14 | drf-writable-nested==0.6.1 # via -r requirements.in
15 | gunicorn==20.0.4 # via -r requirements.in
16 | idna==2.10 # via requests
17 | pytz==2020.1 # via django
18 | requests==2.24.0 # via -r requirements.in
19 | sqlparse==0.3.1 # via django
20 | urllib3==1.25.10 # via requests
21 |
22 | # The following packages are considered to be unsafe in a requirements file:
23 | # setuptools
24 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.6'
2 | services:
3 |
4 | nginx:
5 | restart: always
6 | build:
7 | context: .
8 | dockerfile: ./Dockerfile
9 | target: nginx-build
10 | volumes:
11 | - ./conf/nginx/conf.d:/etc/nginx/conf.d:ro
12 | - ./data/nginx/log/:/var/log/nginx/
13 | ports:
14 | - "${ANGULAR_DJANGO_HTTP_PORT:-80:80}"
15 |
16 | angular:
17 | build:
18 | context: .
19 | dockerfile: ./Dockerfile
20 | target: angular-demo-build
21 |
22 | gunicorn:
23 | restart: always
24 | logging:
25 | driver: "json-file"
26 | options:
27 | max-file: "5"
28 | max-size: "10m"
29 | build:
30 | context: .
31 | dockerfile: ./Dockerfile
32 | target: gunicorn-build
33 | environment:
34 | DATA_DIRECTORY: /data
35 | volumes:
36 | - ./data/gunicorn/:/data/
37 |
38 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | docs:
16 | python build_docs.py
17 |
18 | .PHONY: help Makefile
19 |
20 | # Catch-all target: route all unknown targets to Sphinx using the new
21 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
22 | %: Makefile
23 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
24 |
--------------------------------------------------------------------------------
/docs/build_docs.py:
--------------------------------------------------------------------------------
1 | import os
2 | import subprocess
3 | from pathlib import Path
4 | from bs4 import BeautifulSoup
5 |
6 |
7 | INPUT_DOCS_DIRECTORY = '_build/htmlhelp/'
8 | OUTPUT_DOCS_DIRECTORY_DEFAULT = '../demo/angular/src/assets/docs/'
9 | OUTPUT_DOCS_DIRECTORY = Path(os.environ.get('OUTPUT_DOCS_DIRECTORY') or OUTPUT_DOCS_DIRECTORY_DEFAULT)
10 |
11 |
12 | os.makedirs(OUTPUT_DOCS_DIRECTORY, exist_ok=True)
13 |
14 |
15 | def only_body(fp):
16 | soup = BeautifulSoup(fp, 'html.parser')
17 | el = soup.select_one('div.body')
18 | return str(el)
19 |
20 |
21 | def main():
22 | subprocess.run("make htmlhelp", shell=True, check=True)
23 | for file in Path(INPUT_DOCS_DIRECTORY).glob('*.html'):
24 | with open(str(file), 'rb') as fp:
25 | html = only_body(fp)
26 | with open(OUTPUT_DOCS_DIRECTORY / file.name, 'w') as f:
27 | f.write(html)
28 |
29 |
30 | if __name__ == '__main__':
31 | main()
32 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | # import os
14 | # import sys
15 | # sys.path.insert(0, os.path.abspath('.'))
16 |
17 |
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = 'angular-django'
21 | copyright = '2021, Nekmo'
22 | author = 'Nekmo'
23 |
24 |
25 | # -- General configuration ---------------------------------------------------
26 |
27 | # Add any Sphinx extension module names here, as strings. They can be
28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
29 | # ones.
30 | extensions = [
31 | ]
32 |
33 | # Add any paths that contain templates here, relative to this directory.
34 | templates_path = ['_templates']
35 |
36 | # List of patterns, relative to source directory, that match files and
37 | # directories to ignore when looking for source files.
38 | # This pattern also affects html_static_path and html_extra_path.
39 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
40 |
41 |
42 | # -- Options for HTML output -------------------------------------------------
43 |
44 | # The theme to use for HTML and HTML Help pages. See the documentation for
45 | # a list of builtin themes.
46 | #
47 | html_theme = 'alabaster'
48 |
49 | # Add any paths that contain custom static files (such as style sheets) here,
50 | # relative to this directory. They are copied after the builtin static files,
51 | # so a file named "default.css" will overwrite the builtin "default.css".
52 | html_static_path = ['_static']
--------------------------------------------------------------------------------
/docs/first_steps.rst:
--------------------------------------------------------------------------------
1 | First steps
2 | ===========
3 | Angular-django generates the classes for angular from your Django Rest Framework viewsets and serializers.
4 | 2 types of classes are generated:
5 |
6 | * The ``ApiService`` classes are created from the **viewsets**. It has the api actions. For example *list, retrieve,
7 | etc.*
8 | * The ``SerializerService`` classes are created from the **serializers**. It has the properties of the objects
9 | returned by the api.
10 |
11 | For example from this model, viewset and serializer:
12 |
13 | .. code-block:: python
14 |
15 | # models.py
16 | # ---------
17 |
18 | class Pokemon(models.Model):
19 | identifier = models.CharField(max_length=50, help_text=_("Pokemon name"))
20 | specie = models.ForeignKey(Specie, on_delete=models.CASCADE)
21 | height = models.PositiveSmallIntegerField()
22 | weight = models.PositiveSmallIntegerField()
23 | base_experience = models.PositiveSmallIntegerField()
24 | order = models.PositiveSmallIntegerField()
25 | is_default = models.BooleanField()
26 | added_at = models.DateTimeField()
27 |
28 | # viewsets.py
29 | # -----------
30 |
31 | class PokemonViewSet(viewsets.ModelViewSet):
32 | queryset = Pokemon.objects.all()
33 | serializer_class = PokemonSerializer
34 |
35 | # serializers.py
36 | # --------------
37 |
38 | class PokemonSerializer(serializers.HyperlinkedModelSerializer):
39 | specie = SpecieSerializer()
40 |
41 | class Meta:
42 | model = Pokemon
43 | exclude = ()
44 |
45 | Using the ``manage.py angular_classes`` command these classes are generated:
46 |
47 | .. code-block:: typescript
48 |
49 | ///////////////////////////////////////
50 | // Pokemon API
51 | ///////////////////////////////////////
52 | export class Pokemon extends SerializerService {
53 | @Field() url: string;
54 | @Field() specie: Specie;
55 | @Field() identifier: string;
56 | @Field() height: number;
57 | @Field() weight: number;
58 | @Field() base_experience: number;
59 | @Field() order: number;
60 | @Field() is_default: boolean;
61 | @Field() added_at: Date;
62 | @Field() id: number;
63 | }
64 |
65 | @Injectable({
66 | providedIn: 'root'
67 | })
68 | export class PokemonApi extends ApiService {
69 |
70 | url = '/api/pokemon/';
71 | serializer = Pokemon;
72 | contentType = 'pokedex_pokemon';
73 |
74 | constructor(injector: Injector) {
75 | super(injector);
76 | }
77 | }
78 |
79 | Whenever you want to rebuild the classes for Angular run again from the console:
80 |
81 | .. code-block:: shell
82 |
83 | $ python setup.py angular_classes
84 |
85 | Paste the terminal output to a file in your Angular project. For example ``api.service.ts``. Angular-django doesn't
86 | update your existing classes. Edit your classes with the new changes from the terminal output.
87 |
88 | Now you can use the ``PokemonApi`` class to get interact with your Django Rest Framework api:
89 |
90 |
91 | .. code-block:: typescript
92 |
93 | PokemonApi.retrieve(123).subscribe(obj: Pokemon => { // Get pokemon with id 123
94 | obj.added_at.toLocaleString(); // added_at is returned as Date
95 | obj.specie.methodInCls(); // specie is returned as Specie and its methods are available
96 | });
97 |
98 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. angular-django documentation master file, created by
2 | sphinx-quickstart on Tue Aug 17 01:03:42 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to angular-django's documentation!
7 | ==========================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 | installation
14 | first_steps
15 | api_guide
16 |
17 |
18 | Indices and tables
19 | ==================
20 |
21 | * :ref:`genindex`
22 | * :ref:`modindex`
23 | * :ref:`search`
24 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | **Angular-django** consists of two packages: a package for Angular and an optional package for Django. To install the
5 | **Angular package**.
6 |
7 | .. code-block:: shell
8 |
9 | $ npm i angular-django
10 |
11 | Import ``AngularDjangoModule`` and ``HttpClientModule`` in your app:
12 |
13 | .. code-block:: typescript
14 |
15 | import { AngularDjangoModule } from 'angular-django';
16 | import { HttpClientModule } from '@angular/common/http';
17 |
18 |
19 | @NgModule({
20 | imports: [
21 | AngularDjangoModule,
22 | HttpClientModule,
23 | ],
24 | })
25 | export class AppModule { }
26 |
27 | Enable ``emitDecoratorMetadata`` in the ``compilerOptions`` section of your ``tsconfig.json``:
28 |
29 | .. code-block:: json
30 |
31 | {
32 | "compilerOptions": {
33 | "emitDecoratorMetadata": true
34 | }
35 | }
36 |
37 | Install the **Django package** in your project:
38 |
39 | .. code-block:: shell
40 |
41 | $ pip install -U angular-django
42 |
43 | Add the app in the ``INSTALLED_APPS`` list in your ``settings.py`` file:
44 |
45 | .. code-block:: python
46 |
47 | INSTALLED_APPS = [
48 | # ...
49 | 'rest_framework',
50 | 'angular_django',
51 | ]
52 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/requirements.in:
--------------------------------------------------------------------------------
1 | beautifulsoup4
2 | Sphinx
3 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile
6 | #
7 | alabaster==0.7.12 # via sphinx
8 | babel==2.9.1 # via sphinx
9 | beautifulsoup4==4.9.3 # via -r requirements.in
10 | certifi==2021.5.30 # via requests
11 | charset-normalizer==2.0.4 # via requests
12 | docutils==0.17.1 # via sphinx
13 | idna==3.2 # via requests
14 | imagesize==1.2.0 # via sphinx
15 | jinja2==3.0.1 # via sphinx
16 | markupsafe==2.0.1 # via jinja2
17 | packaging==21.0 # via sphinx
18 | pygments==2.10.0 # via sphinx
19 | pyparsing==2.4.7 # via packaging
20 | pytz==2021.1 # via babel
21 | requests==2.26.0 # via sphinx
22 | snowballstemmer==2.1.0 # via sphinx
23 | soupsieve==2.2.1 # via beautifulsoup4
24 | sphinx==4.1.2 # via -r requirements.in
25 | sphinxcontrib-applehelp==1.0.2 # via sphinx
26 | sphinxcontrib-devhelp==1.0.2 # via sphinx
27 | sphinxcontrib-htmlhelp==2.0.0 # via sphinx
28 | sphinxcontrib-jsmath==1.0.1 # via sphinx
29 | sphinxcontrib-qthelp==1.0.3 # via sphinx
30 | sphinxcontrib-serializinghtml==1.1.5 # via sphinx
31 | urllib3==1.26.6 # via requests
32 |
33 | # The following packages are considered to be unsafe in a requirements file:
34 | # setuptools
35 |
--------------------------------------------------------------------------------
/pypi_readme.rst:
--------------------------------------------------------------------------------
1 | ##############
2 | angular-django
3 | ##############
4 |
5 |
6 | .. image:: https://img.shields.io/travis/Nekmo/angular-django.svg?style=flat-square&maxAge=2592000
7 | :target: https://travis-ci.org/Nekmo/angular-django
8 | :alt: Latest Travis CI build status
9 |
10 | .. image:: https://img.shields.io/pypi/v/angular-django.svg?style=flat-square
11 | :target: https://pypi.org/project/angular-django/
12 | :alt: Latest PyPI version
13 |
14 | .. image:: https://img.shields.io/pypi/pyversions/angular-django.svg?style=flat-square
15 | :target: https://pypi.org/project/angular-django/
16 | :alt: Python versions
17 |
18 | .. image:: https://img.shields.io/codeclimate/github/Nekmo/angular-django.svg?style=flat-square
19 | :target: https://codeclimate.com/github/Nekmo/angular-django
20 | :alt: Code Climate
21 |
22 | .. image:: https://img.shields.io/codecov/c/github/Nekmo/angular-django/master.svg?style=flat-square
23 | :target: https://codecov.io/github/Nekmo/angular-django
24 | :alt: Test coverage
25 |
26 | .. image:: https://img.shields.io/requires/github/Nekmo/angular-django.svg?style=flat-square
27 | :target: https://requires.io/github/Nekmo/angular-django/requirements/?branch=master
28 | :alt: Requirements Status
29 |
30 |
31 | .. image:: https://img.shields.io/requires/github/Nekmo/angular-django.svg?style=flat-square
32 | :target: https://requires.io/github/Nekmo/angular-django/requirements/?branch=master
33 | :alt: Requirements Status
34 |
35 |
36 | .. image:: https://raw.githubusercontent.com/Nekmo/angular-django/master/angular_django.svg
37 | :width: 256px
38 | :height: 256px
39 | :align: center
40 |
41 |
42 | **Angular Django** is a framework to work in *Angular* as in *Django*. Use the Django classes in Angular to build
43 | **forms** and **data** grids in minutes. `A demo is available on the website `_.
44 |
45 | Angular-django consists of **two packages**: a package for *Angular* and an optional package for *Django*. To install
46 | the Angular package:
47 |
48 | .. code-block:: shell
49 |
50 | $ npm i angular-django
51 |
52 |
53 | To install the Django package:
54 |
55 | .. code-block:: shell
56 |
57 | $ pip install -U angular-django
58 |
59 | Full instructions are available `on the website `_.
60 |
61 |
62 | Features
63 | ========
64 | Some features available:
65 |
66 | * Use the methods and filters available in the Django Rest Framework to work with the API.
67 | * Build forms in minutes. Includes validation on frontend and backend. Selector choices are built with the server.
68 | * Easy-to-implement filtering, paging, and searching listings.
69 | * Use your Django classes and types in Angular. The library will transform the API values to the correct types.
70 |
--------------------------------------------------------------------------------
/requirements.in:
--------------------------------------------------------------------------------
1 | Django
2 | djangorestframework
3 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 0.0.1
3 | commit = True
4 | tag = True
5 |
6 | [metadata]
7 | name = angular-django
8 | version = attr: angular_django.__version__
9 | author = Nekmo
10 | author-email = contacto@nekmo.com
11 | url = https://github.com/Nekmo/angular-django/
12 | download_url = https://github.com/Nekmo/angular-django/archive/master.zip
13 | description = Django Rest Framework API for Angular with self-building classes, forms, and listings
14 | long-description = file: pypi_readme.rst
15 | license = MIT
16 | license-file = LICENSE
17 | platform = any
18 | keywords = angular-django
19 |
20 | [options]
21 | zip_safe = False
22 | include_package_data = True
23 | package_dir =
24 | =src/django
25 | packages = angular_django
26 |
27 | [bdist_wheel]
28 | universal = 1
29 |
30 | [sdist]
31 | formats = zip, gztar
32 |
33 | [check]
34 | metadata = True
35 | restructuredtext = True
36 | strict = True
37 |
38 | [bumpversion:file:src/django/angular_django/__init__.py]
39 | search = __version__ = '{current_version}'
40 | replace = __version__ = '{new_version}'
41 |
42 | [flake8]
43 | exclude = docs
44 |
45 | [aliases]
46 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import copy
4 | from itertools import chain
5 | from setuptools import setup
6 |
7 | REQUIREMENT_FILE = 'requirements.in'
8 | DEV_STATUS = 'Production/Stable' # Planning, Pre-Alpha, Alpha, Beta, Production/Stable, Mature, Inactive
9 | CLASSIFIERS = [ # https://github.com/github/choosealicense.com/tree/gh-pages/_licenses
10 | 'License :: OSI Approved :: MIT License',
11 | # 'License :: OSI Approved :: BSD License',
12 | # 'License :: OSI Approved :: ISC License (ISCL)',
13 | # 'License :: OSI Approved :: Apache Software License',
14 | # 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
15 | ] # https://pypi.python.org/pypi?%3Aaction=list_classifiers
16 | NATURAL_LANGUAGE = 'English'
17 | PLATFORMS = [
18 | # 'universal',
19 | 'linux',
20 | # 'macosx',
21 | # 'solaris',
22 | # 'irix',
23 | # 'win'
24 | # 'bsd'
25 | # 'ios'
26 | # 'android'
27 | ]
28 | PYTHON_VERSIONS = ['3.6', '3.7', '3.8', '3.9']
29 |
30 |
31 | def get_python_classifiers(versions):
32 | for version in range(2, 4):
33 | if not next(iter(filter(lambda x: int(float(x)) != version, versions.copy())), False):
34 | versions.add('{} :: Only'.format(version))
35 | break
36 | return ['Programming Language :: Python :: %s' % version for version in versions]
37 |
38 |
39 | def get_platform_classifiers(platform):
40 | parts = {
41 | 'linux': ('POSIX', 'Linux'),
42 | 'win': ('Microsoft', 'Windows'),
43 | 'solaris': ('POSIX', 'SunOS/Solaris'),
44 | 'aix': ('POSIX', 'Linux'),
45 | 'unix': ('Unix',),
46 | 'bsd': ('POSIX', 'BSD')
47 | }[platform]
48 | return ['Operating System :: {}'.format(' :: '.join(parts[:i+1]))
49 | for i in range(len(parts))]
50 |
51 |
52 | def read_file(path):
53 | with open(path) as f:
54 | return f.read()
55 |
56 |
57 | statuses = ['Planning', 'Pre-Alpha', 'Alpha', 'Beta', 'Production/Stable', 'Mature', 'Inactive']
58 |
59 | # Classifiers
60 | classifiers = copy.copy(CLASSIFIERS)
61 | classifiers.extend(get_python_classifiers(set(PYTHON_VERSIONS) - {2.8, 2.9}))
62 | classifiers.extend(chain(*[get_platform_classifiers(platform) for platform in PLATFORMS]))
63 | classifiers.extend([
64 | 'Natural Language :: {}'.format(NATURAL_LANGUAGE),
65 | 'Development Status :: {} - {}'.format(statuses.index(DEV_STATUS) + 1, DEV_STATUS),
66 | ])
67 |
68 |
69 | setup(
70 | classifiers=classifiers,
71 | platforms=PLATFORMS,
72 | install_requires=read_file(REQUIREMENT_FILE),
73 | )
74 |
--------------------------------------------------------------------------------
/src/angular/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | yarn-error.log
41 | testem.log
42 | /typings
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/src/angular/README.md:
--------------------------------------------------------------------------------
1 | # AngularDjango
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.3.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/src/angular/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-django": {
7 | "projectType": "library",
8 | "root": "projects/angular-django",
9 | "sourceRoot": "projects/angular-django/src",
10 | "prefix": "adm",
11 | "architect": {
12 | "build": {
13 | "builder": "@angular-devkit/build-angular:ng-packagr",
14 | "options": {
15 | "tsConfig": "projects/angular-django/tsconfig.lib.json",
16 | "project": "projects/angular-django/ng-package.json"
17 | },
18 | "configurations": {
19 | "production": {
20 | "tsConfig": "projects/angular-django/tsconfig.lib.prod.json"
21 | }
22 | }
23 | },
24 | "test": {
25 | "builder": "@angular-devkit/build-angular:karma",
26 | "options": {
27 | "main": "projects/angular-django/src/test.ts",
28 | "tsConfig": "projects/angular-django/tsconfig.spec.json",
29 | "karmaConfig": "projects/angular-django/karma.conf.js"
30 | }
31 | },
32 | "lint": {
33 | "builder": "@angular-devkit/build-angular:tslint",
34 | "options": {
35 | "tsConfig": [
36 | "projects/angular-django/tsconfig.lib.json",
37 | "projects/angular-django/tsconfig.spec.json"
38 | ],
39 | "exclude": [
40 | "**/node_modules/**"
41 | ]
42 | }
43 | }
44 | }
45 | }},
46 | "cli": {
47 | "analytics": "542e0a50-12b0-4864-a051-7229c687b664"
48 | },
49 | "defaultProject": "angular-django"
50 | }
51 |
--------------------------------------------------------------------------------
/src/angular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-django",
3 | "version": "0.0.6",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint"
10 | },
11 | "private": false,
12 | "dependencies": {
13 | "@angular/animations": "~11.0.5",
14 | "@angular/cdk": "^11.0.3",
15 | "@angular/common": "~11.0.5",
16 | "@angular/compiler": "~11.0.5",
17 | "@angular/core": "~11.0.5",
18 | "@angular/forms": "~11.0.5",
19 | "@angular/material": "^11.0.3",
20 | "@angular/platform-browser": "~11.0.5",
21 | "@angular/platform-browser-dynamic": "~11.0.5",
22 | "@angular/router": "~11.0.5",
23 | "@ngrx/schematics": "^10.0.0",
24 | "rxjs": "~6.6.0",
25 | "tslib": "^2.0.0",
26 | "zone.js": "~0.10.2"
27 | },
28 | "devDependencies": {
29 | "@angular-devkit/build-angular": "~0.1100.5",
30 | "@angular/cli": "~11.0.5",
31 | "@angular/compiler-cli": "~11.0.5",
32 | "@types/jasmine": "~3.6.0",
33 | "@types/jasminewd2": "~2.0.3",
34 | "@types/node": "^12.11.1",
35 | "codelyzer": "^6.0.0",
36 | "jasmine-core": "~3.6.0",
37 | "jasmine-spec-reporter": "~5.0.0",
38 | "karma": "~5.1.1",
39 | "karma-chrome-launcher": "~3.1.0",
40 | "karma-coverage-istanbul-reporter": "~3.0.2",
41 | "karma-jasmine": "~4.0.0",
42 | "karma-jasmine-html-reporter": "^1.5.0",
43 | "ng-packagr": "^11.0.3",
44 | "protractor": "~7.0.0",
45 | "ts-node": "~8.3.0",
46 | "tslint": "~6.1.0",
47 | "typescript": "~4.0.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/README.md:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | Work in Angular as in Django
6 |
7 |
8 | **Angular Django** is a framework to work in *Angular* as in *Django*. Use the Django classes in Angular to build
9 | **forms** and **data** grids in minutes. [A demo is available on the website](https://angular-django.nekmo.org/).
10 |
11 | Angular-django consists of **two packages**: a package for *Angular* and an optional package for *Django*. To install
12 | the Angular package:
13 |
14 | ```shell
15 | $ npm i angular-django
16 | ```
17 |
18 |
19 | To install the Django package:
20 |
21 | ```shell
22 | $ pip install -U angular-django
23 | ```
24 |
25 | Full instructions are available [on the website](https://angular-django.nekmo.org/installation>).
26 |
27 |
28 | Features
29 | ========
30 | Some features available:
31 |
32 | * Use the methods and filters available in the Django Rest Framework to work with the API.
33 | * Build forms in minutes. Includes validation on frontend and backend. Selector choices are built with the server.
34 | * Easy-to-implement filtering, paging, and searching listings.
35 | * Use your Django classes and types in Angular. The library will transform the API values to the correct types.
36 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage/angular-django'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "ngPackage": {}
3 | }
4 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/angular-django-material-table.component.css:
--------------------------------------------------------------------------------
1 | .cdk-column-select {
2 | max-width: 40px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/angular-django-material-table.component.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ column.label }}
29 |
30 |
31 |
32 |
33 |
35 |
36 |
37 |
38 | {{ row[column.name] }}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/angular-django-material-table.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AngularDjangoMaterialTableComponent } from './angular-django-material-table.component';
4 |
5 | describe('AngularDjangoMaterialTableComponent', () => {
6 | let component: AngularDjangoMaterialTableComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ AngularDjangoMaterialTableComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(AngularDjangoMaterialTableComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/angular-django-material-table.directive.ts:
--------------------------------------------------------------------------------
1 | import {ContentChild, Directive, Input, TemplateRef} from '@angular/core';
2 |
3 |
4 | @Directive({
5 | selector: '[admCellDef]',
6 | })
7 | export class DjangoCellDefDirective {
8 | constructor(/** @docs-private */ public template: TemplateRef) { }
9 | }
10 |
11 |
12 | @Directive({
13 | selector: '[admColumnDef]',
14 | })
15 | export class AngularDjangoMaterialColumnDefDirective {
16 | /** Unique name for this column. */
17 | @Input('admColumnDef') name: string;
18 |
19 | /** Whether this column should be sticky positioned at the start of the row */
20 |
21 | /** @docs-private */
22 | @ContentChild(DjangoCellDefDirective) cell: DjangoCellDefDirective;
23 | }
24 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/angular-django-material-table.interface.ts:
--------------------------------------------------------------------------------
1 | export interface Column {
2 | name: string;
3 | label?: string;
4 | ordering?: boolean;
5 | }
6 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/angular-django-material-table.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import {
3 | AngularDjangoMaterialTableComponent,
4 | } from './angular-django-material-table.component';
5 | import {
6 | AngularDjangoMaterialColumnDefDirective,
7 | DjangoCellDefDirective,
8 | } from './angular-django-material-table.directive';
9 | import {AngularDjangoModule} from 'angular-django';
10 | import {MatTableModule} from '@angular/material/table';
11 | import {MatCheckboxModule} from '@angular/material/checkbox';
12 | import {MatPaginatorModule} from '@angular/material/paginator';
13 | import {CommonModule} from '@angular/common';
14 | import {MatSortModule} from '@angular/material/sort';
15 | import {ShiftClickDirective} from './shift-click-directive';
16 |
17 |
18 | @NgModule({
19 | imports: [
20 | CommonModule,
21 | AngularDjangoModule,
22 | MatTableModule,
23 | MatCheckboxModule,
24 | MatPaginatorModule,
25 | MatSortModule,
26 | ],
27 | declarations: [
28 | AngularDjangoMaterialTableComponent,
29 | DjangoCellDefDirective,
30 | AngularDjangoMaterialColumnDefDirective,
31 | ShiftClickDirective,
32 | ],
33 | exports: [
34 | AngularDjangoMaterialTableComponent,
35 | DjangoCellDefDirective,
36 | AngularDjangoMaterialColumnDefDirective,
37 | ]
38 | })
39 | export class AngularDjangoMaterialTableModule {
40 | }
41 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material-table/shift-click-directive.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | Input,
4 | HostListener,
5 | HostBinding,
6 | OnInit,
7 | OnDestroy,
8 | ElementRef,
9 | } from '@angular/core';
10 |
11 | import {SelectionModel, SelectionChange} from '@angular/cdk/collections';
12 |
13 | import { Subject, BehaviorSubject, } from 'rxjs';
14 | import { takeUntil } from 'rxjs/operators';
15 |
16 | import {Page} from 'angular-django';
17 |
18 |
19 |
20 | @Directive({
21 | selector: '[shiftClickSource]'
22 | })
23 | export class ShiftClickDirective implements OnInit, OnDestroy {
24 |
25 | finishHim = new Subject();
26 |
27 | // the item from with the selection originates
28 | lastItem: any;
29 | selecting = false;
30 | shiftHolding$ = new BehaviorSubject(false);
31 |
32 |
33 | // datasource that is used on the Checkbox-Table
34 | @Input('shiftClickSource') dataSource: Page;
35 | @Input('shiftClickSelectModel') selection: SelectionModel;
36 | @Input('shiftClickReloadSourceEvent') reloadSourceEvent: any;
37 | @HostListener('document:keydown.shift', ['$event'])
38 | shiftDown(_) {
39 | this.userSelect = 'none';
40 | this.shiftHolding$.next(true);
41 | }
42 | @HostListener('document:keyup.shift', ['$event'])
43 | shiftUp(event: KeyboardEvent) {
44 | this.userSelect = 'unset';
45 | this.shiftHolding$.next(false);
46 | }
47 |
48 |
49 | // disable select on mat-table while shift is clicked.
50 | @HostBinding('style.user-select')
51 | userSelect = 'unset';
52 |
53 | constructor(private host: ElementRef) {}
54 |
55 | ngOnInit() {
56 | this.selection.changed
57 | .pipe(takeUntil(this.finishHim))
58 | .subscribe((selectionChange: SelectionChange) => {
59 | const item = selectionChange.added[0] || selectionChange.removed[0];
60 | if (item && this.lastItem && !this.selecting && this.userSelect === 'none') {
61 | this.selecting = true;
62 | const index0 = this.dataSource.indexOf(item);
63 | const index1 = this.dataSource.indexOf(this.lastItem);
64 | const indexes = [index0, index1].sort();
65 | const items = [...this.dataSource].slice(...[indexes[0], indexes[1] + 1]);
66 | const allSelected = items.filter((value) => value !== item)
67 | .every((value) => this.selection.isSelected(value));
68 | if (allSelected) {
69 | this.selection.deselect(...items);
70 | } else {
71 | this.selection.select(...items);
72 | }
73 | this.selecting = false;
74 | }
75 | this.lastItem = item;
76 | });
77 | this.reloadSourceEvent
78 | .pipe(takeUntil(this.finishHim))
79 | .subscribe(() => this.reset());
80 | }
81 |
82 | reset() {
83 | this.lastItem = null;
84 | this.selecting = false;
85 | this.userSelect = 'unset';
86 | }
87 |
88 | ngOnDestroy() {
89 | this.finishHim.next();
90 | this.finishHim.complete();
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/angular-django-material.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import {AngularDjangoMaterialTableModule} from './angular-django-material-table/angular-django-material-table.module';
3 |
4 |
5 | @NgModule({
6 | imports: [
7 | AngularDjangoMaterialTableModule,
8 | ],
9 | declarations: [
10 | ],
11 | exports: [
12 | AngularDjangoMaterialTableModule,
13 | ]
14 | })
15 | export class AngularDjangoMaterialModule {
16 | }
17 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/material/src/public_api.ts:
--------------------------------------------------------------------------------
1 | export * from './angular-django-material.module';
2 | export * from './angular-django-material-table/angular-django-material-table.component';
3 | export * from './angular-django-material-table/angular-django-material-table.directive';
4 | export * from './angular-django-material-table/angular-django-material-table.interface';
5 | export * from './angular-django-material-table/angular-django-material-table.module';
6 | // export * from '../../src/public-api';
7 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/angular-django",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | },
7 | "whitelistedNonPeerDependencies": [
8 | "."
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-django",
3 | "version": "0.0.6",
4 | "peerDependencies": {
5 | "@angular/common": "^10.1.3",
6 | "@angular/core": "^10.1.3"
7 | },
8 | "dependencies": {
9 | "tslib": "^2.0.0"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/angular-django.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AngularDjangoComponent } from './angular-django.component';
4 |
5 | describe('AngularDjangoComponent', () => {
6 | let component: AngularDjangoComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ AngularDjangoComponent ]
12 | })
13 | .compileComponents();
14 | });
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(AngularDjangoComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/angular-django.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'lib-angular-django',
5 | template: `
6 |
7 | Ey, angular-django works!! :)
8 |
9 | `,
10 | styles: [
11 | ]
12 | })
13 | export class AngularDjangoComponent implements OnInit {
14 |
15 | constructor() { }
16 |
17 | ngOnInit(): void {
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/angular-django.module.ts:
--------------------------------------------------------------------------------
1 | import {InjectionToken, ModuleWithProviders, NgModule} from '@angular/core';
2 | import { AngularDjangoComponent } from './angular-django.component';
3 | import {GetDisplayPipe} from './get-display.pipe';
4 |
5 |
6 | export interface AngularDjangoConfig {
7 | rootUrl?: string;
8 | }
9 |
10 |
11 | export const ANGULAR_DJANGO_CONFIG = new InjectionToken('ANGULAR_DJANGO_CONFIG');
12 |
13 |
14 | @NgModule({
15 | providers: [
16 | {provide: ANGULAR_DJANGO_CONFIG, useValue: {} },
17 | ],
18 | declarations: [
19 | AngularDjangoComponent,
20 | GetDisplayPipe
21 | ],
22 | imports: [
23 | ],
24 | exports: [
25 | AngularDjangoComponent,
26 | GetDisplayPipe
27 | ]
28 | })
29 | export class AngularDjangoModule {
30 | static forRoot(config: AngularDjangoConfig): ModuleWithProviders {
31 | return {
32 | ngModule: AngularDjangoModule,
33 | providers: [
34 | {provide: ANGULAR_DJANGO_CONFIG, useValue: config }
35 | ]
36 | };
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/angular-django.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { AngularDjangoService } from './angular-django.service';
4 |
5 | describe('AngularDjangoService', () => {
6 | let service: AngularDjangoService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(AngularDjangoService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/angular-django.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class AngularDjangoService {
7 |
8 | constructor() { }
9 | }
10 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/api.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { ApiService } from './api.service';
4 |
5 | describe('User', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [ApiService]
9 | });
10 | });
11 |
12 | it('should be created', inject([ApiService], (service: ApiService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/get-display.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import {Observable} from 'rxjs';
3 |
4 | @Pipe({name: 'getDisplay', pure: true})
5 | export class GetDisplayPipe implements PipeTransform {
6 | transform(serializer: any, fieldName: string): Observable {
7 | return serializer.getDisplay(fieldName);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/serializer.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { SerializerService } from './serializer.service';
4 |
5 | describe('SerializerService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [SerializerService]
9 | });
10 | });
11 |
12 | it('should be created', inject([SerializerService], (service: SerializerService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/utility-types.ts:
--------------------------------------------------------------------------------
1 | export interface Dict {
2 | [key: string /*TKey*/]: TVal;
3 | }
4 |
5 |
6 | export type Dictionary = Dict;
7 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import {Dictionary} from './utility-types';
2 |
3 | export function getCookie(name: string): null | string {
4 | if (!document.cookie) {
5 | return null;
6 | }
7 |
8 | const xsrfCookies = document.cookie.split(';')
9 | .map(c => c.trim())
10 | .filter(c => c.startsWith(name + '='));
11 |
12 | if (xsrfCookies.length === 0) {
13 | return null;
14 | }
15 |
16 | return decodeURIComponent(xsrfCookies[0].split('=')[1]);
17 | }
18 |
19 |
20 | export function getNestedDictionary(dictionary: Dictionary, nestedKey: string): any {
21 | nestedKey.split('__').forEach((subFieldName: string) => {
22 | dictionary = dictionary[subFieldName];
23 | if (dictionary === undefined) {
24 | throw new Error(`Invalid item ${subFieldName} on ${nestedKey} query`);
25 | }
26 | if (dictionary.type === 'nested object') {
27 | dictionary = dictionary.children; // TODO: returning dictionary.children type is unknown.
28 | }
29 | });
30 | return dictionary;
31 | }
32 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/lib/widgets.ts:
--------------------------------------------------------------------------------
1 | import {DjangoFormlyField, FormlyTemplateOptions} from './form';
2 | import {FieldOptions} from './serializer.service';
3 | import {map} from 'rxjs/operators';
4 |
5 |
6 | function removeApi(items: any): any {
7 | if (items && typeof items === 'object' && (items.hasOwnProperty('_api') || items.hasOwnProperty('apiService'))) {
8 | Object.entries(items).forEach(([key, value]) => {
9 | if (key === '_api') {
10 | items._api = undefined;
11 | } else {
12 | removeApi(value);
13 | }
14 | });
15 | }
16 | return items;
17 | }
18 |
19 |
20 | export class Widget {
21 | name?: string;
22 | type: string;
23 | templateOptionsType?: string;
24 |
25 | updateTemplateOptions?(templateOptions: FormlyTemplateOptions, formlyField: DjangoFormlyField): void {
26 | if (this.templateOptionsType) {
27 | templateOptions.type = this.templateOptionsType;
28 | }
29 | }
30 |
31 | constructor(options?: Widget) {
32 | if (options) {
33 | Object.assign(this, options);
34 | }
35 | }
36 | }
37 |
38 |
39 | export class SelectWidget extends Widget {
40 | type = 'select';
41 | name = 'select';
42 |
43 | updateTemplateOptions(templateOptions: FormlyTemplateOptions, formlyField: DjangoFormlyField): void {
44 | if ( formlyField.api.hasOptions ) {
45 | templateOptions.options = formlyField.api.getOptionField(formlyField.key).choices.map((item => {
46 | return {value: item.value, label: item.display_name};
47 | }));
48 | } else {
49 | templateOptions.options = [];
50 | }
51 | }
52 | }
53 |
54 | export class AutocompleteWidget extends Widget {
55 | type = 'autocomplete';
56 | name = 'autocomplete';
57 |
58 | updateTemplateOptions(templateOptions: FormlyTemplateOptions, formlyField: DjangoFormlyField): void {
59 | const field: FieldOptions = formlyField.api.serializer.fields[formlyField.key];
60 | // Improvement: cache term results;
61 | templateOptions.filter = (term) => {
62 | if (typeof term !== 'string') {
63 | term = '';
64 | }
65 | return formlyField.api.injector.get(field.type.getApiClass())
66 | .search(term).list().pipe(map((items: any[]) => removeApi(items)));
67 | };
68 | }
69 | }
70 |
71 |
72 | export const FORM_TYPES = {
73 | number: new Widget({type: 'input', name: 'number', templateOptionsType: 'number'}),
74 | boolean: new Widget({type: 'checkbox', name: 'boolean'}),
75 | date: new Widget({type: 'datepicker', name: 'date'}),
76 | choice: new SelectWidget(),
77 | 'nested object': new AutocompleteWidget(),
78 | };
79 | export const DEFAULT_TYPE = 'input';
80 |
81 |
82 |
83 | export function getWidgetFromName(name: string): Widget {
84 | return Object.keys(FORM_TYPES).map(key => FORM_TYPES[key]).find((w => w.name === name));
85 | }
86 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of angular-django
3 | */
4 |
5 | export * from './lib/angular-django.service';
6 | export * from './lib/angular-django.component';
7 | export * from './lib/angular-django.module';
8 | export * from './lib/api.service';
9 | export * from './lib/serializer.service';
10 | export * from './lib/get-display.pipe';
11 | export * from './lib/utility-types';
12 | export * from './lib/form';
13 |
14 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone';
4 | import 'zone.js/dist/zone-testing';
5 | import { getTestBed } from '@angular/core/testing';
6 | import {
7 | BrowserDynamicTestingModule,
8 | platformBrowserDynamicTesting
9 | } from '@angular/platform-browser-dynamic/testing';
10 |
11 | declare const require: {
12 | context(path: string, deep?: boolean, filter?: RegExp): {
13 | keys(): string[];
14 | (id: string): T;
15 | };
16 | };
17 |
18 | // First, initialize the Angular testing environment.
19 | getTestBed().initTestEnvironment(
20 | BrowserDynamicTestingModule,
21 | platformBrowserDynamicTesting()
22 | );
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/lib",
6 | "target": "es2015",
7 | "declaration": true,
8 | "declarationMap": true,
9 | "inlineSources": true,
10 | "types": [],
11 | "lib": [
12 | "dom",
13 | "es2018"
14 | ]
15 | },
16 | "angularCompilerOptions": {
17 | "skipTemplateCodegen": true,
18 | "strictMetadataEmit": true,
19 | "enableResourceInlining": true
20 | },
21 | "exclude": [
22 | "src/test.ts",
23 | "**/*.spec.ts"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.lib.json",
4 | "compilerOptions": {
5 | "declarationMap": false
6 | },
7 | "angularCompilerOptions": {
8 | "enableIvy": false
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/spec",
6 | "types": [
7 | "jasmine",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/src/angular/projects/angular-django/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "no-string-literal": false,
5 | "no-trailing-whitespace": false,
6 | "directive-selector": [
7 | true,
8 | "attribute",
9 | "lib",
10 | "camelCase"
11 | ],
12 | "component-selector": [
13 | true,
14 | "element",
15 | "lib",
16 | "kebab-case"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/angular/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "downlevelIteration": true,
9 | "experimentalDecorators": true,
10 | "moduleResolution": "node",
11 | "importHelpers": true,
12 | "target": "es2015",
13 | "module": "es2020",
14 | "lib": [
15 | "es2018",
16 | "dom"
17 | ],
18 | "paths": {
19 | "angular-django": [
20 | "dist/angular-django/angular-django",
21 | "dist/angular-django"
22 | ]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/django/angular_django/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Top-level package for angular-django."""
4 |
5 | __author__ = """Nekmo"""
6 | __email__ = 'contacto@nekmo.com'
7 | __version__ = '0.0.1'
8 |
--------------------------------------------------------------------------------
/src/django/angular_django/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/src/django/angular_django/management/__init__.py
--------------------------------------------------------------------------------
/src/django/angular_django/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nekmo/angular-django/f79d8f40a14d8d8844ca4e88d788c0cc8442ae96/src/django/angular_django/management/commands/__init__.py
--------------------------------------------------------------------------------
/src/django/angular_django/management/commands/angular_classes.py:
--------------------------------------------------------------------------------
1 | """Generate Angular 5+ Typescript classes from REST API.
2 | """
3 | from django.core.management.base import BaseCommand, CommandError
4 |
5 | from angular_django.rest_framework import get_api_views
6 | from angular_django.typescript import tpl
7 |
8 |
9 | class Command(BaseCommand):
10 | help = globals()['__doc__']
11 |
12 | def handle(self, *args, **options):
13 | for view, pattern in get_api_views():
14 | print(tpl(view, pattern))
15 |
--------------------------------------------------------------------------------
/src/django/angular_django/metadata.py:
--------------------------------------------------------------------------------
1 | from django.forms import fields as django_forms_fields
2 | from rest_framework.metadata import SimpleMetadata
3 | from rest_framework.utils.field_mapping import ClassLookupDict
4 |
5 | try:
6 | from django_filters.rest_framework import DjangoFilterBackend
7 | except ImportError:
8 | DjangoFilterBackend = None
9 |
10 |
11 | if DjangoFilterBackend is not None:
12 | from django_filters import fields as django_filters_fields
13 | FORM_FIELDS = {
14 | django_forms_fields.NullBooleanField: 'boolean',
15 | django_forms_fields.BooleanField: 'boolean',
16 | django_forms_fields.URLField: 'url',
17 | django_forms_fields.EmailField: 'email',
18 | django_forms_fields.RegexField: 'regex',
19 | django_forms_fields.SlugField: 'slug',
20 | django_forms_fields.IntegerField: 'integer',
21 | django_forms_fields.FloatField: 'float',
22 | django_forms_fields.DecimalField: 'decimal',
23 | django_forms_fields.DateField: 'date',
24 | django_forms_fields.DateTimeField: 'datetime',
25 | django_forms_fields.TimeField: 'time',
26 | django_forms_fields.ChoiceField: 'choice',
27 | django_filters_fields.ChoiceField: 'choice',
28 | django_forms_fields.MultipleChoiceField: 'multiple choice',
29 | django_filters_fields.MultipleChoiceField: 'multiple choice',
30 | django_forms_fields.FileField: 'file upload',
31 | django_forms_fields.FilePathField: 'file upload',
32 | django_forms_fields.ImageField: 'image upload',
33 | django_filters_fields.ModelMultipleChoiceField: 'list',
34 | django_filters_fields.ModelChoiceField: 'nested object',
35 | }
36 | else:
37 | FORM_FIELDS = {}
38 |
39 |
40 | class AngularDjangoMetadata(SimpleMetadata):
41 | label_form_lookup = ClassLookupDict(FORM_FIELDS)
42 |
43 |
44 | def determine_metadata(self, request, view):
45 | metadata = super().determine_metadata(request, view)
46 | filters = {}
47 | for filter_backend in view.filter_backends:
48 | backend = filter_backend()
49 | if DjangoFilterBackend is not None and isinstance(backend, DjangoFilterBackend):
50 | fields = backend.get_filterset_class(view)().form.fields
51 | else:
52 | fields = {}
53 | if hasattr(backend, 'get_schema_operation_parameters'):
54 | for f in backend.get_schema_operation_parameters(view):
55 | f = dict(f)
56 | field = fields.get(f['name'])
57 | try:
58 | f['type'] = self.label_form_lookup[field]
59 | except KeyError:
60 | pass
61 | if field:
62 | f['label'] = field.label
63 | if field and f.get('type') == 'choice':
64 | f['choices'] = [{'value': choice[0], 'display_name': choice[1]} for choice in field.choices]
65 | filters[f['name']] = f
66 | metadata['filters'] = filters
67 | return metadata
68 |
--------------------------------------------------------------------------------
/src/django/angular_django/pagination.py:
--------------------------------------------------------------------------------
1 | from collections import OrderedDict
2 |
3 | from rest_framework.pagination import PageNumberPagination
4 | from rest_framework.response import Response
5 |
6 |
7 | class AngularDjangoPageNumberPagination(PageNumberPagination):
8 | def get_paginated_response(self, data):
9 | return Response(OrderedDict([
10 | ('count', self.page.paginator.count),
11 | ('page_size', self.page.paginator.per_page),
12 | ('next', self.get_next_link()),
13 | ('previous', self.get_previous_link()),
14 | ('results', data)
15 | ]))
16 |
17 | def get_paginated_response_schema(self, schema):
18 | response_schema = super().get_paginated_response_schema(schema)
19 | response_schema['properties']['page_size'] = {
20 | 'type': 'integer',
21 | 'example': 10,
22 | }
23 | return response_schema
24 |
25 |
26 | class StandardResultsSetPagination(AngularDjangoPageNumberPagination):
27 | page_size = 10
28 | page_size_query_param = 'page_size'
29 | max_page_size = 100
30 |
--------------------------------------------------------------------------------
/src/django/angular_django/rest_framework.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | from itertools import chain
3 |
4 | from django.conf import settings
5 | from django.urls import URLResolver, URLPattern
6 | from django.utils.module_loading import import_string
7 | from rest_framework.serializers import ModelSerializer
8 | from rest_framework.views import APIView
9 | from rest_framework.viewsets import GenericViewSet
10 |
11 |
12 | def get_views(resolv, pattern=()):
13 | if isinstance(resolv, URLResolver):
14 | for item in resolv.url_patterns:
15 | for sub in get_views(item, pattern + (resolv.pattern,)):
16 | yield sub
17 | elif isinstance(resolv, URLPattern):
18 | try:
19 | mod = import_string(resolv.lookup_str)
20 | except ModuleNotFoundError:
21 | pass
22 | else:
23 | yield mod, pattern + (resolv.pattern,)
24 |
25 |
26 | def get_api_views():
27 | patterns = getattr(import_string(settings.ROOT_URLCONF), 'urlpatterns')
28 | views = set(list(chain(*[get_views(pattern) for pattern in patterns])))
29 | views = [view for view in views
30 | if inspect.isclass(view[0]) and issubclass(view[0], GenericViewSet) and \
31 | issubclass(view[0].serializer_class, ModelSerializer) and
32 | '(?P[a-z0-9]+)' not in view[1][-1]._regex and \
33 | '(?P[^/.]+)' not in view[1][-1]._regex]
34 | return views
35 |
--------------------------------------------------------------------------------
/src/django/angular_django/typescript.py:
--------------------------------------------------------------------------------
1 | from string import Template
2 |
3 | from django.contrib.contenttypes.models import ContentType
4 | from django.db import ProgrammingError
5 | from rest_framework import serializers
6 | from rest_framework.serializers import SerializerMetaclass
7 |
8 | FIELD_TPL = """\
9 | @Field() $name: $type;\
10 | """
11 |
12 | TPL = """
13 | ///////////////////////////////////////
14 | // ${model} API
15 | ///////////////////////////////////////
16 | export class ${model} extends SerializerService {
17 | $fields
18 | }
19 |
20 | @Injectable({
21 | providedIn: 'root'
22 | })
23 | export class ${model}Api extends ApiService {
24 |
25 | url = '${url}';
26 | serializer = ${model};
27 | contentType = '${content_type}';
28 |
29 | constructor(http: HttpClient) {
30 | super(http);
31 | }
32 | }
33 | """
34 |
35 | TS_TYPES = {
36 | serializers.CharField: 'string',
37 | serializers.EmailField: 'string',
38 | serializers.IPAddressField: 'string',
39 | serializers.DateField: 'Date',
40 | serializers.DateTimeField: 'Date',
41 | serializers.TimeField: 'string',
42 | serializers.DurationField: 'string',
43 | serializers.BooleanField: 'boolean',
44 | serializers.NullBooleanField: 'boolean',
45 | serializers.URLField: 'string',
46 | serializers.HyperlinkedIdentityField: 'string',
47 | serializers.HyperlinkedRelatedField: 'string',
48 | serializers.ChoiceField: 'string',
49 | serializers.UUIDField: 'string',
50 | serializers.SlugField: 'string',
51 | serializers.IntegerField: 'number',
52 | serializers.DecimalField: 'number',
53 | serializers.FloatField: 'number',
54 | }
55 |
56 |
57 | def get_field(name, field):
58 | cls = field.__class__
59 | many = False
60 | if cls == serializers.ManyRelatedField:
61 | many = True
62 | cls = field.child_relation.__class__
63 | type_ = TS_TYPES.get(cls, 'any')
64 | if isinstance(cls, SerializerMetaclass):
65 | type_ = cls.Meta.model._meta.object_name
66 | if many:
67 | type_ += '[]'
68 | return Template(FIELD_TPL).substitute(name=name, type=type_)
69 |
70 |
71 | def get_route_path(route):
72 | if hasattr(route, '_route'):
73 | route = route._route
74 | elif hasattr(route, '_regex'):
75 | route = route._regex
76 | else:
77 | raise ProgrammingError('Route is not available on {} object'.format(route))
78 | return route.lstrip('^').rstrip('$')
79 |
80 |
81 | def tpl(view, pattern):
82 | serializer = view.serializer_class()
83 | model = serializer.Meta.model
84 | model_name = model._meta.object_name
85 | url = '/' + ''.join([get_route_path(part) for part in pattern])
86 | fields = [get_field(name, field) for name, field in serializer.get_fields().items()]
87 | return Template(TPL).substitute(url=url, model=model_name, fields='\n'.join(fields),
88 | content_type='_'.join(ContentType.objects.get_for_model(model).natural_key()))
89 |
--------------------------------------------------------------------------------