【问题标题】:How to implement duration picker with HTML5 or/with Angular8, with hours more than 24?如何使用 HTML5 或/使用 Angular8 实现持续时间选择器,时间超过 24 小时?
【发布时间】:2020-03-11 12:49:38
【问题描述】:

我正在尝试实现一个控件,使用任一

 <input type="time"/>

或只是与

  <input type="text"/>

并实现一个持续时间选择器控件,该控件的小时格式可以超过 24,例如 000:00:00 或 hhh:mm:ss,并且没有 am/pm 选项(时间的默认输入类型的格式为 am/ pm 格式,这在我的情况下没有用)。 要求是能够使用向上和向下键来增加减少持续时间,就像 HTML 的默认输入类型时间一样。

是否有任何原生的HTMLangular 或材质组件? 或者有没有办法使用正则表达式/模式或其他东西来实现这一点?

【问题讨论】:

  • 也许正在寻找类似this 的东西? Here 就是一个例子
  • @xDrago 否。您提供的链接提供了单独选择所有值的选项。寻找可以在提供格式 000:00:00 或 hhh:mm:ss 的单个控件/文本框中执行相同操作的内容,即超过 24 小时或 3 位数字。
  • @Boobalan。首先,请添加您必须建议的任何解决方案作为答案。其次,我特别提到了duration必须能够输入24小时以上。您建议的解决方案只处理少于 24 小时的情况,这也可能是其他明智的默认输入类型时间,但在这种情况下不是必需的。

标签: html angular angular-material angular8


【解决方案1】:

我能想到的一种方法是编写您的自定义控件(@Allabakash 也提到过)。对于 Native HTML,控件可以是这样的:

window.addEventListener('DOMContentLoaded', (event) => {
            document.querySelectorAll('[my-duration-picker]').forEach(picker => {
                //prevent unsupported keys
                const acceptedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowDown', 'ArrowUp'];
                const selectFocus = event => {
                    //get cursor position and select nearest block;
                    const cursorPosition = event.target.selectionStart;
                    "000:00:00" //this is the format used to determine cursor location
                    const hourMarker = event.target.value.indexOf(":");
                    const minuteMarker = event.target.value.lastIndexOf(":");
                    if (hourMarker < 0 || minuteMarker < 0) {
                        //something wrong with the format. just return;
                        return;
                    }
                    if (cursorPosition < hourMarker) {
                        event.target.selectionStart = 0; //hours mode
                        event.target.selectionEnd = hourMarker;
                    }
                    if (cursorPosition > hourMarker && cursorPosition < minuteMarker) {
                        event.target.selectionStart = hourMarker + 1; //minutes mode
                        event.target.selectionEnd = minuteMarker;
                    }
                    if (cursorPosition > minuteMarker) {
                        event.target.selectionStart = minuteMarker + 1; //seconds mode
                        event.target.selectionEnd = minuteMarker + 3;
                    }
                }
                const insertFormatted = (inputBox, secondsValue) => {
                    let hours = Math.floor(secondsValue / 3600);
                    secondsValue %= 3600;
                    let minutes = Math.floor(secondsValue / 60);
                    let seconds = secondsValue % 60;
                    minutes = String(minutes).padStart(2, "0");
                    hours = String(hours).padStart(3, "0");
                    seconds = String(seconds).padStart(2, "0");
                    inputBox.value = hours + ":" + minutes + ":" + seconds;
                }
                const increaseValue = inputBox => {
                    const rawValue = inputBox.value;
                    sectioned = rawValue.split(':');
                    let secondsValue = 0
                    if (sectioned.length === 3) {
                        secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
                    }
                    secondsValue += 1;
                    insertFormatted(inputBox, secondsValue);
                }
                const decreaseValue = inputBox => {
                    const rawValue = inputBox.value;
                    sectioned = rawValue.split(':');
                    let secondsValue = 0
                    if (sectioned.length === 3) {
                        secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
                    }
                    secondsValue -= 1;
                    if (secondsValue < 0) {
                        secondsValue = 0;
                    }
                    insertFormatted(inputBox, secondsValue);
                }
                const validateInput = event => {
                    sectioned = event.target.value.split(':');
                    if (sectioned.length !== 3) {
                        event.target.value = "000:00:00"; //fallback to default
                        return;
                    }
                    if (isNaN(sectioned[0])) {
                        sectioned[0] = "000";
                    }
                    if (isNaN(sectioned[1]) || sectioned[1] < 0) {
                        sectioned[1] = "00";
                    }
                    if (sectioned[1] > 59 || sectioned[1].length > 2) {
                        sectioned[1] = "59";
                    }
                    if (isNaN(sectioned[2]) || sectioned[2] < 0) {
                        sectioned[2] = "00";
                    }
                    if (sectioned[2] > 59 || sectioned[2].length > 2) {
                        sectioned[2] = "59";
                    }
                    event.target.value = sectioned.join(":");
                }
                const controlsDiv = document.createElement("div");
                const scrollUpBtn = document.createElement("button");
                const scrollDownBtn = document.createElement("button");
                scrollDownBtn.textContent = " - ";
                scrollUpBtn.textContent = " + ";
                scrollUpBtn.addEventListener('click', (e) => {
                    increaseValue(picker);
                });
                scrollDownBtn.addEventListener('click', (e) => {
                    decreaseValue(picker);
                });
                picker.parentNode.insertBefore(scrollDownBtn, picker.nextSibling);
                picker.parentNode.insertBefore(scrollUpBtn, picker.nextSibling);
                picker.value = "000:00:00";
                picker.style.textAlign = "right"; //align the values to the right (optional)
                picker.addEventListener('keydown', event => {
                    //use arrow keys to increase value;
                    if (event.key == 'ArrowDown' || event.key == 'ArrowUp') {
                        if(event.key == 'ArrowDown'){
                        decreaseValue(event.target);
                        }
                        if(event.key == 'ArrowUp'){
                        increaseValue(event.target);
                        }
                        event.preventDefault(); //prevent default
                    }

                    if (isNaN(event.key) && !acceptedKeys.includes(event.key)) {
                        event.preventDefault(); //prevent default
                        return false;
                    }
                });
                picker.addEventListener('focus', selectFocus); //selects a block of hours, minutes etc
                picker.addEventListener('click', selectFocus); //selects a block of hours, minutes etc
                picker.addEventListener('change', validateInput);
                picker.addEventListener('blur', validateInput);
                picker.addEventListener('keyup', validateInput);
            });
        });
&lt;input type="text" my-duration-picker&gt;&lt;/input&gt;

在 Google Chrome 78 上测试并运行。我稍后会做一个 Angular 版本。

对于 Angular 版本,您可以编写自己的自定义指令并将其导入到您的 app-module-ts 声明中。请参阅 stackblitz 上的此示例:

应用演示:https://angular-xbkeoc.stackblitz.io

代码:https://stackblitz.com/edit/angular-xbkeoc

更新:随着时间的推移,我开发和改进了这个概念。你可以在这里查看选择器?https://nadchif.github.io/html-duration-picker.js/

【讨论】:

    【解决方案2】:

    查看此解决方案,https://github.com/FrancescoBorzi/ngx-duration-picker。它提供了您正在寻找的选项。

    这里是演示 - https://embed.plnkr.co/1dAIGrGqbcfrNVqs4WwW/

    演示显示 Y:M:W:D:H:M:S 格式。您可以使用文档中定义的标志隐藏参数。


    由于您正在寻找具有单一输入的持续时间选择器,因此创建自己的组件会很方便。 您可以考虑格式化程序和解析器的概念。

    查看有助于您实现这一目标的主题。

    https://netbasal.com/angular-formatters-and-parsers-8388e2599a0e
    https://stackoverflow.com/questions/39457941/parsers-and-formatters-in-angular2
    

    这里是更新的示例演示 - https://stackblitz.com/edit/hello-angular-6-yuvffz

    您可以使用 keyup/keydown 事件函数实现增加/减少功能。

    handle(event) {
        let value = event.target.value; //hhh:mm:ss
        if(event.key === 'ArrowUp') {
            console.log('increase');
        } else if (event.key === 'ArrowDown') {
            console.log('decrease');
        } else {
    
           //dont allow user from entering more than two digits in seconds
        }
    }
    

    您需要考虑的验证 ::

      - If user enters wrong input, show error message / block from entering anything other than numbers
      - allowing only unit specific digits - (Ex :: for hr - 3 digits, mm - 2 digits etc as per your requirement)
    

    【讨论】:

    • 这看起来很有价值,很有用,但不能解决我的用例。这是使用多个控件或文本框。我的要求是在单个控件而不是一组控件中具有相同的功能,因此用户只需键入或增加格式即可。
    • 好的,您需要创建自己的格式化程序和解析器函数,您可以查看这个 - netbasal.com/angular-formatters-and-parsers-8388e2599a0e。这是我创建的示例演示 - stackblitz.com/edit/hello-angular-6-yuvffz 只是为了提供一个想法。您需要实现鼠标上键和下键功能以及验证。
    • 格式化程序和解析器似乎对我的用例很有用,并且您提供的示例似乎可以通过一些调整来使用。更符合我的要求。您能否使用可以处理上下的格式化程序整理一个示例并将其发布为答案而不是评论。
    • 我已经更新了答案,希望对您有帮助,如果有任何疑问,请告诉我。
    【解决方案3】:

    要做一些更有趣的事情或让它看起来像互动,你可以使用 flipclock.js 看起来很酷,使用起来也是可行的。

    这是链接:-

    http://flipclockjs.com/

    【讨论】:

      【解决方案4】:

      您可以尝试使用数字作为类型:

      &lt;input type="min" min="0" max="60"&gt;

      演示: https://stackblitz.com/edit/angular-nz9hrn

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-08
        • 1970-01-01
        • 1970-01-01
        • 2014-01-28
        • 2018-04-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-17
        相关资源
        最近更新 更多