Как добавить поле ввода рядом с меткой оси Chart.js? - PullRequest
0 голосов
/ 22 марта 2019

Я создал опрос в режиме реального времени с помощью Chart.js и хочу, чтобы пользователь мог выбрать свою опцию, установив флажок слева от метки оси Y. У меня проблемы с выяснением, как сделать это правильно с Chart.js

Моей первой попыткой было поместить поле ввода за пределы холста, но оно очень ненадежно с опциями динамического опроса по мере их роста. Это довольно плохо, потому что высота гистограммы будет варьироваться в зависимости от количества доступных вариантов, поэтому поля ввода становятся беспорядочными, поскольку отображаемые параметры являются динамическими.

Вот изображение того, чего я в конечном итоге пытаюсь достичь на внешнем интерфейсе. Если пользователь выбирает поле, опрос увеличивается, чтобы отразить выбор, выбранный пользователем.

enter image description here

Ниже приведен мой код, в котором я сейчас застрял. Я застрял, пытаясь понять это в течение достаточно долгого времени. Любая помощь будет принята с благодарностью!

import { Component, OnInit } from '@angular/core';
// import { Chart } from 'chart.js';
import * as Ably from 'ably';
import * as Chart from 'chart.js';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable, Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { FirebaseService } from '../services/firebase.service';

@Component({
  selector: 'app-vote-chart',
  templateUrl: './vote-chart.component.html',
  styleUrls: ['./vote-chart.component.scss']
})
export class VoteChartComponent implements OnInit {
  // Attributes
  ably: any
  receiveChanel: any
  chart: any
  users: any
  polls:any = [];
  poll:any;
  votes:any = [];
  labels:any = [];
  poll_type:string = "";

  constructor(db: AngularFirestore, private firebaseService: FirebaseService) {}

  ngOnInit() {
    this.firebaseService.getPoll("key").subscribe(res => {
      const poll_data:any = res.payload.data();
      this.poll = {
        id: res.payload.id,
        helper_text: poll_data.helper_text,
        poll_type: poll_data.poll_type,
        scoring_type: poll_data.scoring_type,
        user: poll_data.user.id,
        choices: []
      };
      this.poll_type = this.poll.poll_type == 2 ? "Pick One" : "Pick Two";
      this.firebaseService.getChoices('key').subscribe(res => {
        res.forEach((choice) => {
          const choice_data:any = choice.payload.doc.data()
          this.poll.choices.push({
            id: choice.payload.doc.id,
            text: choice_data.text,
            votes: choice_data.votes
          });
          this.votes.push(choice_data.votes);
          this.labels.push(choice_data.text);
        });
        console.log("Poll updated!", this.poll);
      });
    });
    this.ably = new Ably.Realtime("key")
    // Attach to channel
    this.receiveChanel = this.ably.channels.get('vote-channel12')
    // Ably subscription
    this.receiveChanel.subscribe("update", (message: any) => {

      var canvas =  <HTMLCanvasElement> document.getElementById("chartjs-2")
      var ctx = canvas.getContext("2d");

      this.chart = new Chart(ctx, {
        type: 'horizontalBar',
        data: {
          labels: this.labels,
          datasets: [{
            label: this.poll_type,
            data: this.votes,
            fill: false,
            backgroundColor: [
              "rgba(255, 99, 132, 0.2)",
              "rgba(255, 159, 64, 0.2)",
              "rgba(255, 205, 86, 0.2)",
              "rgba(75, 192, 192, 0.2)",
              "rgba(54, 162, 235, 0.2)",
              "rgba(153, 102, 255, 0.2)",
              "rgba(201, 203, 207, 0.2)"

            ],
            borderColor: [
              "rgb(255, 99, 132)",
              "rgb(255, 159, 64)",
              "rgb(255, 205, 86)",
              "rgb(75, 192, 192)",
              "rgb(54, 162, 235)",
              "rgb(153, 102, 255)",
              "rgb(201, 203, 207)"
            ],
            borderWidth: 1
          }]
        },
        options: {
          events: ["touchend", "click", "mouseout"],
          onClick: function(e) {
            console.log("clicked!", e);
          },
          tooltips: {
            enabled: true
          },
          title: {
            display: true,
            text: this.poll_type,
            fontSize: 14,
            fontColor: '#666'
          },
          legend: {
            display: false
          },
          maintainAspectRatio: true,
          responsive: true,
          scales: {
            xAxes: [{
              ticks: {
                beginAtZero: true,
                precision: 0
              }
            }]
          }
        }
      })
      console.log("Poll", this.poll);
    });
  }
}

Шаблон кода, встроенный стиль для упрощения просмотра

<div id="chart_and_labels_container" style="min-width:620px !important;">
  <div style="float:left; line-height: 175px; display: inline-grid; padding-top: 55px; margin: 0;">
    <input type="checkbox" (click)="vote(1)" style="width:57px; height: 30px;"/>
    <input type="checkbox" (click)="vote(-1)" style="width:57px; margin-top: 10px; height: 30px;"/>
    <input type="checkbox" (click)="vote(-1)" style="width:57px; margin-top: 10px; height: 30px;"/>
  </div>
  <div id="canvasDiv" style="width: 425px; float:left;">
  <canvas id="chartjs-2" ></canvas>
  </div>
</div>

** ОБНОВЛЕНИЕ **

Если невозможно получить поля ввода на холсте, может кто-нибудь дать совет о том, как лучше всего убедиться, что поля ввода надежно совпадают с динамическими вариантами? Т.е. размер текста для надписей различен для 2 вариантов по сравнению с 5 вариантами. Как правильно и надежно выстроить поля ввода вне холста?

1 Ответ

1 голос
/ 22 марта 2019

Предполагая, что комментарий Марка является правильным (что, по-видимому, имеет место), чтобы ответить на ваше "Каково альтернативное решение для выравнивания входных данных" ...

Обновленный ответ

Использование display: flex упрощает автоматическую настройку размера и / или расстояния между флажками, если высота контейнера, обертывающего столбцы на холсте, всегда одинакова независимо от количества доступных вариантов.

.chart-container {
  display: flex;
}

.chart {
  height: 200px;
}

.checkbox-container {
  display: flex;
  flex-direction: column;
  height: 128px;    /* height - adjust as needed */
  margin-top: 38px; /* positioning - adjust as needed */
  
  background: white;  /* demo purpose only - hide image checkboxes */
  position: absolute; /* demo purpose only - hide image checkboxes */
  left: 10px;         /* demo purpose only - hide image checkboxes */
  padding: 0 8px;     /* demo purpose only - hide image checkboxes */
  z-index: 1;         /* demo purpose only - hide image checkboxes */
} 

.checkbox {
  cursor: pointer;
  height: 100%;
  width: 100%;
  min-width: 16px; /* minimum checkbox size - adjust as needed */
}
<!-- 3 bars example -->
<div class="chart-container">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
</div>

<!-- 4 bars examples -->
<div class="chart-container">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
</div>

<!-- 5 bars examples -->
<div class="chart-container">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
</div>

Оригинальный ответ

Вы можете ...

  • обернуть холст в контейнер div с position: relative
  • создайте контейнер для ваших флажков с помощью position: absolute
  • добавьте свои флажки в контейнер position: absolute и используйте margin-bottom, чтобы они идеально совпали с вашим графиком

Если количество опций влияет на высоту столбцов диаграммы, вам также необходимо изменить значения CSS либо соответственно с помощью ...

  • javascript: document.querySelector('.checkbox').style.height = ...
  • с использованием директивы Angular [style]: [style.height.px]="..."
  • с использованием классов CSS в зависимости от количества флажков / сиблингов внутри контейнера флажков: https://stackoverflow.com/a/12198561/5583283

Ниже приведен рабочий примериспользуя 3-й вариант выше, классы CSS, которые изменяют значения в зависимости от количества флажков / родных элементов внутри checkbox-container.

Вся идея состоит в том, чтобы высота флажков оставалась равной высоте столбцов диаграммы и расстояниюмежду флажками равнодо расстояния между столбцами диаграммы.

Обратите внимание, что все значения пикселей для выравниваний относятся к тому, что вы ожидаете, и с ними можно играть, но я думаю, что это даст вам общую картину.

Надеюсь, это поможет!

.chart-container {
  position: relative;
}

.chart {
  height: 200px;
}

.checkbox-container {
  background: white;  /* for demo purpose - hides image checkboxes */
  padding-right: 8px; /* for demo purpose - hides image checkboxes */
  position: absolute;
  top: 44px;          /* adjust as needed */
  left: 8px;          /* adjust as needed */
} 

.checkbox {
  display: block;
  margin-bottom: 15px; /* adjust as needed */
  height: 28px;        /* desired checkbox height */
  width: 28px;         /* desired checkbox width */
  cursor: pointer;
}

/* 5 checkboxes */
.checkbox:first-child:nth-last-child(5),
.checkbox:first-child:nth-last-child(5) ~ .checkbox {
  margin-bottom: 6px; /* adjust as needed */
  height: 18px;       /* desired checkbox height */
  width: 18px;        /* desired checkbox width */
}
<!-- 3 bars example -->
<div class="chart-container">
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
</div>

<!-- 5 bars examples - sorry didn't have an image with 5 bars -->
<div class="chart-container">
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
</div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...