mirror of
https://github.com/django-components/django-components.git
synced 2025-07-14 04:14:58 +00:00

* Fix issue with missing variables by binding template to context in Component.render method. * Pass extra_context to Component.render() Co-authored-by: rbeard0330 <@dul2k3BKW6m>
100 lines
3.3 KiB
Python
100 lines
3.3 KiB
Python
from django.forms.widgets import MediaDefiningClass
|
|
from django.template import Context
|
|
from django.template.base import NodeList, TextNode
|
|
from django.template.loader import get_template
|
|
from django.utils.safestring import mark_safe
|
|
from six import with_metaclass
|
|
|
|
# Allow "component.AlreadyRegistered" instead of having to import these everywhere
|
|
from django_components.component_registry import AlreadyRegistered, ComponentRegistry, NotRegistered # noqa
|
|
|
|
# Python 2 compatibility
|
|
try:
|
|
from inspect import getfullargspec
|
|
except ImportError:
|
|
from inspect import getargspec as getfullargspec
|
|
|
|
# Django < 2.1 compatibility
|
|
try:
|
|
from django.template.base import TokenType
|
|
except ImportError:
|
|
from django.template.base import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
|
|
|
|
class TokenType:
|
|
TEXT = TOKEN_TEXT
|
|
VAR = TOKEN_VAR
|
|
BLOCK = TOKEN_BLOCK
|
|
|
|
|
|
class Component(with_metaclass(MediaDefiningClass)):
|
|
def context(self):
|
|
return {}
|
|
|
|
def template(self, context):
|
|
raise NotImplementedError("Missing template() method on component")
|
|
|
|
def render_dependencies(self):
|
|
"""Helper function to access media.render()"""
|
|
|
|
return self.media.render()
|
|
|
|
def render_css_dependencies(self):
|
|
"""Render only CSS dependencies available in the media class."""
|
|
|
|
return mark_safe("\n".join(self.media.render_css()))
|
|
|
|
def render_js_dependencies(self):
|
|
"""Render only JS dependencies available in the media class."""
|
|
|
|
return mark_safe("\n".join(self.media.render_js()))
|
|
|
|
def slots_in_template(self, template):
|
|
nodelist = NodeList()
|
|
for node in template.template.nodelist:
|
|
if (
|
|
node.token.token_type == TokenType.BLOCK
|
|
and node.token.split_contents()[0] == "slot"
|
|
):
|
|
nodelist.append(node)
|
|
|
|
return nodelist
|
|
|
|
def render(self, slots_filled=None, *args, **kwargs):
|
|
slots_filled = slots_filled or []
|
|
context_args_variables = getfullargspec(self.context).args[1:]
|
|
context_args = {
|
|
key: kwargs[key] for key in context_args_variables if key in kwargs
|
|
}
|
|
context = self.context(**context_args)
|
|
template = get_template(self.template(context))
|
|
slots_in_template = self.slots_in_template(template)
|
|
|
|
if slots_in_template:
|
|
valid_slot_names = set([slot.name for slot in slots_in_template])
|
|
nodelist = NodeList()
|
|
for node in template.template.nodelist:
|
|
if (
|
|
node.token.token_type == TokenType.BLOCK
|
|
and node.token.split_contents()[0] == "slot"
|
|
):
|
|
if node.name in valid_slot_names and node.name in slots_filled:
|
|
nodelist.append(TextNode(slots_filled[node.name]))
|
|
else:
|
|
for node in node.nodelist:
|
|
nodelist.append(node)
|
|
else:
|
|
nodelist.append(node)
|
|
|
|
render_context = Context(context)
|
|
with render_context.bind_template(template.template):
|
|
return nodelist.render(render_context)
|
|
|
|
return template.render(context)
|
|
|
|
class Media:
|
|
css = {}
|
|
js = []
|
|
|
|
|
|
# This variable represents the global component registry
|
|
registry = ComponentRegistry()
|