Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thank you for library, I have some questions #13

Open
hunterinious opened this issue Sep 12, 2020 · 4 comments
Open

Thank you for library, I have some questions #13

hunterinious opened this issue Sep 12, 2020 · 4 comments

Comments

@hunterinious
Copy link

Hello Eddy, thank you for the library!

I am not a very experienced developer and I have several questions and problems with the library:

  1. Is it possible to render a component by link? I need this for the following case: there is a main form, and some of its fields are list of objects of another model(many-to-many relationship), I need to asynchronously add an object to this list through another form that is displayed in a new browser tab and then re-render the main form. I tried to do it in different ways, but I constantly ran into errors or nothing worked. If it is not possible to render component by link, how can I implement this functionality? If you give an example of the code, it will be generally great, but if you describe it in words, then it will be well too.
  2. send_redirect method takes url as django url name or it is string?
  3. I ran into the problem that when sending an event, the component is unmounted for some reason. What is the reason for this?

Thanks again for work you've done!

Waiting for your reply

@edelvalle
Copy link
Owner

Hi, all good! let's see...

  1. You cold create a view that just renders a single component and launch it, if you are using django channels with redis and the auto-broadcast feature of reactor, you should be able to do this. When you enable auto-broadcast (https://github.com/edelvalle/reactor#settings) reactor will watch changes in the models using model signals and will broadcast a message to django channels, like in:
class User:
   pass

class Boat:
   users = models.ManyToManyField(User, related_name='boats')

if you have a user with ID 1, associated to a boat with id 2....

u = User.objects.get(id=1)
u.save()

Will trigger the following messages, depending on how did you configure auto-broadcast:

  • user, meaning: a change was made to a user
  • user.1, meaning: a change was made to user with id 1
  • boat.2.users, meaning: a user of boat with id 2 was changed

In the code of your component you can do self.subscrube(f"boat.{boat.id}.users"), and that will trigger a refresh in your component when a message on that channel is issued, that refresh causes the component to run the mount method again, refreshing it's state from the database.

**I think I need to write proper documentation about this and the live cycles of the component.

  1. send_redirect uses from django.shortcuts import resolve_url to resolve the URL so you can pass almost anything to it, it uses the same logic as https://docs.djangoproject.com/en/3.1/topics/http/shortcuts/#redirect

  2. Is hard to tell what's going on without specifics, but it could be that your component rendered it self empty or a destroy message was issued.

I will use your feedback to improve the documentation and as always, sorry for my broken English.

Happy hacking!

@hunterinious
Copy link
Author

hunterinious commented Sep 15, 2020

Hello Eddy, sorry for late response.

  1. Thank you for the explanation about subscribe, but with the rendering of the component it is still not clear, maybe I didn't even fully understand your answer. I should fully describe what I want to do and provide some of my code:

So, i have psychologists_list.html in which there are links to the views of changing objects of psychologists:

 <p>
     <a href="{% url 'psy-user-profile-update' psy.id %}">{{ psy.username }}</a>
 </p>

Then in psy_user_profile_update.html i render component which contains a formset:

<form method="post" enctype="multipart/form-data">{% csrf_token %}
     <div class="form-group">
         {{ form.email|as_crispy_field }}
     </div>
     ...
     {{ profile.management_form }}

     {% for form in profile.forms %}
        {% component 'dynamic-component' form=form profile=user.profile %}
     {% endfor %}
</form>

And psy_user_profile_update_dynamic.html which is a component and contains statuses form field, in fact, I want to add these statuses asynchronously by following link:

<div class="form-group">
    {{ form.statuses|as_crispy_field }}
</div>

<a href="{% url 'psy-status-create'%}" target="_blank" class="non-color-link">
    <i class="fa fa-plus-square"></i>
</a>

**
I tried to put both forms in a template like this:

{% component 'dynamic-component' form=form profile_forms=profile.forms profile=user.profile %}

But then when I try to generate a token in the component template {% csrf_token %}, the token is not generated, I don't understand the reason for this.
**

**
As I understand it, the only way to render the component and pass the state to it is:

{% component 'component-name' state=state %}

But, I tried to do something like your example with to-do:

<div is="dymanic-component" id="{{ this.id }}" state="{{ this.serialize|tojson }}">

I thought that this is how you can use a component and render it implicitly, but ran into a problem how to pass the state correctly, tried to do it differently:

state="{{ statuses: profile.statuses }}",
state="{{ statuses=profile.statuses }}",
state="{{ statuses=profile.statuses|tojson }}"

and so on)
**

Finally the code of my component:

from psychologists.models import PsychologistStatus, PsychologistUserProfile
from reactor.component import Component

class DynamicComponent(Component):
    template_name = 'cadmin/psychologists/psy_user_profile_update_dynamic.html'

    def mount(self, form, profile, **kwargs):
        self.form = form
        self.profile = profile
        self.subscribe(f"psychologistuserprofile.{profile.id}.statuses")

    def serialize(self):
        return dict(
            form=self.form,
            profile=self.profile,
        )

Also added to settings:

AUTO_BROADCAST = {
    # model_a
    # model_a.del
    # model_a.new
    'MODEL': True,

    # model_a.1234
    'MODEL_PK': True,

    # model_b.1234.model_a_set
    # model_b.1234.model_a_set.new
    # model_b.1234.model_a_set.del
    'RELATED': True,

    # model_b.1234.model_a_set
    # model_a.1234.model_b_set
    'M2M': True,
}

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}

I may have missed something or don't understand it, but it doesn't work yet(statuses are not updated after adding).

  1. Understandably
  2. Yes, the problem was just in the empty state.

**I think I need to write proper documentation about this and the live cycles of the component.

It will be perfectly and solve misunderstandings.

Sorry that I did not immediately describe all the problems I encountered.
And also excuse me for my English.

@edelvalle
Copy link
Owner

First of all, I need to write proper documentation and examples, that's for sure, and that's what you are missing here.

From a quick look the serialize method needs to return also the id... if you do not return the id of the component it will go missing from the interface because reactor will not be able to find it.

The data returned by serialize is the sate of the component and gets serialized to JSON, and is passed back to mount when the components connects to the back-end, so make sure to not use objects that are not JSON serializable.

Also instead of: <div is="dymanic-component" id="{{ this.id }}" state="{{ this.serialize|tojson }}"> you can do: <div {{ header }}>

@hunterinious
Copy link
Author

Thank you for the quick response!

so make sure to not use objects that are not JSON serializable.

I missed {{ header }} and now I see that neither the form nor the form.statuses can be serialized. But in any case, I need to somehow transfer form to the component in order to display it in the component template, since I cannot do something like this in psy_user_profile_update_dynamic.html:

{% block body %}
   {% component 'dynamic-component' %}
{% endblock body %}

This will lead to endless recursion.

First of all, I need to write proper documentation and examples, that's for sure, and that's what you are missing here.

Ok, I'll wait for your examples, thank you in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants