LINUX.ORG.RU

vue.js и состояние кнопок

 ,


0

2

Я совсем не веб-разработчик, но для самообучения разбираюсь с vue.js, хочу сделать веб-интерфейс для запуска скриптов на сервере.
Скрипты долгоиграющие, типа запуска и сбора логов и требующие взаимодействия с пользователем.
И никак не могу понять, как реализовать следующую логику с помощью vue.js:
1. Кнопка активна, на ней написано «Start»
2. Пользователь нажимает кнопку, она генерирует ajax запрос, пока ответ не получен - кнопка неактивна(чтобы предотвратить множественные запуски скрипта на сервере) и на ней написано «Starting...»
3. a) При успешном получении ответа от сервера, думаем что скрипт успешно запустился и кнопка должна перейти в активное состояние с надписью «Stop» b) При неуспешном ответе кнопка опять должна вернуться в состояние «Start»
4. При нажатии на «Stop» генерируется ajax запрос, кнопка опять становится неактивной и на ней написано «Stopping...»
5. При получении ответа об успешной остановке скрипта кнопка опять переходит в «Start».

Вопросы:
Логика для v-bind:class слишком сложная, я пока не вижу, как можно переключать состояния просто классами.
1) По идее нужно запихать это всё в @click и методах манипулировать классами? Если да - то как?
2) Кнопок может быть произвольное количество, как надо разделять флаги и состояния в data в таком случае?
Неужели надо плодить флаги: button1_disabled, button2_disabled...buttonN_disabled?

★★★★★
Ответ на: комментарий от makoven

Скорее всего, много и долго пердолился бы со всеми этими скучными document.getElementById(«myButton1»), classList, addClass, removeClass, setAttribute() в которых ещё есть дух старой школы.

zolden ★★★★★
() автор топика

впринципе можно просто классами.

чтобы менять текст, нужно пользовать ::before или ::after.

AndreyKl ★★★★★
()
Ответ на: комментарий от AndreyKl

Спасибо, посмотрю в этом направлении

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

Конечно забавнее пердолиться десяти этажами абстракций (которые не продержатся и пары лет на волне хайпа) выше, пытаясь скрестить ежа с ужом так, чтобы эти абстракции не потекли

makoven ★★★★★
()

Код не проверял, но идея примерно такая. Если кнопок много, то каждая кнопка - компонент. Значения, которые нужно устанавливать задаёшь параметрами.

<script type='text/x-template' id='mycomp'>
  <button @click='doSomething()' :disabled='disabled' :value='val'></button>
</script>
Vue.component = ('mycomp', {
  data: function() {
    return {
      disabled: false,
      val: 'start',
    };
  },
  methods: {
    doSomething: function() {
      let component = this;
      component.disabled = true;
      let xhr = new XMLHttpRequest();
      xhr.open('POST', '/api/doSomething', true);
      xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
      xhr.onload = function() {
        let res = JSON.parse(xhr.responseText);
        // do something with res.
        component.val = 'stop';
        component.disabled = false;
      };
      xhr.send(data);
});
Ivan_qrt ★★★★★
()
Ответ на: комментарий от makoven

Ну я же ненастоящий сварщик, мне простительно

zolden ★★★★★
() автор топика
Ответ на: комментарий от Ivan_qrt

Спасибо за наводку, докурил документацию до компонентов, похоже это действительно то, что мне нужно

zolden ★★★★★
() автор топика
Ответ на: комментарий от ddidwyll

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

zolden ★★★★★
() автор топика

Логика для v-bind:class слишком сложная, я пока не вижу, как можно переключать состояния просто классами.

Чисто классами текст менять и не нужно особенно в Vue.

v-bind:class - тут ничего сложного нет, это объект ключами которого являются имена классов, а значения это свойства описанные в props, data или computed, в зависимости от результата преобразования в Boolean либо включают классы, либо выключают.

surefire ★★★
()
Ответ на: комментарий от zolden

без ноды

Работает так же как и vue.js, разница в наборе готовых компонентов и несколько плюшек. Всмысле компилируешь в бандл, ложишь в веб-сервер и всё, нода не нужна.

ddidwyll ★★★★
()
Ответ на: комментарий от ddidwyll

компилируешь в бандл

В том то и проблема, что ноутпад не умеет компилировать в бандл...

zolden ★★★★★
() автор топика
Ответ на: комментарий от Ivan_qrt

А как разделить параметры для каждой кнопки?
Мне надо разделить URL для AJAX запроса.
В шаблоне нельзя, так как он один для всего будет.
Вариант с вынесением onclick из шаблона и назначением обработчиков в каждом экземпляре:

<my-button @click='doSomething("/script1")'></my-button>
<my-button @click='doSomething("/script2")'></my-button> 
почему-то не работает, при нажатии ничего не происходит

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

Читать до просветления.

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <meta charset=utf8>
    <script src='/tmp/vue.js'></script>
    <script type='text/x-template' id='propsbtn'>
        <button style="height:50px;width:200px" @click='goto()' :disabled='disabled'></button>
    </script>
    <script type='text/x-template' id='clickbtn'>
        <button @click='emitEvent();' style="height:50px;width:200px" :disabled='disabled'></button>
    </script>
</head>
<body>
<div id='app' class='app'>
    <propsbtn url="/props">props based button</propsbtn>
    <clickbtn @click='appGoto("/click")'>click based button</clickbtn>
</div>
<script>
Vue.component('propsbtn', {
    props: ['url'],
    data: function() {
        return {
            disabled: false,
        }
    },
    template: '#propsbtn',
    methods: {
        goto: function(url) {
            console.log('goto inside propsbtn called!');
            console.log('url: ' + url);
        },
    },
});

Vue.component('clickbtn', {
    template: '#clickbtn',
    data: function() {
        return {
            disabled: false,
        }
    },
    methods: {
        emitEvent: function() {
            console.log('goto inside propsbtn called!');
            this.$emit('click');
        },
    },
});

let main = new Vue({
    el: '#app',
    methods: {
        appGoto: function(url) {
            console.log('goto inside main app called!');
            console.log('url: ' + url);
        },
    },
});
</script>
</body>
</html>

Мне надо разделить URL для AJAX запроса.

Предпочтительный способ:

Заводишь props с полем url и передаёшь нужный параметр при размещении компонента на странице. Можно биндить к данным родителя или просто задавать значение (без биндинга). Значение из props можно спокойно использовать в методах.

почему-то не работает, при нажатии ничего не происходит

Не работает, т.к. событие click перехватывается непосредственно кнопкой <button>, у которой не назначено никаких обработчиков. Хочешь делать так, придётся пробрасывать события из <button> в <my-button>.

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

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Сложнааа

Я читаю про компоненты и разбираю ваш пример.
Понял что передавать параметры в компоненты нужно через props.
Но в вашем примере в консоль выводится

goto inside propsbtn called!
url: undefined

хоть url него задан.
Почему?

Плюс вопрос про: <propsbtn url=«/props»>props based button</propsbtn>
Я предполагаю, что «props based button» должно стать названием кнопки, но кнопки рисуются пустыми. Если так и задумано, то для чего предполагается использовать эту строку?

zolden ★★★★★
() автор топика
Ответ на: Сложнааа от zolden

Про url, думаю, понял, внутри метода надо обращаться как this.url
Тогда, видимо, смысла в методах с параметрами нет...
В общем всё сложнее, чем я думал поначалу

zolden ★★★★★
() автор топика
Ответ на: Сложнааа от zolden

Про url, думаю, понял, внутри метода надо обращаться как this.url

Да, всё так. Пока с событиями ковырялся, сломал метод и не заметил.

но кнопки рисуются пустыми. Если так и задумано, то для чего предполагается использовать эту строку?

Мной ни для чего не предполагалось. По привычке написал. Этот текст можно использовать через слоты. Как-то так:

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <meta charset=utf8>
    <script src='https://unpkg.com/vue'></script>
    <script type='text/x-template' id='propsbtn'>
        <button style="height:50px;width:200px;background:grey;" @click='goto()' :disabled='disabled'>
            <slot>
                Props Button Name
            </slot>
        </button>
    </script>
</head>
<body>
<div id='app' class='app'>
    <propsbtn url="/props">props based button</propsbtn>
</div>
<script>
Vue.component('propsbtn', {
    props: ['url'],
    data: function() {
        return {
            disabled: false,
        }
    },
    template: '#propsbtn',
    methods: {
        goto: function() {
            console.log('goto inside propsbtn called!');
            console.log('url: ' + this.url);
        },
    },
});

let main = new Vue({
    el: '#app',
});
</script>
</body>
</html>
Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Спасибо, это почти то что нужно, но возник ещё вопрос.
Мне нужно возвращать что-то с сервера.
Я добавил поле для полученных результатов и его вывод рядом с кнопкой:

<script type='text/x-template' id='propsbtn'>
<button style="height:50px;width:200px;background:grey;" @click='goto()' :disabled='disabled'>
         <slot>
           Props Button Name
         </slot>
</button>
Result: {{ result }}
</script>

Vue.component('propsbtn', {
    props: ['url'],
    data: function() {
        return {
            disabled: false,
            result:"",
        }
    },
    template: '#propsbtn',
    methods: {
        goto: function() {
            console.log('goto inside propsbtn called!');
            console.log('url: ' + this.url);
            this.result = "AJAX RESPONSE";
        },
    },
});

Но получаю ошибку text «Result: {{ result }}» outside root element will be ignored
Почему так, ведь внутри шаблона элемент доступен?

zolden ★★★★★
() автор топика
Ответ на: комментарий от zolden

У компонента должен быть только один корневой элемент. В твоём случае это <button>. Текст вне этого эл-та будет игнорироваться. Если добавишь ещё один элемент в корень - выдаст предупреждение.

Выход оберни всё в <div>:

<script type='text/x-template' id='propsbtn'>
<div>
    <button @click='goto()' :disabled='disabled'>
        <slot>
           Props Button Name
        </slot>
    </button>
    <p>Result: {{ result }}</p>
</div>
</script>

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

В общем, я, думаю, уловил суть, прототип на вид работает, большое спасибо за помощь, дело закрыто

zolden ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.