jsreport / jsreport-core

The minimalist jsreport rendering core
GNU Lesser General Public License v3.0
85 stars 24 forks source link

jsreport-core and using recipe html-with-browser-client #47

Closed mayuradesh closed 4 years ago

mayuradesh commented 5 years ago

Hi,

I am using jsreport-core package in my meteor app to generate reports. Normally I use the "html" as "recipe" and it suffices.

Now I want to include filter option in my report. Accordingly I want to use "recipe" as "html-with-browser-client".

For doing so I have added "jsreport-browser-client" package.

But I am getting error as : **

TypeError: Cannot read property 'baseUrl' of undefined at Object.recipe ..\node_modules\jsreport-browser-client\lib\browser.js at invokeRender ..\jsreport-core\lib\render\render.js

**

I made a new post on jsreport forum and got to know that I need to additionally use "jsreport-express". https://forum.jsreport.net/topic/1105/can-i-use-html-with-browser-client-recipe-with-jsreport-core-package/3

var jsreport = require('jsreport-core')(); jsreport.use(require('jsreport-templates')()); jsreport.use(require('jsreport-handlebars')()); jsreport.use(require('jsreport-browser-client')()); jsreport.use(require('jsreport-express')({ httpPort: 2000}));

I tried still getting same error.

bjrmatos commented 5 years ago

can you please share the rest of code that you are using for the rendering? i mean, i want to see how you call render and how you pass the template content (and what is the content also). if your project is tiny, then you can also upload your code to github repository and share it here, then i will take a look and check the error you are getting.

mayuradesh commented 5 years ago

var jsreport = require('jsreport-core')(); jsreport.use(require('jsreport-templates')()); jsreport.use(require('jsreport-handlebars')()); jsreport.use(require('jsreport-browser-client')()); jsreport.use(require('jsreport-express')({ httpPort: 2000}));

onJSReportStarted = () => { template = getTemplate()
jsreport.beforeRenderListeners.add('beforeRender', this.jsReportManager.beforeRender) return jsreport.render({ template: { content: template, engine: 'handlebars', recipe: 'html-with-browser-client' }, }).then((resp, req) => { return resp.content.toString() }).catch((err) => { console.log(err ); }); }
initJSReport = () => { return jsreport.init().then(() => { return this.onJSReportStarted() }).catch((e) => { console.error(e) }) }

bjrmatos commented 5 years ago

i see that the html-with-browser-client will not work when you start a render from code, but it works when you start a render with http API (that is the reason why it works from studio too).

as a workaround (until we fix the usage for render with code), i think you can do this to make your render work.

jsreport.render({
    template: {
        content: template,
        engine: 'handlebars',
        recipe: 'html-with-browser-client'
    },
    context: {
      http: {
        baseUrl: 'http://localhost:2000' // or put here the host and port that you are using
      }
    }
})
mayuradesh commented 5 years ago

Amazing. With the changes that you have given my code works. I am no longer getting the "baseUrl" error. BUT (: unfortunately the "html-with-browser-client" recipe has no effect on the way my template is being rendered. I have script tags and script functions in my template. None of them work.

Please refer to this example that I have created in JSreport online studio: https://playground.jsreport.net/w/mayura.deshmukh/Zhl6HmZ6

I want to use the same templates in my meteor - react js app where I use JS Report-Core and render the JS report from code and not via a http call to the JS Report engine.

How do I do it? These templates work in the studio. But in my app, they don't. The script does not get processed.

mayuradesh commented 5 years ago

Ok so this is how the JSreport is getting rendered in my app:

div dangerouslySetInnerHTML={{ __html: this.props.reportHTML }}

That is, in the react component. This is way the script calls get disabled. I updated this to apply the reportHTML to "createContextualFragment" instead of innerHTML and the scripts work!

Now another issue is coming up.

How do I refer to the inner template in the jsreport.render method :

$("#filter").click(function() {
        var filter = $("#userFilter").val();
        $.getJSON("https://jsonplaceholder.typicode.com/posts?userId=" + filter, function(posts) {
            // render "posts table" template into "results" div
            // this creates iframe inside the div with the html content, which avoids scripts and styles collision
            jsreport.render("results", { 
                template: { 
                    name: 'listWithFilterChildTemplate'
                },
                data: {
                    posts:posts
                }
            })
             });
     }); 

In Studio, its easily possible to refer to another template using the name or shortid. But in my app, there is only JSReport-core.

bjrmatos commented 5 years ago

div dangerouslySetInnerHTML={{ __html: this.props.reportHTML }} That is, in the react component. This is way the script calls get disabled. I updated this to apply the reportHTML to "createContextualFragment" instead of innerHTML and the scripts work!

i was thinking the same, your original issue was just caused because the way React handles html

Now another issue is coming up. How do I refer to the inner template in the jsreport.render method In Studio, its easily possible to refer to another template using the name or shortid.

it is not possible if you don't save the template, you are rendering with anonymous templates (passing the whole template content in .render), if you need references to another template you can just save it, you can do that somewhere after your jsreport.init but before the render.

// save your templates with name
jsreport.documentStore.collection('templates').insert({
  name: 'listWithFilterMasterTemplate',
  content: '...',
  engine: 'handlebars',
  recipe: 'html-with-browser-client'
}) // -> this returns promise

jsreport.documentStore.collection('templates').insert({
  name: 'listWithFilterChildTemplate',
  content: '...',
  engine: 'handlebars',
  recipe: 'html'
}) // -> this returns promise

// then in your render you can use reference by name
gupta-nitish commented 5 years ago

i see that the html-with-browser-client will not work when you start a render from code, but it works when you start a render with http API (that is the reason why it works from studio too).

as a workaround (until we fix the usage for render with code), i think you can do this to make your render work.

jsreport.render({
  template: {
      content: template,
      engine: 'handlebars',
      recipe: 'html-with-browser-client'
  },
    context: {
      http: {
        baseUrl: 'http://localhost:2000' // or put here the host and port that you are using
      }
    }
})

We tried this solution but are still facing an issue. When rendering the report on UI, "jsreport is undefined" error is being thrown. We tried including jsreport-browser-client-dist in the template itself as suggested at https://forum.jsreport.net/topic/184/referenceerror-jsreport-is-not-defined/12 but are still facing the same issue.

bjrmatos commented 5 years ago

@mayuradesh confirmed that it works, if it does not maybe you are using other port than 2000 and you need to adapt the code. i guess both are using the same codebase so i'm not sure why it would work for @mayuradesh but not for you. maybe it is a good idea to check deeper with @mayuradesh