EmilStenstrom / django-components

Create simple reusable template components in Django.
MIT License
973 stars 62 forks source link

Automatically look for CSS and JS files in component directory #92

Closed EmilStenstrom closed 1 year ago

EmilStenstrom commented 2 years ago

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.

hanifbirgani commented 2 years ago

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):

  1. if there are no Media in component attrs (default behavior) → system doesn't look for anything.
  2. else (there is a Media in component attrs): 2.1. if css/js attributes defined in the Media → system look exactly for user-specified filenames in the my-component directory. For example: my-component/could-be-anything.css 2.2. if autoload = True defined in class Media → system looks for my-component.css and my-component.js in the my-component directory. The user doesn't need to specify any filename. The autoload flag is enough in this case.
EmilStenstrom commented 2 years ago

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.

EmilStenstrom commented 2 years ago

@hanifbirgani Would you be open to working at this? :)

hanifbirgani commented 2 years ago

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?

EmilStenstrom commented 2 years ago

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?

hanifbirgani commented 2 years ago

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.

EmilStenstrom commented 2 years ago

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?

hanifbirgani commented 2 years ago

I like the idea of a minimal boilerplate, and I agree 👍

EmilStenstrom commented 2 years ago

@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.

hanifbirgani commented 2 years ago

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?

EmilStenstrom commented 2 years ago

Fully understand.

Yes, that’s what I think we need to do. 1) add a default directory for components, and figure out backwards compatibility. 2) add a CLI command to generate it

hanifbirgani commented 2 years ago

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.

EmilStenstrom commented 2 years ago

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]"

simkimsia commented 1 year ago

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

EmilStenstrom commented 1 year ago

@simkimsia Almost! The only thing missing now is a management command that generates the structure from the README! Will simplify setup even more.

EmilStenstrom commented 1 year ago

This is being worked on here: https://github.com/EmilStenstrom/django-components/pull/175

EmilStenstrom commented 1 year ago

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