How to return HTML / components from clientside callbacks? - plotly-dash

I want to convert a regular (Python) callback to clientside. I'm running into difficulties with outputting to children property anything more complicated then a simple string.
The hope was that I could create a new component with new dash_html_components.Span(), but this throws an error:
Error: An object was provided as `children` instead of a component, string, or number (or list of those). Check the children property that looks something like:
{
"type": "span",
"key": null,
"ref": null,
"props": {
"children": "You have clicked THE Button!"
},
"_owner": null
}
The full code is below. It is possible to set anything other than a string into children like this, or what am I doing wrong?
import dash_html_components as html
from dash import Dash
from dash.dependencies import Output, Input
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
html.Button("THE Button", id="the_button"),
html.Div(id="the_log"),
])
app.clientside_callback(
"""
function(n_clicks){
// return "You have clicked THE Button!"; // works
// return "You have clicked <strong>THE Button!</strong>"; // works, but escapes the HTML (as expected)
return new dash_html_components.Span({children: "You have clicked THE Button!"}); // doesn't work
// return ["You have clicked ", new dash_html_components.Strong({children: "THE Button!" })]; // the goal
}
""",
Output("the_log", "children"),
Input("the_button", "n_clicks"),
)
if __name__ == '__main__':
app.run_server()

If you just need to write inline HTML code, you could use the Purify component from dash-extensions. When you pass HTML code to its html property, it is rendered inline,
from dash import html, Dash, Output, Input
from dash_extensions import Purify
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Click me", id="btn"), Purify(id="purify")])
app.clientside_callback("""function(n_clicks){return 'This is <b>html</b>';}""",
Output("purify", "html"), Input("btn", "n_clicks"))
if __name__ == "__main__":
app.run_server()
Prior to rendering, sanitization is performed using DOMPurify, hence the name of the component.

After some debugging, I found this to work:
function(n_clicks){
return [
"You have clicked ",
{
type: "Strong",
namespace: "dash_html_components",
props: {children: "THE Button"},
},
"!"
];
}

Related

NextJs Webpack asset/source returns JSON as a string

Looking for some help to understand what is going on here.
The Problem
We are using a translation service that requires creating JSON resource files of copy, and within these resource files, we need to add some specific keys that the service understands so it knows what should and should not be translated.
To do this as simple as possible I want to import JSON files into my code without them being tree shaken and minified. I just need the plain JSON file included in my bundle as a JSON object.
The Solution - or so I thought
The developers at the translation service have instructed me to create a webpack rule with a type of assets/source to prevent tree shaking and modification.
This almost works but the strange thing is that the JSON gets added to the bundle as a string like so
module.exports = "{\n \"sl_translate\": \"sl_all\",\n \"title\": \"Page Title\",\n \"subtitle\": \"Page Subtitle\"\n}\n";
This of course means that when I try and reference the JSON values in my JSX it fails.
Test Repo
https://github.com/lukehillonline/nextjs-json-demo
NextJs 12
Webpack 5
SSR
Steps To Reproduce
Download the test repo and install packages
Run yarn build and wait for it to complete
Open /.next/server/pages/index.js to see the SSR page
On line 62 you'll find the JSON object as a string
Open .next/static/chunks/pages/index-{HASH}.js to see the Client Side page
If you format the code you'll find the JSON object as a string on line 39
Help!
If anyone can help me understand what is going wrong or how I can improve the webpack rule to return a JSON object rather than a string that would be a massive help.
Cheers!
The Code
next.config.js
module.exports = {
trailingSlash: true,
productionBrowserSourceMaps: true,
webpack: function (config) {
config.module.rules.push({
test: /\.content.json$/,
type: "asset/source",
});
return config;
},
};
Title.content.json
{
"sl_translate": "sl_all",
"title": "Page Title",
"subtitle": "Page Subtitle"
}
Title.jsx
import content from "./Title.content.json";
export function Title() {
return <h1>{content.title}</h1>;
}
pages/index.js
import { Title } from "../components/Title/Title";
function Home({ dummytext }) {
return (
<div>
<Title />
<p>{dummytext}</p>
</div>
);
}
export const getServerSideProps = async () => {
const dummytext = "So we can activate SSR";
return {
props: {
dummytext,
},
};
};
export default Home;

Groovy: find a particular item from JSON array

JSON File
{
"FormElements": [
{
"ButtonLabel": [
"New",
"Done",
"Save as draft",
"Submit",
"Next",
"Finish"
],
"XPath": "//*[text()='LABEL']"
}
]
}
From the above JSON to find a particular item in the array.
For example, I want to Find "New" from the above one.
Below is my code but its return null
def isAvailable = InputJSON.FormElements.find {it."ButtonLabel[0]"=="New" }
Can anyone validate the above code, whether any changes are required in this?
From your code follows that you want to check for existence of an element, rather than find that element.
With this im mind the code can look like:
import groovy.json.*
def json = new JsonSlurper().parseText '''{ "FormElements": [ {"ButtonLabel": [ "New", "Done", "Save as draft", "Submit", "Next", "Finish"], "XPath": "//*[text()='LABEL']" } ] }'''
boolean isAvailable = 'New' in json.FormElements*.ButtonLabel.flatten()
boolean isNotAvailable = 'AAAAA' in json.FormElements*.ButtonLabel.flatten()
assert isAvailable && !isNotAvailable
Slight syntax error accessing ButtonLabel items. It should be,
def isAvailable = InputJSON.FormElements.find {it."ButtonLabel".[0]=="New" }
OR
def isAvailable = InputJSON.FormElements.find {it."ButtonLabel".get(0) == "New" }
Normally I would use indexOf.
InputJson.FormElements[0].ButtonLabel.indexOf('New')
-1 means its not present. Its hard to tell not knowing the source of your json... etc

Creating tooltip using callback for dash DataTable

I am trying to create tooltip for a dash data_table using callback. But my multiple attempts are unsuccessful.
I have seen examples where tooltip is create by reading a csv from a path. But in my case dataframe is created within the callback function and returned after clicking a submit button. Below is the code I am using
display_cols=["col1","col2","col3","col4"]
columns_property=[{"name": i, "id": i, "deletable": False, "selectable": True, "renamable":True, "hideable":True} for i in display_cols]
dash_table.DataTable(id="table",
columns=columns_property,data=[],fill_width=True,
export_columns="all",export_format="xlsx", sort_action="native",is_focused=True,
sort_mode="multi",export_headers ="names",editable=True,tooltip_data=tooltip,## Tootlip is returned from callback as options
style_cell={'textAlign': 'left','border': '1px solid grey',
'whiteSpace':'normal','height':'auto'},
style_header={'backgroundColor': 'white','fontWeight': 'bold',
'border': '1px solid black'},
style_table={'fontFamily': 'Open Sans',
'textAlign': 'right',
'whiteSpace': 'no-wrap',
'overflowX': 'scroll',
'minWidth': '100%',
'height': '600px',
'overflowY': 'scroll'})
#app.callback([Output('table', 'data'),Output("tooltip", "options") ],
[Input('submit3', 'n_clicks')],
[
State('input1', 'value'),
State('input2', 'value')
]
)
def update_output(clicked, input1, input2):
if clicked:
input_file=input1
model_path=input2
""" Some Code for Generatng DF"""
df=df[["col1","col2","col3","col4"]]
tooltip_data= [{c:{'type': 'text', 'value': f'{r},{c}'} for c in df.columns} for r in df[df.columns].values]
return list(df.to_dict("index").values()), tooltip_data
So if when you have a question, it is helpful if the code you provide can be run on its own. Looks like the code you provided was part of a larger project and was missing many required calls and boilerplate type stuff to work.
By making your code into a runable Dash app, I may have fixed issues inadvertently. Also, I find that having each function argument to dash_table.DataTable() on its own line makes it easier to read, troubleshoot, and verify. As there are about a bazillion arguments, it can get pretty crazy.
I've found the Dash DataTable documentation to be pretty good. I recommend reading through this to see various ways to use and setup tooltips:
https://dash.plotly.com/datatable/tooltips
You've provided a use case where the tooltips are dependent on a callback. Callbacks can be complicated as they are triggered off a change in the page (changing typically meaning input from the user). Callbacks are run on initial app start up and then every time the specified input changes.
Although callbacks are run on app start, you cannot have the output of the callback listed in the object definition. So, this portion:
dash_table.DataTable(#...
tooltip_data=tooltip,## Tootlip is returned from callback as options
#...
Results in a syntax error as tooltip is not defined. The callback configuration will specify where the callback return value(s) go. In this case you have no tooltips to start with, so the dash_table.DataTable() should not have the argument specified.
Here is a version of your code tweaked to show tooltips.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_table
app = dash.Dash(__name__)
display_cols=["col1","col2","col3","col4"]
columns_property=[{"name": i, "id": i, "deletable": False, "selectable": True, "renamable":True, "hideable":True} for i in display_cols]
app.layout = html.Div(children=[
html.Div(
dcc.Input(
id="submit3",
type='number'
)
),
dash_table.DataTable(id="table",
columns=columns_property,
data=[],
fill_width=True,
export_columns="all",
export_format="xlsx",
sort_action="native",
is_focused=True,
sort_mode="multi",
export_headers ="names",
editable=True,
style_cell={'textAlign': 'left','border': '1px solid grey',
'whiteSpace':'normal','height':'auto'},
style_header={'backgroundColor': 'white','fontWeight': 'bold',
'border': '1px solid black'},
style_table={'fontFamily': 'Open Sans',
'textAlign': 'right',
'whiteSpace': 'no-wrap',
'overflowX': 'scroll',
'minWidth': '100%',
'height': '600px',
'overflowY': 'scroll'})
])
#app.callback([Output('table', 'data'),
Output("table", "tooltip_data")],
[Input('submit3', 'value')]
)
def update_output(input1):
""" Some Code for Generatng DF"""
df=pd.DataFrame(np.random.randint(0,10,size=(10, 4)), columns=['col1', 'col2', 'col3', 'col4'])
# tooltips
tooltip_data= [{c:{'type': 'text', 'value': f'{r},{c}'} for c in df.columns} for r in df[df.columns].values]
return list(df.to_dict("index").values()), tooltip_data
if __name__ == '__main__':
app.run_server(debug=True)

Django - How to render html and return a json at the same time

I have a view which populate a json object, and then, at the end of the same view I would render an html page, but also return the final json.
probably this is not relevant but the json would be for example something like this:
{
"embedToken": "dasfgjasdàgjasdàgasdàgèe-AveryLongToken",
"embedUrl": "https://app.powerbi.com/let's_go_to_the_lake",
"reportId": "e615-sfash-9746"
}
the line I'm not able to fix (tried all day with all alternatives methods) is the following:
return render(request, "home.html", jsn)
my url.py is simple as follow:
urlpatterns = [
path('', HomePageView, name='home'),
]
I currently get the following error:
context must be a dict rather than str.
But I encountered all different kinds of errors on the way without succeeding to reach the desired result(rendering the html and returning the json at the same time). So my doubt is that I'm taking the wrong approach at the basics, should I change road?
I would like to try to convert the json into a dictionary, and then maybe convert it back in a json in JavaScript
I have also tried to split my requests, by rendering the html as a Django view, and performing the function call from JavaScript ajax request as follow:
function handler1(){
// there are many other methods like $.get $.getJSON
$.ajax({
type: 'GET',
dataType: 'json',
url: "http://piedpiper.com/api/callers"
}).then(function(result) {
// do something with the result
});
}
But I ended up by understanding that in this way I must create the URL api/callers which will be available/reachable to everybody, which I cannot do, because of the user session. only the logged in user must see the json data
You need to add the proper arguments on render. Here is the docs for render function in Django
Here is a sample code of a view
def post_detail(request, slug=None):
instance = get_object_or_404(Post, slug=slug)
share_string = quote_plus(instance.content)
context = {
"title": instance.title,
"instance": instance,
"share_string": share_string,
}
return render(request, "post_detail.html", context)

Get rid of Mongo $ signs in JSON

I am building python backend for SPA (Angular) using MongoDB.
Here is what I use: Python 3.4, MongoDB 3, Flask, flask-mongoengine and flask-restful
Now I receive the following JSON from my backend:
[
{
"_id": {
"$oid": "55c737029380f82fbf52eec3"
},
"created_at": {
"$date": 1439129906376
},
"desc": "Description.....",
"title": "This is title"
},
etc...
]
And I want to receive something like that:
[
{
"_id": "55c737029380f82fbf52eec3",
"created_at": 1439129906376,
"desc": "Description.....",
"title": "This is title"
},
etc...
]
My code for now:
from flask import json
from vinnie import app
from flask_restful import Resource, Api
from vinnie.models.movie import Movie
api = Api(app)
class Movies(Resource):
def get(self):
movies = json.loads(Movie.objects().all().to_json())
return movies
api.add_resource(Movies, '/movies')
Model:
import datetime
from vinnie import db
class Movie(db.Document):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
desc = db.StringField(required=True)
def __unicode__(self):
return self.title
What is the best way to format convenient JSON for front-end?
If you are confident you want to get rid of all the similar cases, then you can certainly write code that matches that pattern. For example:
info = [
{
"_id": {
"$oid": "55c737029380f82fbf52eec3"
},
"created_at": {
"$date": 1439129906376
},
"desc": "Description.....",
"title": "This is title"
},
#etc...
]
def fix_array(info):
''' Change out dict items in the following case:
- dict value is another dict
- the sub-dictionary only has one entry
- the key in the subdictionary starts with '$'
In this specific case, one level of indirection
is removed, and the dict value is replaced with
the sub-dict value.
'''
for item in info:
for key, value in item.items():
if not isinstance(value, dict) or len(value) != 1:
continue
(subkey, subvalue), = value.items()
if not subkey.startswith('$'):
continue
item[key] = subvalue
fix_array(info)
print(info)
This will return this:
[{'title': 'This is title', 'created_at': 1439129906376, 'desc': 'Description.....', '_id': '55c737029380f82fbf52eec3'}]
Obviously, reformatting that with JSON is trivial.
I found a neat solution to my problem in flask-restful extension which I use.
It provides fields module.
Flask-RESTful provides an easy way to control what data you actually render in your response. With the fields module, you can use whatever objects (ORM models/custom classes/etc.) you want in your resource. fields also lets you format and filter the response so you don’t have to worry about exposing internal data structures.
It’s also very clear when looking at your code what data will be rendered and how it will be formatted.
Example:
from flask_restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'date_updated': fields.DateTime(dt_format='rfc822'),
}
class Todo(Resource):
#marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo() # Some function that queries the db
Flask-RESTful Output Fields Documentation