首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Elixir phoenix LiveView在更新时可折叠

Elixir phoenix LiveView在更新时可折叠
EN

Stack Overflow用户
提问于 2020-10-27 16:50:45
回答 3查看 594关注 0票数 6

问题

LiveView折叠我打开的元素。

详细信息

我有一个在页面加载时折叠的元素:

代码语言:javascript
复制
<a class="collapse_trigger">...</a>
<div class="is-collapsible">
# content updated by liveview
</div>

如果用户单击可折叠对象,则该可折叠对象具有.is-active类。

代码语言:javascript
复制
<a class="collapse_trigger">...</a>
<div class="is-collapsible is-active">
# content
</div>

但是liveview删除了这个类。你知道如何确保liveview忽略父元素<div class="is-collapsible is-active">,而是照看孩子吗?我的第一个想法是phx-update="ignore"。但现在我想我需要把可折叠的逻辑放到后端。:/

更多信息

我使用bulma-collapsible,只做了一个css更改:

代码语言:javascript
复制
// the following is necessary because liveview does not work well with the bulma-collapsible. Otherwise elements would stay open but can be closed by clicking them twice.
.is-collapsible{
  height: 0;
  &.is-active{
    height: auto;
  }
}
EN

回答 3

Stack Overflow用户

发布于 2021-02-12 19:36:05

仅限前端更改

为了使用仅限前端的选项,我建议使用以下选项。

  • 我们需要存储该可折叠元素的状态。
  • 我们需要在每次更新套接字通道时恢复该元素的可折叠状态

为了简单起见,我将使用普通的javascript。

我们需要修改按钮,并编写存储状态的函数(我使用的是简单的localStorage)

代码语言:javascript
复制
<a class="collapse_trigger" onclick="memoizeCollapsibleState()">...</a>

<script type="text/javascript">
  function memoizeCollapsibleState() {
    if (!localStorage.getItem('collapsibleState')) {
      localStorage.setItem('collapsibleState', true)
    } else {
      localStorage.removeItem('collapsibleState')
    }
  }
</script>

然后,我们需要编写从本地存储恢复该状态的函数

代码语言:javascript
复制
function restoreCollapsibleState() {
  var collapsibleEl = document.getElementById('collapseExample');

  if (localStorage.getItem('collapsibleState')) {
    collapsibleEl.classList.add('is-active')
  }
}

最后,我们需要将该函数绑定到phoenix_live_view套接字更新,我们需要在窗口加载后立即执行此操作。

代码语言:javascript
复制
window.onload = init;

function init() {
  liveSocket.getSocket().channels[0].onMessage = function (e, t, n) {
    setTimeout(restoreCollapsibleState, 10)
    return t
  } 
}

setTimeout函数的目的是使套接字更新是异步操作,我们需要添加一些延迟才能恢复可折叠状态。10ms似乎还可以,但我们可以将其更改为任何其他去抖动函数,我只是为了简单起见使用它,认为这是概念的证明。

仅后端更改

我刚刚用默认的live phoenix结构做了一个例子。

mix phx.new my_app --live

将bootstrap添加到其中(但我认为bulma基本上遵循相同的规则),并将phoenix live模板修改为以下内容

代码语言:javascript
复制
<div class="collapse <%= if (@results && String.trim(@query) != ""), do: "show", else: "" %>" id="collapseExample">
  <div class="card card-body">
    <%= for {app, _vsn} <- @results do %>
      <p value="<%= app %>"><%= app %></p>
    <% end %>
  </div>
</div>

因此,如果有任何结果,并且查询不为空,则不会折叠。

至于你的情况,我想也是一样的。

代码语言:javascript
复制
<a class="collapse_trigger">...</a>
<div class="is-collapsible <% if (@results && String.trim(@query) != "") do: "is-active", else: "" %>">
# content
</div>
票数 1
EN

Stack Overflow用户

发布于 2021-02-21 21:32:12

基于@zhisme的回答,我创建了一个可行的解决方案。唯一的要求是可折叠对象必须有id。

代码语言:javascript
复制
function initCollapsibles() {
  const bulmaCollapsibleInstances = bulmaCollapsible.attach('.is-collapsible');
  bulmaCollapsibleInstances.forEach(bulmaCollapsibleInstance => {
    const key = 'collapsible_' + bulmaCollapsibleInstance.element.id;
    // some id's from other pages might leak over if we don't clean the localstorage on page load
    localStorage.removeItem(key)

    bulmaCollapsibleInstance.on('before:expand', (e) => {
      localStorage.setItem(key, true)
    })

    bulmaCollapsibleInstance.on('after:collapse', (e) => {
      localStorage.removeItem(key)
    })
  });
}

function restoreCollapsiblesState() {
  const collapsibles = Array.prototype.slice.call(document.querySelectorAll('.is-collapsible'), 0);
  collapsibles.forEach(el => {
    if (localStorage.getItem('collapsible_' + el.id)) {
      // I'd love to call the collapsible open method, but haven't figured out how to get that instance without creating a new one or storing it to the window globally.
      el.classList.add('is-active')
    }
  });
}

document.addEventListener('phx:update', restoreCollapsiblesState);
document.addEventListener('DOMContentLoaded', initCollapsibles);
票数 0
EN

Stack Overflow用户

发布于 2020-10-28 02:18:03

一个非常著名的解决方案是在mount()上对该元素执行addEventListener操作,然后将其放回update()上。这个想法是这样的

代码语言:javascript
复制
Utils.onDetailsTagState = {
  mount(storeEl) {
    let detailsTag = storeEl.el.closest("details")
    if (detailsTag) {
      storeEl.expand = detailsTag.open
      detailsTag.addEventListener("toggle", event => {
        storeEl.expand = event.target.open
      })
    }
  },
  update(storeEl) {
    let detailsTag = storeEl.el.closest("details")
    if (detailsTag) { detailsTag.open = storeEl.expand }
  }
}

Hooks.callaspable = {
  mounted() { Utils.onDetailsTagState.mount(this) },
  updated() { Utils.onDetailsTagState.update(this) },
}
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64550907

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档