├── .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 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 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 | Angular Django 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 |
2 | 3 | 4 |

Angular Django

5 |
6 | 7 | 9 | 11 | 12 | Installation 13 | First steps 14 | API guide 15 | Retrieve api service 16 | List api service 17 | Create or update 18 | Material 19 | 20 | 21 | 22 | 23 |
24 | 25 |
26 |
27 |
28 |
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 |
2 | 3 | 4 | 5 |
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 |
2 | 3 | 4 | 5 |
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 | Angular Django 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 |
48 | 49 | 50 |
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 | 12 | 13 | 14 | 18 | 19 |
20 | {{ row.generation.identifier }} 21 |
22 |
23 | 24 |
25 | {{ row|getDisplay:'color'|async }} 26 |
27 |
28 |
29 |
30 |
31 | 32 | {{ selection.selected.length }} elements selected 33 | 34 | 1 element selected 35 | 37 | select all {{ resultsCount }} 38 | 39 | 40 | All {{ resultsCount }} selected 41 | 42 | 43 | Clear selection 44 | 45 |
46 | 48 |
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 | 6 | 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 | 8 | Page {{ page.currentPage }} 9 | 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 | 12 | 13 | 14 | 18 | 19 |
20 | {{ row.generation.identifier }} 21 |
22 |
23 | 24 |
25 | {{ row|getDisplay:'color'|async }} 26 |
27 |
28 |
29 |
30 |
31 | 32 | {{ selection.selected.length }} elements selected 33 | 34 | 1 element selected 35 | 37 | select all {{ resultsCount }} 38 | 39 | 40 | All {{ resultsCount }} selected 41 | 42 | 43 | Clear selection 44 | 45 |
46 | 48 |
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 | Angular Django 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 | --------------------------------------------------------------------------------