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

Allow to edit server meta context on SSR #2856

Open
mondeja opened this issue Aug 18, 2024 · 9 comments
Open

Allow to edit server meta context on SSR #2856

mondeja opened this issue Aug 18, 2024 · 9 comments

Comments

@mondeja
Copy link
Contributor

mondeja commented Aug 18, 2024

Is your feature request related to a problem? Please describe.

This problem affects the two internationalization projects that currently exist in Leptos (leptos_i18n and leptos-fluent). Nowadays we are using the next dirty hack to set the lang (and in the case of leptos-fluent, the dir also) attribute(s) on SSR with Leptos v0.6:

leptos_meta::Html(
    leptos_meta::HtmlProps {
         lang: ...,
         dir: ...,
         class: ...,
         attributes: ...,
    }
);

Of course, the above code is a simplification. On leptos-fluent, I'm parsing the result of leptos_meta::use_head().html.as_string() to keep the previous values of lang, dir and class, altough attributes is not possible because is a private field. These values are possibly previously set by a previous invocation of an <Html/> component in the tree.

Describe the solution you'd like

I want that use_head() would return ServerMetaContext instead of MetaContext on SSR. These two types would need a refactor because are only exposing certain things when not on SSR, which IMO is really strange. And then, probably build an API to allow something like:

#[cfg(features = "ssr")]
{
    let context = use_head();
    let mut attrs = context.html().attributes();
    // ... manipulate the attributes
    context.html.set_attributes(attrs);
}

Describe alternatives you've considered

There is no alternatives currently.

Additional context

This is a blocking issue for the migration to Leptos v0.7. Note that a Provider implementation as suggested in #2780 is not possible because if an user wants to provide their own <Html/> before or after in the component tree they can't, we would be forcing users to give to our libraries (leptos-fluent and leptos_i18n) the ability to set their own <Html/> components in their code.

FYI @Baptistemontan

@gbj
Copy link
Collaborator

gbj commented Aug 18, 2024

I want to connect the additional discussion here: #2780

if an user wants to provide their own <Html/> before or after in the component tree they can't

I'm not sure what you mean. Multiple <Html/> components work together fine:

#[component]
pub fn App() -> impl IntoView {
    // Provides context that manages stylesheets, titles, meta tags, etc.
    provide_meta_context();

    view! {
        <Html attr:class="some-user-html-class"/>
        <I18n>
            <HomePage/>
        </I18n>
    }
}

#[component]
fn I18n(children: Children) -> impl IntoView {
    view! {
        <Html attr:lang="es"/>
        {children()}
    }
}

/// Renders the home page of your application.
#[component]
fn HomePage() -> impl IntoView {
    view! {
        <Html attr:id="bar"/>
        <h1>"Welcome to Leptos!"</h1>
    }
}

I do not understand why your current approach requires parsing the HTML in the way you're describing. Is the issue that you do not want to override a lang attribute set by the user with a lang attribute set by the internationalization library?

@mondeja
Copy link
Contributor Author

mondeja commented Aug 18, 2024

Is the issue that you do not want to override a lang attribute set by the user with a lang attribute set by the internationalization library?

The issue is that currently I don't want to override other attributes that are not lang neither dir. But, for sure, if the user has provided a <Html lang=.../> and/or <Html dir=.../> attribute, I at least want to display a warning on #[cfg(debug_assertions)] advicing that leptos-fluent will override these attributes.

From the conversation at #2780:

I would suggest that rather than an SsrHtmlTag component you have something like a FluentProvider component, which takes children and uses the Html component, then evaluates the children. This is a pretty typical pattern in component-based frameworks, and will ensure that modifying the <html> tag works as expected on any platform, rather than only using SSR.

I'm still very interested to know why the framework is providing functions like provide_meta_context and provide_context but library authors are forced to use a <Provider/>, even when is not clear that resolves all the problems that a programmatic API would.

@Baptistemontan
Copy link
Contributor

Baptistemontan commented Aug 18, 2024

Thanks for the tag @mondeja!

I too would be interested in something like this, it would be great to be able to do

let head = use_head();

let on_click = move || head.set_lang("en-GB");

Without having to deal with the <Html /> component, that in itself does'nt render to a view anyway.

@gbj
Copy link
Collaborator

gbj commented Aug 18, 2024

The issue is that currently I don't want to override other attributes that are not lang neither dir.

See the example above. Using an <Html/> component to set lang and dir will not override user-set attributes that are not lang or dir.

I'm still very interested to know why the framework is providing functions like provide_meta_context and provide_context but library authors are forced to use a <Provider/>, even when is not clear that resolves all the problems that a programmatic API would.

Library authors are not forced to use a <Provider/>. I believe I wrote a pretty good description of the case for Provider vs provide_context in the other thread, but I am happy to clarify if you have further questions.

@mondeja
Copy link
Contributor Author

mondeja commented Aug 18, 2024

More context about the next fragment of code:

<I18n>
    ...
</I18n>

This will not work on leptos-fluent because all the code is generated from a macro. In any case, I would be forcing users to do this:

 #[component]
pub fn I18n() -> impl IntoView {
    leptos_fluent! {

    }
}

Because I need all the advantadges that macros provides to shrink unused code and other performance improvements at compile time. Which could be solved with components-as-proc-macros (<I18n! my_compile_time_option="a syn literal"/>), but that's another topic to discuss.

@mondeja
Copy link
Contributor Author

mondeja commented Aug 18, 2024

See the example above. Using an <Html/> component to set lang and dir will not override user-set attributes that are not lang or dir.

if the user has provided a <Html lang=.../> and/or <Html dir=.../> attribute, I at least want to display a warning on #[cfg(debug_assertions)] advicing that leptos-fluent will override these attributes.

How can I get values of previous <Html/> invocations on SSR? AFAIK is not possible.

@gbj
Copy link
Collaborator

gbj commented Aug 18, 2024

It's a little hard for me to keep track of this thread because there are several different topics being raised. Apologies if I have missed something.

This will not work on leptos-fluent because all the code is generated from a macro. In any case, I would be forcing users to do this: ...

I have looked a bit at the macro code for leptos-fluent. It seems to me that by far the simplest API here is to adapt the macro's output slightly so that it is declarative, rather than imperative, by returning the attributes it needs to set. The user would write:

let attrs = leptos_fluent! {{
  translations: [TRANSLATIONS, TRANSLATIONS] + COMPOUND,
  locales: "./locales",
  ...
};

view! {
  <Html {..attrs}/>
  // ... the rest of the user's application
}

This makes it very obvious what is going on.

How can I get values of previous <Html/> invocations on SSR? AFAIK is not possible.

You are correct, this is not currently possible. (This kind of introspection of one part of the view tree from another is, in general, not supported.) I can think of several ways to make it possible at least to access which attribute keys have been set. There would be overhead to any of these approaches, but I guess it's fine in debug mode.

Is this only for the debugging purpose of warning the user when they have set their own lang or dir that are being overwritten?

For reference, the way this would end up being handled is that the framework would simply emit both the values into HTML, in the order they appeared in the application, and the browser would use one of them (I believe it's usually the first one, but am not sure if this is specified)

<html lang="en" lang="es">

======

I am not trying to be difficult here. It's just that exposing internals comes with a significant downside, including the fact that making internal changes becomes a breaking change. The core functionality you are trying to implement can be implemented without exposing these internals, so it is not likely that I will make the change to expose them.

@mondeja
Copy link
Contributor Author

mondeja commented Aug 21, 2024

Okay, your concerns and pretty reasonable. I'll implement the <Provider> + <Html/> expansion on the macro as it seems that the framework is being developed in that direction.

Is this only for the debugging purpose of warning the user when they have set their own lang or dir that are being overwritten?

Yes, that would make the whole implementation complete. But it's definitely not something that is in a hurry or completely necessary.

For my part this issue can be closed, at least in the way it is presented. Thanks for your patient and detailed explanations about Leptos' internals!

@gbj
Copy link
Collaborator

gbj commented Aug 21, 2024

I'm going to leave the issue open as a reminder to myself to add the ability to get that debug info on which keys have been added! Let me know if you run into issues with the <Html/>-based approach.

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

No branches or pull requests

3 participants