Can you pass an element to a function within the template in Vue?

问题: I'm trying to calculate and set an element's max-height style programmatically based on the number of children it has. I have to do this on four separate elements, each wit...

问题:

I'm trying to calculate and set an element's max-height style programmatically based on the number of children it has. I have to do this on four separate elements, each with a different number of children, so I can't just create a single computed property. I already have the logic to calculate the max-height in the function, but I'm unable to pass an element from the template into a function.

I've tried the following solutions with no luck:

  1. <div ref="div1" :style="{ maxHeight: getMaxHeight($refs.div1) }"></div>
    This didn't work because $refs is not yet defined at the time I'm passing it into the function.

  2. Trying to pass this or $event.target to getMaxHeight(). This didn't work either because this doesn't refer to the current element, and there was no event since I'm not in a v-on event handler.

The only other solution I can think of is creating four computed properties that each call getMaxHeight() with the $ref, but if I can handle it from a single function called with different params, it would be easier to maintain. If possible, I would like to pass the element itself from the template. Does anyone know of a way to do this, or a more elegant approach to solving this problem?


回答1:

Making a custom directive thata operates directly on the div element would probably be your best shot. You could create a directive component like:

export default {
  name: 'maxheight',
  bind(el) {
    const numberOfChildren = el.children.length;

    // rest of your max height logic here

    el.style.maxHeight = '100px';
  }
}

Then just make sure to import the directive in the file you plan on using it, and add it to your div element:

<div ref="div1" maxheight></div>

回答2:

A cheap trick I learned with Vue is that if you require anything in the template that isnt loaded when the template is mounted is to just put a template with a v-if on it:

<template v-if="$refs">
   <div ref="div1" :style="{ maxHeight: getMaxHeight($refs.div1) }"></div>
</template>

around it. This might look dirty at first, but the thing is, it does the job without loads of extra code and time spend and prevents the errors.

Also, a small improvement in code length on your expandable-function:

const expandable = el => el.style.maxHeight = 
    ( el.classList.contains('expanded') ?  
        el.children.map(c=>c.scrollHeight).reduce((h1,h2)=>h1+h2)
        : 0 ) + 'px';

回答3:

I ended up creating a directive like was suggested. It tries to expand/compress when:

  • It's clicked
  • Its classes change
  • The element or its children update


Vue component:

<button @click="toggleAccordion($event.currentTarget.nextElementSibling)"></button>
<div @click="toggleAccordion($event.currentTarget)" v-accordion-toggle>
    <myComponent v-for="data in dataList" :data="data"></myComponent>
</div>

.....

private toggleAccordion(elem: HTMLElement): void {
    elem.classList.toggle("expanded");
}


Directive: Accordion.ts

const expandable = (el: HTMLElement) => {
    if (el.classList.contains("expanded")) { // When the class ".expanded" is added, expand list
        const children: HTMLCollection = el.children;
        let totalHeight = 0;

        for (let i = 0; i < children.length; i++) {
            totalHeight += children[i].scrollHeight;
        }
        el.style.maxHeight = `${totalHeight}px`;
    } else { // Not ".expanded"
        el.style.maxHeight = "0px";
    }
};

Vue.directive("accordion-toggle", {
    bind: (el: HTMLElement, binding: any, vnode: any) => {
        el.onclick = ($event: any) => {
            expandable($event.currentTarget) ; // When the element is clicked
        };

        // If the classes on the elem change, like another button adding .expanded class
        const observer = new MutationObserver(() => expandable(el));        
        observer.observe(el, {
            attributes: true,
            attributeFilter: ["class"],
        });
    },
    componentUpdated: (el: HTMLElement) => {
        expandable(el); // When the component (or its children) update
    }
});
  • 发表于 2019-03-28 19:29
  • 阅读 ( 171 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除