问题
LiveView折叠我打开的元素。
详细信息
我有一个在页面加载时折叠的元素:
<a class="collapse_trigger">...</a>
<div class="is-collapsible">
# content updated by liveview
</div>如果用户单击可折叠对象,则该可折叠对象具有.is-active类。
<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更改:
// 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;
}
}发布于 2021-02-12 19:36:05
仅限前端更改
为了使用仅限前端的选项,我建议使用以下选项。
为了简单起见,我将使用普通的javascript。
我们需要修改按钮,并编写存储状态的函数(我使用的是简单的localStorage)
<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>然后,我们需要编写从本地存储恢复该状态的函数
function restoreCollapsibleState() {
var collapsibleEl = document.getElementById('collapseExample');
if (localStorage.getItem('collapsibleState')) {
collapsibleEl.classList.add('is-active')
}
}最后,我们需要将该函数绑定到phoenix_live_view套接字更新,我们需要在窗口加载后立即执行此操作。
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模板修改为以下内容
<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>因此,如果有任何结果,并且查询不为空,则不会折叠。
至于你的情况,我想也是一样的。
<a class="collapse_trigger">...</a>
<div class="is-collapsible <% if (@results && String.trim(@query) != "") do: "is-active", else: "" %>">
# content
</div>发布于 2021-02-21 21:32:12
基于@zhisme的回答,我创建了一个可行的解决方案。唯一的要求是可折叠对象必须有id。
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);发布于 2020-10-28 02:18:03
一个非常著名的解决方案是在mount()上对该元素执行addEventListener操作,然后将其放回update()上。这个想法是这样的
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) },
}https://stackoverflow.com/questions/64550907
复制相似问题