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

Stylesheet is not found after UI.navigate to AppLayout in production mode #8292

Closed
Dudeplayz opened this issue May 11, 2020 · 12 comments
Closed

Comments

@Dudeplayz
Copy link
Contributor

Description of the bug

The stylesheet is not loaded properly on UI.navigate for vaadin-app-layout in production mode. Not occurring in dev mode.
See: appreciated/vaadin-app-layout#297, also appreciated/vaadin-app-layout#306 and vaadin/flow-spring-examples#53

Minimal reproducible example

Navigate with UI.navigate to an AppLayout view, e.g. from a login screen.

Expected behavior

Correctly loaded stylesheet.

Actual behavior

Server can not find stylesheet, resulting in missing style until page reload.

Workaround

Replace UI.navigate(...) with UI.getCurrent().getPage().setLocation(...) (suggested in appreciated/vaadin-app-layout#297)

Tested versions:

- Vaadin / Flow version: 14.2.0.beta1 / 2.2 (also tested with latest 14.1.x versions)
- Java version: OpenJDK-11
- OS version: Windows10 (1909) and Debian 10 (Buster)
- Browser version: Vivaldi (3.0.1874.32) based on Chromium (81.0.4044.123)
- IDE: IntelliJ 2020.1.1
@rolandoisidoro
Copy link

Same problem here. I've been using 14.1.x, have tried in all versions from 14.1.17 to 14.1.27. When forcing a page refresh the CSS gets loaded correctly.

On the first request, after a successful login, Vaadin tries to load the CSS from a frontend-es6 location instead of the correct frontend. Forcing a page refresh, the CSS file is loaded from the correct location.

When directly accessing the URL Vaadin is trying to load I get the following result with a 404 HTTP status:

Could not navigate to 'frontend-es6/css/main.css'

Reason: Couldn't find route for 'frontend-es6/css/main.css'

@denis-anisimov
Copy link
Contributor

denis-anisimov commented May 27, 2020

Unfortunately I'm not able to understand the issue from this mixture of incomplete/not specified information.

Navigate with UI.navigate to an AppLayout view, e.g. from a login screen.

How may I find the AppLayout view?
How my I find the login screen ?

Correctly loaded stylesheet.

Which stylesheet ?
A custom stylesheet or included in the component ?

frontend-es6 location is referenced in many tickets here.
This location is used in compatibility mode ONLY.
So do you run compatibility mode ?

Was not able to find any steps to reproduce.
Please note that "Minimal reproducible example" means:

  • provide the code which doesn't contain anything else except the necessary snippets which allow to reproduce the bug.
  • the code has to contain all information which is required to reproduce the issue.

Since there are no steps to reproduce I've been trying to do the following:

Check the errors in the browser console.
I don't see any error.

public class AppLayoutView extends AppLayout {

}

@Route(value = "a", layout = AppLayoutView.class)
public class AppLayoutRoute extends Div {

    public AppLayoutRoute() {
        setText("Navigated");
    }

}

public class MainView extends VerticalLayout {

    public MainView() {
        GreetService greetService = new GreetService();
        Button button = new Button("Say hello", e -> 
            UI.getCurrent().navigate(AppLayoutRoute.class));

        add(button);
    }
}

@rolandoisidoro
Copy link

@denis-anisimov , I'll try to add as much insight as possible. Given the following project file structure:

├── src/main/java/org/vaadin/example/
│   ├── ui
│   │   ├── layout
│   │   │   └── MainLayout.java
│   │   ├── views
│   │   │   ├── anon
│   │   │   │   └── LoginView.java
│   │   │   ├── authc
│   │   │   │   ├── ...
│   │   │   │   └── MainView.java
│   │   │   └── ...
│   │   └── ...
│   └── ...
└── ...

LoginView.java

@PageTitle(LoginView.TITLE)
@Route(LoginView.URL)
@StyleSheet("css/login.css")
public class LoginView extends Div implements HasComponents, RouterLayout, PageConfigurator {

(...)

    LoginForm component = new LoginForm();
    component.addLoginListener(e -> {
            boolean isAuthenticated = authenticate(e);
            if (isAuthenticated) {
                    UI.getCurrent().navigate(MainView.class);
            } else {
                    component.setError(true);
            }

(...)

}

MainLayout.java

@StyleSheet("css/main.css")
public class MainLayout extends AppLayout implements RouterLayout, PageConfigurator, AfterNavigationObserver {

MainView.java

@PageTitle(MainView.TITLE)
@Route(value = MainView.URL, layout = MainLayout.class)
public class MainView extends VerticalLayout implements HasComponents, RouterLayout {

The problem occurs when I perform the following actions:

  1. Access the Login view;
  2. Login successfully.

After step 2, the main.css file isn't loaded upon the LoginView -> MainView redirect, as described before:

On the first request, after a successful login, Vaadin tries to load the CSS from a frontend-es6 location instead of the correct frontend. Forcing a page refresh, the CSS file is loaded from the correct location.

Workaround

At this point, I managed to make it work by replacing UI.getCurrent().navigate(MainView.class); to UI.getCurrent().getPage().setLocation(MainView.URL); , as suggested in appreciated/vaadin-app-layout#297.

@denis-anisimov
Copy link
Contributor

@rolandoisidoro , thanks for clarification, I will look whether I'm able to reproduce with these steps.

But still I would like to know the project configuration.
Is it 14.2 Platform ?
Do you use compatibility mode ? Because I see frontend-es6 mention.
In the latter case I would like to see the pom.xml.
Or better the project based on https://github.com/vaadin/skeleton-starter-flow/tree/v14
or https://github.com/vaadin/skeleton-starter-flow-spring.

I will use one of those projects ANYWAY to reproduce.

@rolandoisidoro
Copy link

@denis-anisimov , sorry for not providing that info earlier. In my first comment on this issue I mentioned:

I've been using 14.1.x, have tried in all versions from 14.1.17 to 14.1.27.

I'm not using compatibility mode, nor Spring. My setup relies on Vaadin + JEE. Here are some relevant parts of my pom.xml, which was based in some previous Vaadin starter project:

    <properties>

        (...)

        <vaadin.version>14.1.27</vaadin.version>

        (...)

    </properties>

    <pluginRepositories>
        <pluginRepository>
            <id>central</id>
            <url>https://repo1.maven.org/maven2/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo1.maven.org/maven2/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>

        <!-- Repository used by many Vaadin add-ons -->
        <repository>
            <id>Vaadin Directory</id>
            <url>https://maven.vaadin.com/vaadin-addons</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <version>${vaadin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Vaadin -->
        <dependency>
            <groupId>com.vaadin</groupId>
            <!-- vaadin-core (instead of vaadin) to use only free components -->
            <artifactId>vaadin</artifactId>
            <exclusions>
                <!-- Webjars are only needed when running in Vaadin 13 compatibility mode -->
                <exclusion>
                    <groupId>com.vaadin.webjar</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.insites</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.polymer</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.polymerelements</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.vaadin</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.webjars.bowergithub.webcomponents</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-cdi</artifactId>
        </dependency>

        (...)

    </dependencies>

@knoobie
Copy link
Contributor

knoobie commented May 27, 2020

I tried to reproduce the problem and haven't seen that you use @StyleSheet instead of @CssImport.

A working example with @CssImport can be found at https://github.com/knoobie/vaadin-maven-multi-module-example.git branch flow/issue-8292.

I'm wondering if @rolandoisidoro problem could be fixed with using @CssImport instead of @StyleSheet. Not sure about the problem of @Dudeplayz.

Was there any reasoning to create different annotations @denis-anisimov? There is an explanation in the docs "In server-side views (Java), use the @CssImport annotation to import local/bundled style sheets and the @StyleSheet annotation to import external/linked style sheets". - dunno if there was more.

@denis-anisimov
Copy link
Contributor

I think I've started to understand the issue.

First of all: @rolandoisidoro thank you very much for quick response with the necessary details.
The original ticket description doesn't allow to work with this ticket anyhow.

The second thing which is important here: AppLayout has no any relation to the issue.
There is so much not relevant information and almost no important information in the ticket.

The only thing which is important : custom style specified via @StyleSheet("css/login.css") doesn't work as expected in NPM mode.

And this is just because @StyleSheet should be normally used for external URLs.
For local/bundled styles @CssImport should be used.

From the javadocs of StyleSheet:
Relative URLs are interpreted as relative to the configured {@code frontend} directory location.

So when you use css/login.css it becomes "frontend://css/login.css".
In the dev mode it's resolved to /frontend/css/login.css.
But in production mode we still have a logic on the client side which uses frontend-es6 prefix to resolve the relative URLs.
I think we should fix this logic so that /frontend/ is still used in NPM mode.
This is what I'm going to check and fix.

The problem is : I don't see in the comments where css/login.css is located in the project.
Project structure shows only Java classes location.

Anyway : when you use NPM you should put all your resources into the /frontend folder of your root project. In this case you won't be able to use @StyleSheet at all because it won't be able to find this resource at all.

I assume that the location of css/login.css is /src/main/webapp/frontend/css/login.css .
And this location normally should not be used in NPM.
Another way can be : avoid frontend schema at all since it's not supposed to work in NPM at all.
Use context schema and set it explicitly in your @StyleSheet annotation.

@denis-anisimov
Copy link
Contributor

Was there any reasoning to create different annotations @denis-anisimov?

I'm not sure.
Not mine decision.

But @StyleSheet is a mostly compatibility mode annotation which still works for GLOBAL styles in NPM mode as well.
But if you want to use styles for your web components then @StyleSheet can't be ever used.
@CssImport is the only correct way to use styles for web components.

@StyleSheet could have been reused for NPM mode but in fact @CssImport has a number of parameters which has no sense for @StyleSheet and semantically it's different. So I guess that was the reason.

@pleku pleku moved this from P1 - High priority to P3 - Low Priority in OLD Vaadin Flow bugs & maintenance (Vaadin 10+) May 29, 2020
@pleku
Copy link
Contributor

pleku commented May 29, 2020

It seems that it is not easy to reproduce this issue and there are lots of unrelated information here.

We've spent time on reproducing this issue and will not spend any more time. Please create a new ticket with the exact steps to reproduce the problem, thank you.

@pleku pleku closed this as completed May 29, 2020
OLD Vaadin Flow bugs & maintenance (Vaadin 10+) automation moved this from P3 - Low Priority to Closed May 29, 2020
@pleku pleku added this to the Abandoned milestone May 29, 2020
OLD Vaadin Flow bugs & maintenance (Vaadin 10+) automation moved this from Closed to Needs triage May 29, 2020
@denis-anisimov
Copy link
Contributor

Sorry, for this specific issue after spending several days I was able to reproduce it.

One more time : thanks to @rolandoisidoro who helped with this.

@rolandoisidoro
Copy link

@denis-anisimov , sorry for coming back late to the discussion. In my case, the decision to go with @Stylesheet over @CssImport was my call, taking into consideration what I read on the oficial documentation regarding Importing Style Sheets to the global scope:

External/linked style sheets can be used to import styles without inlining the contents to the application bundle. This allows the browser to load and cache the style sheet separately from the rest of the application.

My goal was to have linked style sheets files instead of inlining them.

I see you were able to reproduce the issue, can you clarify what are the conditions under which it occurs?

@denis-anisimov
Copy link
Contributor

Use @Stylesheet with relative path or explicit frontend:// schema.
Make a route view which navigates via UI.navigate to the view above (with @Stylesheet).

It works fine in dev mode, it doesn't work in production mode.
If you load view with @Stylesheet directly (without programatic navigation) then it works fine.
It's necessary to use UI.navigate.

This is a consequence of applying URI resolution on the client side which uses compatibility mode URLs . Compatibility mode URLs should not be sent to the client side if app is running without compatibility mode.

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