Skip to content

Commit 6f49037

Browse files
authored
Rework view_to_component (#98)
Rework view_to_component to prevent needing to constantly pass in frequently used parameters.
1 parent 276f962 commit 6f49037

File tree

8 files changed

+273
-181
lines changed

8 files changed

+273
-181
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ Using the following categories, list your changes in this order:
2828

2929
### Changed
3030

31+
- `view_to_component` now returns a `Callable`, instead of directly returning a `Component`. Check the docs for new usage info.
3132
- `use_mutation` and `use_query` will now log any query failures.
3233

3334
### Fixed
3435

3536
- Allow `use_mutation` to have `refetch=None`, as the docs suggest is possible.
3637
- `use_query` will now prefetch all fields to prevent `SynchronousOnlyOperation` exceptions.
38+
- `view_to_component`, `django_css`, and `django_js` type hints will now display like normal functions.
3739

3840
## [1.2.0] - 2022-09-19
3941

docs/includes/examples.md

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,3 @@
1-
<!--hello-world-view-start-->
2-
3-
```python linenums="1"
4-
from django.http import HttpResponse
5-
6-
def hello_world_view(request, *args, **kwargs):
7-
return HttpResponse("Hello World!")
8-
```
9-
10-
<!--hello-world-view-end-->
11-
12-
<!--hello-world-cbv-start-->
13-
14-
```python linenums="1"
15-
from django.http import HttpResponse
16-
from django.views import View
17-
18-
class HelloWorldView(View):
19-
def get(self, request, *args, **kwargs):
20-
return HttpResponse("Hello World!")
21-
```
22-
23-
<!--hello-world-cbv-end-->
24-
251
<!--todo-model-start-->
262

273
```python linenums="1"

docs/src/features/components.md

Lines changed: 101 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,115 +10,170 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
1010

1111
```python linenums="1"
1212
from idom import component, html
13+
from django.http import HttpResponse
1314
from django_idom.components import view_to_component
14-
from .views import hello_world_view
15+
16+
@view_to_component
17+
def hello_world_view(request):
18+
return HttpResponse("Hello World!")
1519

1620
@component
1721
def my_component():
1822
return html.div(
19-
view_to_component(hello_world_view),
23+
hello_world_view(),
2024
)
2125
```
2226

23-
=== "views.py"
24-
25-
{% include-markdown "../../includes/examples.md" start="<!--hello-world-view-start-->" end="<!--hello-world-view-end-->" %}
26-
2727
??? example "See Interface"
2828

2929
<font size="4">**Parameters**</font>
3030

3131
| Name | Type | Description | Default |
3232
| --- | --- | --- | --- |
3333
| view | `Callable | View` | The view function or class to convert. | N/A |
34-
| compatibility | `bool` | If True, the component will be rendered in an iframe. When using compatibility mode `tranforms`, `strict_parsing`, and `request` arguments will be ignored. | `False` |
35-
| transforms | `Iterable[Callable[[VdomDict], Any]]` | A list of functions that transforms the newly generated VDOM. The functions will be called on each VDOM node. | `tuple` |
34+
| compatibility | `bool` | If True, the component will be rendered in an iframe. When using compatibility mode `tranforms`, `strict_parsing`, `request`, `args`, and `kwargs` arguments will be ignored. | `False` |
35+
| transforms | `Sequence[Callable[[VdomDict], Any]]` | A list of functions that transforms the newly generated VDOM. The functions will be called on each VDOM node. | `tuple` |
3636
| strict_parsing | `bool` | If True, an exception will be generated if the HTML does not perfectly adhere to HTML5. | `True` |
37-
| request | `HttpRequest | None` | Request object to provide to the view. | `None` |
38-
| args | `Iterable` | The positional arguments to pass to the view. | `tuple` |
39-
| kwargs | `Dict | None` | The keyword arguments to pass to the view. | `None` |
4037

4138
<font size="4">**Returns**</font>
4239

4340
| Type | Description |
4441
| --- | --- |
45-
| `Component` | An IDOM component. |
46-
| `None` | No component render. |
42+
| `_ViewComponentConstructor` | A function that takes `request: HttpRequest | None, *args: Any, **kwargs: Any` and returns an IDOM component. |
43+
44+
??? warning "Existing limitations"
45+
46+
There are currently several limitations of using `view_to_component` that may be resolved in a future version of `django_idom`.
47+
48+
- Requires manual intervention to change request methods beyond `GET`.
49+
- Does not currently load any HTML contained with a `<head>` tag
50+
- Has no option to automatically intercept local anchor link (ex. `#!html <a href='example/'></a>`) click events
51+
52+
_Please note these limitations do not exist when using `compatibility` mode._
4753

4854
??? question "How do I use this for Class Based Views?"
4955

50-
You can simply pass your Class Based View directly into this function.
56+
You can simply pass your Class Based View directly into `view_to_component`.
5157

5258
=== "components.py"
5359

5460
```python linenums="1"
5561
from idom import component, html
62+
from django.http import HttpResponse
63+
from django.views import View
5664
from django_idom.components import view_to_component
57-
from .views import HelloWorldView
65+
66+
@view_to_component
67+
class HelloWorldView(View):
68+
def get(self, request):
69+
return HttpResponse("Hello World!")
5870

5971
@component
6072
def my_component():
6173
return html.div(
62-
view_to_component(HelloWorldView),
74+
HelloWorldView(),
6375
)
6476
```
6577

66-
=== "views.py"
78+
??? question "How do I transform views from external libraries?"
79+
80+
=== "components.py"
6781

68-
{% include-markdown "../../includes/examples.md" start="<!--hello-world-cbv-start-->" end="<!--hello-world-cbv-end-->" %}
82+
In order to convert external views, you can utilize `view_to_component` as a function, rather than a decorator.
6983

70-
??? question "How do I pass arguments into the view?"
84+
```python linenums="1"
85+
from idom import component, html
86+
from django.http import HttpResponse
87+
from django_idom.components import view_to_component
88+
from some_library.views import example_view
7189

72-
You can use the `args` and `kwargs` parameters to pass arguments to the view.
90+
converted_view = view_to_component(example_view)
91+
92+
@component
93+
def my_component():
94+
return html.div(
95+
converted_view(),
96+
)
97+
```
98+
99+
??? question "How do I provide `args` and `kwargs` to a view?"
100+
101+
You can use the `args` and `kwargs` parameters to provide positional and keyworded arguments to a view.
73102

74103
=== "components.py"
75104

76105
```python linenums="1"
77106
from idom import component, html
107+
from django.http import HttpResponse
78108
from django_idom.components import view_to_component
79-
from .views import hello_world_view
109+
110+
@view_to_component
111+
def hello_world_view(request, arg1, arg2, key1=None, key2=None):
112+
return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}")
80113

81114
@component
82115
def my_component():
83116
return html.div(
84-
view_to_component(
85-
hello_world_view,
117+
hello_world_view(
86118
args=["value_1", "value_2"],
87-
kwargs={"key_1": "value_1", "key_2": "value_2"},
119+
kwargs={"key1": "abc", "key2": "123"},
88120
),
89121
)
90122
```
91123

92-
=== "views.py"
124+
??? question "How do I provide a custom `request` object to a view?"
93125

94-
{% include-markdown "../../includes/examples.md" start="<!--hello-world-view-start-->" end="<!--hello-world-view-end-->" %}
126+
You can use the `request` parameter to provide the view a custom request object.
127+
128+
=== "components.py"
129+
130+
```python linenums="1"
131+
from idom import component, html
132+
from django.http import HttpResponse, HttpRequest
133+
from django_idom.components import view_to_component
134+
135+
example_request = HttpRequest()
136+
example_request.method = "PUT"
137+
138+
@view_to_component
139+
def hello_world_view(request):
140+
return HttpResponse(f"Hello World! {request.method}")
141+
142+
@component
143+
def my_component():
144+
return html.div(
145+
hello_world_view(
146+
request=example_request,
147+
),
148+
)
149+
```
95150

96151
??? question "What is `compatibility` mode?"
97152

98153
For views that rely on HTTP responses other than `GET` (such as `PUT`, `POST`, `PATCH`, etc), you should consider using compatibility mode to render your view within an iframe.
99154

100-
Any view can be rendered within compatibility mode. However, the `transforms`, `strict_parsing`, and `request` arguments do not apply to compatibility mode.
155+
Any view can be rendered within compatibility mode. However, the `transforms`, `strict_parsing`, `request`, `args`, and `kwargs` arguments do not apply to compatibility mode.
101156

102157
Please note that by default the iframe is unstyled, and thus won't look pretty until you add some CSS.
103158

104159
=== "components.py"
105160

106161
```python linenums="1"
107162
from idom import component, html
163+
from django.http import HttpResponse
108164
from django_idom.components import view_to_component
109-
from .views import hello_world_view
165+
166+
@view_to_component(compatibility=True)
167+
def hello_world_view(request):
168+
return HttpResponse("Hello World!")
110169

111170
@component
112171
def my_component():
113172
return html.div(
114-
view_to_component(hello_world_view, compatibility=True),
173+
hello_world_view(),
115174
)
116175
```
117176

118-
=== "views.py"
119-
120-
{% include-markdown "../../includes/examples.md" start="<!--hello-world-view-start-->" end="<!--hello-world-view-end-->" %}
121-
122177
??? question "What is `strict_parsing`?"
123178

124179
By default, an exception will be generated if your view's HTML does not perfectly adhere to HTML5.
@@ -131,20 +186,20 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
131186

132187
```python linenums="1"
133188
from idom import component, html
189+
from django.http import HttpResponse
134190
from django_idom.components import view_to_component
135-
from .views import hello_world_view
191+
192+
@view_to_component(strict_parsing=False)
193+
def hello_world_view(request):
194+
return HttpResponse("<my-tag> Hello World </my-tag>")
136195

137196
@component
138197
def my_component():
139198
return html.div(
140-
view_to_component(hello_world_view, strict_parsing=False),
199+
hello_world_view(),
141200
)
142201
```
143202

144-
=== "views.py"
145-
146-
{% include-markdown "../../includes/examples.md" start="<!--hello-world-view-start-->" end="<!--hello-world-view-end-->" %}
147-
148203
Note that best-fit parsing is very similar to how web browsers will handle broken HTML.
149204

150205
??? question "What is `transforms`?"
@@ -159,32 +214,25 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
159214

160215
```python linenums="1"
161216
from idom import component, html
217+
from django.http import HttpResponse
162218
from django_idom.components import view_to_component
163-
from .views import hello_world_view
164219

165220
def example_transform(vdom):
166221
attributes = vdom.get("attributes")
167-
168222
if attributes and attributes.get("id") == "hello-world":
169223
vdom["children"][0] = "Good Bye World!"
170224

225+
@view_to_component(transforms=[example_transform])
226+
def hello_world_view(request):
227+
return HttpResponse("<div id='hello-world'> Hello World! <div>")
228+
171229
@component
172230
def my_component():
173-
return view_to_component(
174-
hello_world_view,
175-
transforms=[example_transform],
231+
return html.div(
232+
hello_world_view(),
176233
)
177234
```
178235

179-
=== "views.py"
180-
181-
```python linenums="1"
182-
from django.http import HttpResponse
183-
184-
def hello_world_view(request, *args, **kwargs):
185-
return HttpResponse("<div id='hello-world'> Hello World! <div>")
186-
```
187-
188236
## Django CSS
189237

190238
Allows you to defer loading a CSS stylesheet until a component begins rendering. This stylesheet must be stored within [Django's static files](https://docs.djangoproject.com/en/dev/howto/static-files/).

0 commit comments

Comments
 (0)