{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "\n",
    "pn.extension('tabulator', design=\"material\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `panel.ReactiveExpr` pane renders a [Param `rx` object](https://param.holoviz.org/user_guide/Reactive_Expressions.html) which represents a reactive expression, displaying both the widgets that are part of the expression and the final output of the expression. The position of the widgets relative to the output can be set, or widgets can be removed entirely.\n",
    "\n",
    "Please note that you can use use `pn.rx` instead of [`param.rx`](https://param.holoviz.org/user_guide/Reactive_Expressions.html) when you `import panel as pn`.\n",
    "\n",
    "See the [`param.rx` documentation](https://param.holoviz.org/user_guide/Reactive_Expressions.html) for details on using `rx`.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "The basic parameters are:\n",
    "\n",
    "* **`object`** (param.reactive): A [`param.reactive`](https://param.holoviz.org/user_guide/Reactive_Expressions.html) expression\n",
    "\n",
    "The more advanced parameters which give you more control are:\n",
    " \n",
    "* **`center`** (bool): Whether to center the output horizontally.\n",
    "* **`show_widgets`** (bool): Whether to show the widgets.\n",
    "* **`widget_layout`** (ListPanel): The layout object to display the widgets in. For example `pn.WidgetBox` (default), `pn.Column` or `pn.Row`.\n",
    "* **`widget_location`** (str): The location of the widgets relative to the output of the reactive expression. One of  'left', 'right', 'top', 'bottom', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'left_top' (default), 'right_top',right_bottom'.\n",
    "\n",
    "#### Properties\n",
    "\n",
    "* **`widgets`** (ListPanel): Returns the widgets in a `widget_layout`.\n",
    "\n",
    "For more layout and styling related parameters see the [Sizing how-to guide](../../how_to/layout/size.md).\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "The [`param.rx`](https://param.holoviz.org/user_guide/Reactive_Expressions.html) API is a powerful tool for building declarative and reactive UIs.\n",
    "\n",
    "Lets take a few examples"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def model(n):\n",
    "    return f\"🤖 {n}x2 is {n*2}\"\n",
    "\n",
    "n = pn.widgets.IntSlider(value=2, start=0, end=10)\n",
    "pn.rx(model)(n=n)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Behind the scenes panel has made sure the *reactive expression* above is rendered in a `pn.ReactiveExpr` pane. You can also do this explicitly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = pn.widgets.IntSlider(value=2, start=0, end=10)\n",
    "pn.ReactiveExpr(pn.rx(model)(n=n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A reactive expression is never a *dead end*. You can always update and change a *reactive expression*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = pn.widgets.IntSlider(value=2, start=0, end=10)\n",
    "\n",
    "pn.rx(model)(n=n) + \"\\n\\n🧑 Thanks\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also combine *reactive expressions*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = pn.widgets.IntSlider(value=2, start=0, end=10, name=\"x\")\n",
    "y = pn.widgets.IntSlider(value=2, start=0, end=10, name=\"y\")\n",
    "\n",
    "expr = x.rx()*\"⭐\" + y.rx()*\"⭐\"\n",
    "expr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Layouts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can change the `widget_location`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = pn.widgets.IntSlider(value=2, start=0, end=10, name=\"x\")\n",
    "y = pn.widgets.IntSlider(value=2, start=0, end=10, name=\"y\")\n",
    "\n",
    "expr = x.rx()*\"⭐\" + \"\\n\\n\" + y.rx()*\"❤️\"\n",
    "\n",
    "pn.ReactiveExpr(expr, widget_location=\"top\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can change the `widget_layout` to `Row`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.ReactiveExpr(expr, widget_layout=pn.Row)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can `center` the output horizontally"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.ReactiveExpr(expr, center=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can hide the widgets by setting `show_widgets=False`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.ReactiveExpr(expr, show_widgets=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can access the `.widgets` in a `widget_layout` and lay them out as you please"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.ReactiveExpr(expr).widgets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Reactive expressions as references\n",
    "\n",
    "Using the `pn.ReactiveExpr` pane implicitly or explicitly is great for exploration in a notebook. But its not very performant because every time the reactive expression rerenders, Panel has to create a new pane to render your output in.\n",
    "\n",
    "Instead you can and should pass the *reactive expression* as a *reference* to a specific Panel component. The Panel component can resolve the value of the expression dynamically:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = pn.widgets.IntSlider(value=2, start=0, end=10, name=\"x\")\n",
    "y = pn.widgets.IntSlider(value=2, start=0, end=10, name=\"y\")\n",
    "\n",
    "ref = x.rx() + y.rx()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.pane.Str(ref)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.indicators.Progress(name='Progress', value=ref, max=20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try changing the `x` and `y` values using the widgets below!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.ReactiveExpr(ref).widgets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-info\">\n",
    "<p><b>The <em>reference approach</em> should generally be preferred</b> as it is more declarative and explicit, allowing Panel to efficiently update the existing view(s) rather than completely re-rendering the output.</p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Styled DataFrame Example\n",
    "\n",
    "Let us work through this in a slightly more complex example, and build an expression to dynamically load some data and sample N rows from it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "dataset = pn.widgets.Select(name='Pick a dataset', options={\n",
    "    'penguins': 'https://datasets.holoviz.org/penguins/v1/penguins.csv',\n",
    "    'stocks': 'https://datasets.holoviz.org/stocks/v1/stocks.csv'\n",
    "})\n",
    "nrows = pn.widgets.IntSlider(value=5, start=0, end=20, name='N rows')\n",
    "\n",
    "# Load the currently selected dataset and sample nrows from it\n",
    "df_rx = pn.rx(pd.read_csv)(dataset).sample(n=nrows)\n",
    "\n",
    "df_rx"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have an expression that does what we want we can use it as a reference to reactive update the `value` of a `Tabulator` widget:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = pn.widgets.Tabulator(df_rx, page_size=5, pagination='remote')\n",
    "\n",
    "table.style.bar(cmap='RdYlGn_r')\n",
    "\n",
    "pn.Row(pn.Column(dataset, nrows), table)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, particularly for complex expressions with tons of inputs it may still be useful to use the `ReactiveExpr` object to render all the widget inputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Row(pn.ReactiveExpr(df_rx).widgets, table)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
