烧瓶中带有 htmx 的无限滚动对我不起作用

问题描述 投票:0回答:2

这是我第一次使用 htmx,我尝试使用无限滚动,但它不起作用。这是我的代码如下

@app.route('/home', methods = ['GET'])



def home():
    # pagination
    page = request.args.get('page', 1, type=int)
    all_data = Data.query.order_by(desc(Data.timestamp)).paginate(page = page, per_page = 20)

    if "hx_request"  in request.headers:
        return render_template("table.html",datas = all_data)
    return render_template("home.html", datas = all_data)

这是我试图添加无限滚动的html代码

{% for data in datas.items %}
{% if loop.last and data.has_next %}
<tr hx-get="{{url_for('home', page = data.next_num)}}" hx-trigger="revealed" hx-swap="afterend">
{% else %}
    <tr>
  {% endif %} 
     
        <td scope="row">{{data.uuid}}</td>
        <td scope="row">{{data.timestamp}}</td>
        <td scope="row">{{data.decibel}}</td>
        
    </tr>
    
    {% endfor %}

这是我的 home.html,其中包含表格

{% extends 'layout.html' %}
{% block head %}
<title>home</title>
{% endblock %}
{% block body %}
<center>
<table class="table table-primary table-striped" >
    <tr> 
        <th scope="col">UUID</th>
        <th scope="col">Timestamp</th>
        <th scope="col">Decibel</th>
    </tr>
   <tbody>
    {% include 'table.html' %}


</tbody>


</table>
</center>
{% endblock %}

我在这里和那里更新了一些东西,但它仍然不起作用

html flask flask-sqlalchemy flask-restful htmx
2个回答
2
投票

下面的示例向您展示了一种不太好的使用 htmx 无限滚动的方法。这和你的方法非常相似。

from flask import (
    Flask,
    render_template,
    request
)
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLAlchemy(app)

class Record(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, unique=True, nullable=False)

with app.app_context():
    db.drop_all()
    db.create_all()
    items = [Record(name=f'Record-{i}') for i in range(100)]
    db.session.add_all(items)
    db.session.commit()

@app.route('/')
def index():
    page = request.args.get('page', 1, type=int)
    records = Record.query.paginate(page=page, per_page=20)
    return render_template('index.html', **locals())

定义了一个宏,它使用 paginate 对象为加载块的最后一行添加 htmx 模式,只要还有另一个页面。

迭代加载页面的元素,为每个条目返回一个表行。无限滚动模式所需的属性将添加到最后一行,前提是还有另一页。
为了使宏也可用于其他数据记录,使用

caller()
将当前对象发送回迭代内的调用。这使得它可以用作调用的嵌套块中的参数。 另请参阅文档中的
call
部分以获取更详细的说明。

{% macro htmx_infinite_table_rows(paginate, endpoint) -%}
  {% for item in paginate.items -%}
    {% if loop.last and paginate.has_next -%}
    <tr
      hx-get="{{ url_for(endpoint, page=paginate.next_num) }}"
      hx-trigger="revealed"
      hx-swap="afterend"
    >
    {% else -%}
    <tr>
    {% endif -%}
      {{ caller(item) }}
    </tr>
  {% endfor -%}
{% endmacro -%}

{% if 'hx_request' in request.headers -%}
  {% call(record) htmx_infinite_table_rows(records, 'index') -%}
    <td>{{ record.name }}</td>
  {% endcall -%}
{% else -%}
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
      <title></title>
      <style>
        .htmx-indicator {
          display:none;
        }
        .htmx-indicator.htmx-request {
          display:inline;
        }
      </style>
    </head>
    <body>
      <table>
        <thead>
          <tr>
            <th>Name</th>
          </tr>
        </thead>
        <tbody>
          {% call(record) htmx_infinite_table_rows(records, 'index') -%}
            <td>{{ record.name }}</td>
          {% endcall -%}
        </tbody>
      </table>
      <center><img class="htmx-indicator" width="60" src="https://htmx.org/img/bars.svg"></center>
      
      <script 
        src="https://unpkg.com/[email protected]" 
        integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" 
        crossorigin="anonymous" 
        defer></script>
    </body>
  </html>
{% endif -%}

更新:

您在获取属性

has_next
next_num
时犯了一个错误。代码应该如下。

{% for data in datas.items %}
  {% if loop.last and datas.has_next %}
  <tr hx-get="{{url_for('home', page=datas.next_num)}}" hx-trigger="revealed" hx-swap="afterend">
  {% else %}
  <tr>
  {% endif %}
    <td scope="row">{{data.uuid}}</td>
    <td scope="row">{{data.timestamp}}</td>
    <td scope="row">{{data.decibel}}</td>
  </tr>
{% endfor %}

0
投票

由于这是一个 Flask 实现,所以像往常一样,它(至少)有三个组件 - 宏、模板和控制器。

宏观

这里我们有关键的无限滚动位 - 这是一个将以无限滚动样式处理分页的宏

{% macro htmx_infinite_table_rows(paginate, endpoint) -%}
  {% for item in paginate.items -%}
    {% if loop.last and paginate.has_next -%}
    <tr
      hx-get="{{ url_for(endpoint, page=paginate.next_num) }}"
      hx-trigger="revealed"
      hx-swap="afterend"
    >
    {% else -%}
    <tr>
    {% endif -%}
      {{ caller(item) }}
    </tr>
  {% endfor -%}
{% endmacro -%}

备注:

  • 这是无限的表行,这就是为什么将三元应用于
    <tr>
    ,这意味着,如果是第一次加载,
    <tr>
    只是正常调用,但如果我们位于“页面”的末尾,我们从控制器获取下一页

模板 - 基础

根据最佳实践,我们的页面都扩展了一个base.html文件,其中包括标准的导航栏、页眉和页脚。一般来说,我在这里使用引导程序,它有一个很好的交互式表,但是(据我所知)该表需要完整的数据集加载。这是一个很好的交互式表格,但是如果有 10,000 条记录怎么办?这就是我们进行无限滚动的原因。

无论如何,这就是为什么你会在我的 base.html 中看到所有 Bootstrap 内容

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>{% block title %}Some Title{% endblock %}</title>
    <style>
        .htmx-indicator {
            opacity: 0;
            transition: opacity 500ms ease-in;
        }

        .htmx-indicator.htmx-request {
            opacity: 1
        }

        .htmx-request.htmx-indicator {
            opacity: 1
        }
        table, 
        th,
        td {
            border: 1px solid lightgray;
            border-collapse: separate;
            padding: 10px 5px;
        }
    </style>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link rel="stylesheet" type="text/css"
        href="http://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.16.0/extensions/filter-control/bootstrap-table-filter-control.css">
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.13/css/bootstrap-multiselect.css">
    <link rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap-select.min.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.10/css/all.css"
        integrity="sha384-+d0P83n9kaQMCwj8F4RJB66tzIwOKmrdb46+porD/OvrJ+37WqIM7UoBtwHO6Nlg" crossorigin="anonymous">
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
</head>

<body>
    {% include 'navbar.html' %}
    <div class="container">
        <div class="row row-lg-4">
            <div class="col">
                {% block head %}
                {% include 'head.html' %}
                {% endblock %}
            </div>
        </div>
        {% include 'messages.html' %}
        {% block body %}
        <div class="row">
            <div class="col-lg-9">
                {% block leftbody %}
                {% endblock %}
            </div>
            <div class="col-lg-3 rounded">
                {% block rightbody %}

                {% endblock %}
            </div>
            <div>
                {% block notetext %}
                {% endblock %}
            </div>
            <div class="table">
                {% block sampledata %}
                {% endblock %}
            </div>

        </div>
        {% endblock %}
        {% include 'footer.html' %}
    </div>
</body>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"
    integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"
    crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
    integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
    crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
    integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
    crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/js/bootstrap-select.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.13/js/bootstrap-multiselect.js"></script>
<script src="https://unpkg.com/[email protected]/dist/bootstrap-table.min.js"></script>
<script
    src="https://unpkg.com/[email protected]/dist/extensions/filter-control/bootstrap-table-filter-control.min.js"></script>
<script src="https://unpkg.com/[email protected]"
    integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"
    defer></script>
</body>

</html>

备注:

  • 在这种情况下,我们将使用无限表而不是使用 Bootstrap 交互式表 - 这意味着任何样式细节都将丢失,因此我们必须将它们带回来
  • 我们还设计了 htmx 指示器 - YMMV,但它似乎对我没有任何作用
  • 这里的好处是,我们真的不必更改我们的基本模板 - 只需添加一些仅适用于我们的无限滚动页面的内容

模板 - 页面

{% from "macros.html" import htmx_infinite_table_rows %}
{% if 'hx_request' in request.headers -%}
{% call(record) htmx_infinite_table_rows(records, 'scroll.home') -%}

<!-- once we're infinitely scrolling, we're just calling this block -->

<td>{{ record.Package }}</td>
<td>{{ record.Module }}</td>
<td style="background-color:#F0F0F0"><a href="{{ url_for('scroll.afFunction', afFunctionID=record.id) }}"
        data-toggle="popover" title="[Hover help text]" data-content="[Explain what's behind the link]">{{
        record.Function }}</a> </td>
<td>{{ record.Description }}</td>

{% endcall -%}

{% else -%}

<!-- On first load, we are *not* infinitely scrolling, so we setup the page -->

{% extends "base.html" %}

{% block body %}
<table>
    <thead>
        <tr>
            <th>Package</th>
            <th>Module</th>
            <th>Function</th>
            <th>Function Description</th>
        </tr>
    </thead>
    <tbody>
        {% call(record) htmx_infinite_table_rows(records, 'scroll.home') -%}
        <td>{{ record.Package }}</td>
        <td>{{ record.Module }}</td>
        <td style="background-color:#F0F0F0"><a href="{{ url_for('scroll.afFunction', afFunctionID=record.id) }}"
                data-toggle="popover" title="[Hover help text]" data-content="[Explain what's behind the link]">{{
                record.Function }}</a> </td>
        <td>{{ record.Description }}</td>

        {% endcall -%}
    </tbody>
</table>

<!-- On first load, we are *not* infinitely scrolling, so we setup the page -->

<img class="htmx-indicator" width="60" src="/static/loading.gif"></center>


{% endblock %}

<!-- Just a reminder - the rest of the base template will be here (footer, scripts, etc.) -->

{% endif -%}

备注:

  • 如果是第一次加载,我们会正常运行页面,但需要注意的是,我们使用调用来表示

    <tr>
    - 回想一下宏,如果它没有执行无限滚动加载,那么它只是返回这个:

    {%其他-%}

    <tr>
    {% endif -%}

  • 如果它正在进行无限滚动加载,那么它会调用第一个块

  • 在某种程度上,我们需要做的是在一个完整的页面中创建一个开放式表格,因此无论页码是多少,我们都需要最后一行来触发新页面的加载

  • 最好将整行放在单独的页面中,然后每次都调用它

控制器

这很简单,这是一个使用 Mongo 的版本

@scroll_blueprint.route('/<int:page>')
@scroll_blueprint.route('/')
def home(page=1):
    theseAFFunctions = Mongo_AF_Functions.objects.order_by("PackageModuleFunction").paginate(page, 10)
    return render_template("scrollfunctionsAF.html",
                    records=theseAFFunctions)

备注:

  • paginate 的第 10 个参数是每“页”的记录数

现在,这里有一个关键点 - 我们将宏称为“htmx_infinite_table_rows” - 这只是我们编造的一个名称。除了表格行之外,我们还能做些什么吗?是的,我们只需要找出“给定当前数据的表中最后一行,这应该触发更多数据的加载”的 html 等效项。

这是带有 Bootstrap 卡的版本:

宏观

{% macro htmx_infinite_cards(paginate, endpoint) -%}
  {% for item in paginate.items -%}
    {% if loop.last and paginate.has_next -%}
    <div class="row"
      hx-get="{{ url_for(endpoint, page=paginate.next_num) }}"
      hx-trigger="revealed"
      hx-swap="afterend"
    >
    {% else -%}
    <div class="row">
    {% endif -%}
      {{ caller(item) }}
    </div>
  {% endfor -%}
{% endmacro -%}

模板

{% from "macros.html" import htmx_infinite_cards %}
{% if 'hx_request' in request.headers -%}
{% call(record) htmx_infinite_cards(records, 'scroll.home') -%}

<!-- once we're infinitely scrolling, we're just calling this block -->
    <!-- remember the <row> element is in the call so we don’t need it here -->
    <div class="card">
        <div class="card-body">
            <h4 class="card-title">
                {{ record.Function }}
            </h4>
            <div class="card-text">
                <dl class="row">

                    <dt class="col-sm-4">Function Name:</dt>
                    <dd class="col-lg-5">{{ record.Function }}</dd>
                    <dt class="col-sm-4">Package:</dt>
                    <dd class="col-lg-5">{{ record.Packages }}</dd>
                    <dt class="col-sm-4">Module</dt>
                    <dd class="col-lg-5">{{ record.Module }}</dd>
                    <dt class="col-sm-4">Description</dt>
                    <dd class="col-sm-5">{{ record.Description}}</dd>
                
                </dl>
            </div>
            <div class="card-footer">

                <a href="{{ url_for('scroll.afFunction', afFunctionID=record.id) }}"
            data-toggle="popover" title="[Hover help text]" data-content="[Explain what's behind the link]">{{
            record.Function }}</a>
            </div>
        </div>
    </div>
{% endcall -%}

{% else -%}

<!-- On first load, we are *not* infinitely scrolling, so we setup the page -->

{% extends "base.html" %}

{% block body %}

<div class="col-lg-9">
    {% call(record) htmx_infinite_cards(records, 'scroll.home') -%}
            <!-- the <row> element is in the call -->
        <div class="card">
            <div class="card-body">
                <h4 class="card-title">
                    {{ record.Function }}
                </h4>
                <div class="card-text">
                    <dl class="row">

                        <dt class="col-sm-4">Function Name:</dt>
                        <dd class="col-lg-5">{{ record.Function }}</dd>
                        <dt class="col-sm-4">Package:</dt>
                        <dd class="col-lg-5">{{ record.Packages }}</dd>
                        <dt class="col-sm-4">Module</dt>
                        <dd class="col-lg-5">{{ record.Module }}</dd>
                        <dt class="col-sm-4">Description</dt>
                        <dd class="col-sm-5">{{ record.Description}}</dd>
                    
                    </dl>
                </div>
                <div class="card-footer">

                    <a href="{{ url_for('scroll.afFunction', afFunctionID=record.id) }}"
                data-toggle="popover" title="[Hover help text]" data-content="[Explain what's behind the link]">{{
                record.Function }}</a>
                </div>
            </div>
        </div>

    {% endcall -%}
</div>



<!-- On first load, we are *not* infinitely scrolling, so we setup the page -->

<img class="htmx-indicator" width="60" src="/static/loading.gif"></center>


{% endblock %}

<!-- Just a reminder - the rest of the base template will be here (footer, scripts, etc.) -->

{% endif -%}

参考资料:

© www.soinside.com 2019 - 2024. All rights reserved.