django-components
django-components copied to clipboard
Automatically look for CSS and JS files in component directory
In the default example in the README this Media definition is used:
class Media:
css = '[your app]/components/calendar/calendar.css'
js = '[your app]/components/calendar/calendar.js'
I would like to simplify this to this:
class Media:
css = 'calendar/calendar.css'
js = 'calendar/calendar.js'
... so that it matches the name template_name paths. CSS and JS should still be loaded from the same directory.
IMO the media defining flow could be more simplified. I suggest that the media handler should never look outside of the component directory. The handler could run 3 condition checks to include media files as follow (assume we have a component named my-component
):
- if there are no
Media
in component attrs (default behavior) → system doesn't look for anything. - else (there is a
Media
in component attrs): 2.1. if css/js attributes defined in theMedia
→ system look exactly for user-specified filenames in themy-component
directory. For example:my-component/could-be-anything.css
2.2. ifautoload = True
defined inclass Media
→ system looks formy-component.css
andmy-component.js
in themy-component
directory. The user doesn't need to specify any filename. Theautoload
flag is enough in this case.
I think I agree with you here, that things can be simplified quite a bit.
I'm a little weary of the autoload feature, because then as a reading of someone else's component code, you have to know what autoload does. When two paths are specified like they are today, that indirectly explains when the component does: Brings together python code with html, CSS, and JS. Also, when searching a whole Django project nothing will show up for i.e. "calendar.css", which again requires knowledge how components works. It doesn't save that many keystrokes either. Do we really need it?
The last thing I'm not sure about is how other asset bundling libraries work with Form Media (which we indirectly use by inheriting from MediaDefiningClass). I guess there should be some test that ensures this continues working, but I don't think we have that yet.
@hanifbirgani Would you be open to working at this? :)
Sure, I should somehow change my mindset on this problem.
IMO a component should contain its assets in one place (a directory). So the back-end logic (.py
), front-end logic (.js
), template (.html
), and style (.css
) files should be in a directory named the-component-name
. In this case, everyone knows where to look for the component's related files. It makes the developer experience smoother than the current workflow.
my-component/
├─ my-component.css
├─ my-component.html
├─ my-component.js
└─ my-component.py
What do you think about this structure?
Sounds like a great structure!
The only thing I tried to argue above was that I think that the my-component.py-file should have a reference to my-component.css/html/css in them somewhere, so they get found when you search for "my-component.css" to see where a file is used. It makes the reference between the files explicit, and also allows people to specify other paths to files if they need to. Does that make sense?
I believe encapsulation and independence are essential in component structure design, and there should not be any external dependency in a component. So if a developer wanted to use the component, they could copy the component directory, and they know that there is nothing left to worry about.
To reference the static files inside the code, we can use comments in the py file. We also can make a command-line utility (manage.py extension) to make a boilerplate component directory structure for developers to make it easy for them to create new components with batteries included.
Great points. I agree about encouraging encapsulation and not referencing files outside of the component directory.
Let's try another angle. What about encouraging people to split their CSS and JS files into multiple files? This is supported today by just saying class Media: css = ["file1.css", "file2.css"]. If the default is to have no Media class there is a lot of work to add another file.
Another take to minimize the boilerplate: Let's make the component directory the base path when looking for supporting files. So this would be the boilerplate needed:
from django_components import component
@component.register("calendar")
class Calendar(component.Component):
template_name = "calendar.html"
class Media:
css = 'calendar.css'
js = 'calendar.js'
I think this makes a lot of sense, because it answers the question "What is a component?": A python class that ties together the html, css and js into one reusable building block. I really like the self documenting nature of this.
What are your thoughts?
I like the idea of a minimal boilerplate, and I agree 👍
@hanifbirgani Did you have any time to think more about this? I really like these kind of simplifying PRs that slowly chip away at the boilerplate needed. Feels like we are approaching the core of what a component really is.
Deadlines have hammered me in the past months!
I suppose our core code works well with the proposed file structure, so we need to make the boilerplate and the management command, right?
Fully understand.
Yes, that’s what I think we need to do.
- add a default directory for components, and figure out backwards compatibility.
- add a CLI command to generate it
The system-wide components directory could be components
, like what we already have in the Django templates
, and I guess it doesn't break the compatibility.
Yes, I was thinking about automatically looking in a subdirectory with the same name as the component you registered when resolving the paths to component CSS and JS. So instead of writing "calendar/calendar.css", you could just do "calendar.css" and automatically get the same result. I think it would be a nice way to enforce putting each component in a separate directory like you suggested in the beginning of this thread. So the default path would be "[system-wide components]/[component-name]/[the path given]"
Isn't this solved by the latest v0.21 changes and also by what i raised in the discussion? https://github.com/EmilStenstrom/django-components/discussions/160
@simkimsia Almost! The only thing missing now is a management command that generates the structure from the README! Will simplify setup even more.
This is being worked on here: https://github.com/EmilStenstrom/django-components/pull/175
Since the original need here is solved, let's work on the management command in a separate issue: https://github.com/EmilStenstrom/django-components/issues/249