odoo / owl

OWL: A web framework for structured, dynamic and maintainable applications
https://odoo.github.io/owl/
Other
1.14k stars 344 forks source link

[16.0]How to initialize/mount properly a component in a website / portal view #1584

Closed flotho closed 6 months ago

flotho commented 8 months ago

Hi,

Frist of all thanks for this awesome lib! I've read multiple tutorial, documentations and piece of code. I've succeeded in making standalone application with a component. tutorial are working great.

Now I'm struggling to useService in my component for the website / portal.

Here is my structure :

/** @odoo-module **/
//main.js file pointed in the manifest

import { ProductAppWebClient } from "./product_app/product_app";
import { whenReady, mount } from "@odoo/owl";
import { templates } from "@web/core/assets";
import { makeEnv, startServices } from "@web/env";
// import { mountComponent } from "../backport/env_17"; //imported from v17 to check 

whenReady(() => {
  console.log("for the breakpoint");
  const env = makeEnv();
  //   startServices(env); //causes storm in the browser, impossible to start a service already started
  /**   https://www.odoo.com/documentation/master/developer/howtos/standalone_owl_application.html#root-component
   * this one don't work sadly
   */
  //   mountComponent(
  //     ProductAppWebClient,
  //     document.getElementById("artist_product_app"),
  //     { props: {}, env, templates, dev: env.debug }
  //   );

  /*   This one work but not for the services */
  mount(ProductAppWebClient, document.getElementById("product_app"), {
    props: {},
    env,
    templates,
    dev: env.debug,
  });
});

Then the component by itself :

/** @odoo-module **/

import { Component, useState, onWillStart, onMounted } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { registry } from "@web/core/registry";

export class ProductAppWebClient extends Component {
  static template = "partner_artist_portal.ProductAppWebClient";
  setup() {
    console.log("Inside product APP");
    this.product_url = document.URL;
    this.product_id = useState([]);
    console.log(document.URL);

    console.log("about to setup services ");
    const serviceRegistry = registry.category("services");
    console.log(serviceRegistry); //not empty

    // UseService below failed when using mount method
    // don't failed when using something like :
    // registry.category("main_components").add("ProductAppWebClient", {
    //   Component: ProductAppWebClient,
    //   props: {},
    // });
    // Yet using add to main_components don't plug the component on the element id choosed before
    this.rpc = useService("rpc");
    this.orm = useService("orm");

  }

As a summary, either I succeed in mounting the component on the target and the services are not available or I load the component into main_component registry and the services are available . I'm sure I missing something even if it lloks like to be not so easy https://github.com/odoo/odoo/issues/73467#issuecomment-877095434

Thanks in advance for reading

flotho commented 8 months ago

Looks like an odoo question rather than a owl one

sdegueldre commented 8 months ago

causes storm in the browser, impossible to start a service already started

The issue here is that we already start the services on the website and portal and they can only be started once, in those cases you want to reuse the environment that contains the started services. In version 17.0 you should be able to get your app mounted automatically by the public_component service, in prior versions there is unfortunately no great way to access that env, so you'll have to access it with a workaround, for example by creating a dummy service that stores a reference to the env in an object exported by the module.

flotho commented 8 months ago

Hi @sdegueldre , thanks for this great information

DorianMAG commented 8 months ago

Hi @sdegueldre , and @flotho ,

I tried this method in odoo 17, and i had the same problem when i called mountComponent(), An error from the console was display with the following message "service is niot initialized" and a storm of infinite loop errors about lifecycle component.

This is a same error as flotho.

Regards

/** @odoo-module **/

import { whenReady } from "@odoo/owl";
import { mountComponent } from "@web/env";
import { componentHelloWorld } from "./component/component";

whenReady(() => {
    console.log('HELLO START');
    let helloWorldById = document.getElementById("wrap");
    console.log(helloWorldById);
    mountComponent(componentHelloWorld, helloWorldById);

});
flotho commented 8 months ago

Hum, thanks @DorianMAG We found how to work in frontened with the new design in v17. First we have to add the component to public_component as explained previsously At this moment your services will be correctly available if you add it to public_components and if you use this tag in your QWEB https://github.com/odoo/odoo/blob/17.0/addons/web/static/src/public/public_component_service.js#L24 it will rock !

BTW @sdegueldre @ged-odoo this is a great improvement in public components !!! Awesome

jujuna commented 7 months ago

Hi @sdegueldre , and @flotho ,

I tried this method in odoo 17, and i had the same problem when i called mountComponent(), An error from the console was display with the following message "service is niot initialized" and a storm of infinite loop errors about lifecycle component.

This is a same error as flotho.

Regards

/** @odoo-module **/

import { whenReady } from "@odoo/owl";
import { mountComponent } from "@web/env";
import { componentHelloWorld } from "./component/component";

whenReady(() => {
    console.log('HELLO START');
    let helloWorldById = document.getElementById("wrap");
    console.log(helloWorldById);
    mountComponent(componentHelloWorld, helloWorldById);

});

Is there anything new about that? I have a similar code and get infinite loop. when i use just mount i couldn't use useService and when i use mountComponent and get infinite loop

richard-hp commented 6 months ago

I just did this inside my template:

<template id="finance.web_portfolio_template">
          <t t-call="website.layout">
            <div id="mount-point"></div>
              <script type="module">
                setTimeout(() => {
                  const { mount } = owl;
                  const el = document.getElementById('mount-point');
                  const { registry } = odoo.runtimeImport('@web/core/registry')
                  const PortfolioAsset = registry.category("public_components").get("finance.PortfolioAsset");
                  mount(PortfolioAsset, el)
                }, 1000)
              </script>
            </div>
         </t>
</template>

It's a bit hacky but it did find the component and mount it to the front end. The timeout is necessary to wait for global variables like owl and odoo to become available

richard-hp commented 6 months ago

Ok this is a bit more of a robust solution and loads faster

const mountComponent = () => {
  console.log(`Trying to mount`)
  try {
    if (!owl) {
      return
    }
  } catch (e) {
    console.log(`Owl not loaded, timing out`)
    setTimeout(mountComponent, 100)
    return
  }

  const { mount } = owl;
  const el = document.getElementById('mount-point');
  const { registry } = odoo.runtimeImport('@web/core/registry')
  const PortfolioAsset = registry.category("public_components").get("finance.PortfolioAsset");
  mount(PortfolioAsset, el)
}
mountComponent()
flotho commented 6 months ago

thanks @richard-hp , which version ar you working on. In 17 the syntax owl-component-nameoftheowlcompanent make the deal https://github.com/odoo/odoo/blob/17.0/addons/web/static/src/public/public_component_service.js#L24

sdegueldre commented 6 months ago

I have written a guide on how to mount components in the website and portal in 17.0 and above here: https://www.odoo.com/documentation/17.0/developer/howtos/frontend_owl_components.html

In 16.0 this is not well supported by the framework. You can do it but it's undocumented and not officially supported so you're on your own. And this question is an Odoo question, not an Owl question so this isn't really the right place to discuss this.

richard-hp commented 5 months ago

My solution was for 16, as 17 has official way to do this now, you don't need this hack in 17

suraj19641 commented 5 months ago

"I'm currently working with v16 and using a controller router to render QWeb templates. Inside these templates, I'm mounting Owl components using the div's ID. However, I'm encountering an issue when trying to implement the **useService hook.** . Any insights into why this might be happening?

Additionally, whenever I try to import { barcodeGenericHandlers } from "@barcodes/barcode_handlers"; and { barcodeService } from "@barcodes/barcode_service";, I'm facing dependency errors and can't seem to retrieve any content on my Owl template. Any suggestions on how to resolve this?"

       -----This is my js code------  

/ @odoo-module / const {Component,mount,whenReady,useState,useEnv,onWillStart,EventBus} = owl; import {templates} from "@web/core/assets"; import { useBus, useService } from "@web/core/utils/hooks";

// Popup Component class BarcodeErrorPopup extends Component { static template = 'multi_language_module.BarcodeErrorPopupTemplate'; setup() { const barcode = useService("barcode"); useBus(barcode.bus, "barcode_scanned", this.onBarcodeScanned); } onBarcodeScanned(barcode , target) { const { barcode } = event.detail; console.log('Event Details',event.detail) } } BarcodeErrorPopup.template = 'multi_language_module.BarcodeErrorPopupTemplate'; whenReady(() => { const element = document.querySelector('#call_owl_error_popup_template'); if (element) { mount(BarcodeErrorPopup, element, {templates}); } });

----This is my manifest file -----
'depends' : ['base','web','barcodes','barcodes_gs1_nomenclature'],

    'web.assets_frontend': [  'multi_language_module/static/src/components/barcode_popup.xml',
        'multi_language_module/static/src/components/barcode_popup.js',
    ],

----This is my qweb template that render useing controller route ----

 <template id="pos_barcode_error_popup_template">
    <t t-call-assets="web.assets_backend" t-js="false"/>
    <t t-call-assets="web.assets_backend" t-css="false"/>
    <t t-call-assets="web.assets_common" t-css="false"/>
    <t t-call-assets="web.assets_common" t-js="false"/>
    <t t-call-assets="web.assets_frontend" t-css="false"/>

     <div id="call_owl_error_popup_template"/>

</template>
sdegueldre commented 5 months ago

Locking this issue for the following reasons: 1) This is not supported by the framework itself in 16.0, if you want access to the services you're on your own. Either start them by hand, or, if you're using the assets_frontend, find a way to get your hands on the existing env and use that. I might write a guide on that in the 16.0 documentation at some point since it appears to be something people frequently want to do. 2) This has nothing to do with Owl and is an issue with Odoo, this is not the right place to discuss this. 3) Issues are not a support forum, I have repeatedly helped people with these issues out of good will, but investingating why third-party custom code does not work is not part of my duties. If you believe you are using the framework correctly but things are not working, that would be a bug and you should report it on the appropriate repo: odoo/odoo if it's a bug in the code, odoo/documentation if the documentation is wrong. odoo/owl is for bugs in owl itself or its documentation. 4) If all of the above do not apply to you, your report should clearly be inside of a new issue since most of the above points apply to this issue.