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

исходный код @ здесь:

Https://github.com/quoeamaster/golang_blogs/tree/master/2020-03-gui-task-app

добавление верхней строки меню

Обычный компонент пользовательского интерфейса - это строка меню верхнего уровня. Menubar выполняет несколько функций, включая место для отображения заголовка приложения, предоставление популярных опций меню, таких как «добавить», «сохранить», «загрузить» и т. Д. На данный момент мы просто добавим заголовок приложения плюс «добавить примечание »в строке меню.

HTML-код строки меню выглядит следующим образом:

...
<!-- splash -->
<splash-screen></splash-screen>

<div style="height: 30px; box-shadow: 0 4px 5px #aaa; line-height: 30px; vertical-align: middle; padding-left: 4px; padding-right: 4px;">
    <div class="float-left" style="font-family: 'Comic Sans MS'; font-size: 0.8em;">
        daily task app
    </div>
    <div class="float-right" style="font-size: 1.1em; margin-top: 4px;">
        <i class="far fa-sticky-note" style="cursor: pointer;"></i>
    </div>
</div>

{{msg}}
<button class="btn btn-primary">bootstrap buttons</button>
...

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

Единственная деталь, на которую следует обратить внимание, - это «значок»; здесь мы используем иконки с потрясающим шрифтом ‹i class =« far fa-sticky-note »›. Мы можем просто рассматривать каждый значок здесь как символ символа (ресурс шрифта), поэтому он намного эффективнее по сравнению с реальным фоторесурсом.

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

обновите следующее:

<div class="float-right" style="font-size: 1.1em; margin-top: 4px;">
    <i class="far fa-sticky-note" style="cursor: pointer;" 
     v-on:click="onAddNoteClick"></i>
</div>

Мы добавили v-on: click = «functionName», что означает, что теперь мы привязываем событие onClick этого значка к вызову функции. Соответственно, в app.js нам нужно будет снова добавить функцию с именем «onAddNoteClick»:

...
data: function () {
    return {
        msg: 'vue app created',
    };
},
methods: {

    onAddNoteClick: function () {
        alert('time to add a note');
    }
}
...

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

Мягкое напоминание о процессе сборки (объяснения доступны в Части 4 серии):

go run generateAssets.go
mv generateAssets.go generateAssets.go.disable
go build -o taskApp
mv generateAssets.go.disable generateAssets.go
./taskApp

создание диалога

Javascript предоставляет диалоговые окна по умолчанию, такие как окно подтверждения и окно предупреждения, но оно может быть не таким красивым и красивым, поэтому мы могли бы создать свои собственные диалоговые окна. Давайте создадим новый файл js - createNoteDlg.js

Исходный код будет следующим:

Vue.component('create-note-dlg', {
    template: `
<div style="width: 600px; height: 300px; margin: auto; border: 1px solid #ddd; border-radius: 4px; z-index: 20;" 
    class="core-display-block">
    
    <div style="text-align: center; font-size: 1.1em; margin-top: 4px; margin-bottom: 12px;">
        create a note~
    </div>
    
    <div style="padding-left: 12px; padding-right: 12px;">
        <input class="form-control" 
               style="margin-bottom: 4px;" 
               placeholder="position: x coordinate">
        <input class="form-control" 
               style="margin-bottom: 4px;" 
               placeholder="position: y coordinate">
        <textarea class="form-control" rows="5" 
                  style="margin-bottom: 4px;" 
                  placeholder="content of the note"></textarea>
    </div>
</div>
    `
});

Затем нам нужно будет снова добавить компонент в index.html:

...
<div id="app">
    <!-- splash -->
    <splash-screen></splash-screen>
<!-- the menubar -->
...
    <!-- create note dlg -->
    <create-note-dlg></create-note-dlg>

</div>
<!-- load back all vue related components; app.js MUST be the last one -->
<script src="./component/demo/splash.js"></script>
<script src="./component/demo/createNoteDlg.js"></script>
<script src="./component/demo/app.js"></script>
...

который дает:

Хорошо, внешний вид диалога готов ... но не должен отображаться, пока не будет нажата иконка «добавить заметку»! Измените класс компонента диалога с core-display-block на core-display-none (сделайте его невидимый). Обновите функцию onAddNoteClick, расположенную в app.js.

new Vue({
    el: '#app',
    data: function () {
        return {
            displayDlg: false
        };
    },
    methods: {

        // * ------------------ *
        // *   event handlers   *
        // * ------------------ *

        onAddNoteClick: function () {
            this.displayDlg = true;
        }
    }
});

Давайте посмотрим на 2 строки обновленного кода в app.js:

  • добавить новый логический член данных (локальное состояние) - displayDlg. Это локальное состояние определяет, должен ли отображаться диалог.
  • в соответствующей функции onAddNoteClick; обновить локальное состояние «displayDlg» до true

Затем нам нужно будет также изменить index.html:

...
    <!-- create note dlg -->
    <create-note-dlg v-bind:show="displayDlg"></create-note-dlg>
...

Обратите внимание на атрибут v-bind: show = «displayDlg», мы передаем значение локального состояния - displayDlg в «диалог создания заметки». Переданное значение указывает, должен ли диалог быть видимым или нет. Давайте посмотрим на соответствующее изменение кода на createNoteDlg.js:

Vue.component('create-note-dlg', {
    props: ['show'],
    methods: {

        getDlgCss: function () {
            let c = {};
            console.log(this.show);

            if (this.show === true) {
                c['core-display-block'] = true;
                c['core-display-none'] = false;
            } else {
                c['core-display-block'] = false;
                c['core-display-none'] = true;
            }
            return c;
        }

    },
    template: `
<div style="width: 600px; height: 380px; margin: auto; border: 1px solid #ddd; border-radius: 4px; z-index: 20; overflow: auto;" 
    class="core-display-block" v-bind:class="getDlgCss()">
    
    ...
</div>
    `
});

Важные изменения кода:

  • верните настройку «props» - мы ясно видим, что доступен элемент с именем «show». Параметр props соответствует тому, какие значения можно ПОЛУЧИТЬ от его родительского компонента (в нашем случае компонент app).
  • добавлен новый метод «getDlgCss». Вернуть объект классов css, соответствующий значению полученного значения «show»
  • наконец, добавьте v-bind: class = «getDlgCss ()» в элемент div верхнего уровня; так что теперь мы можем контролировать, должен ли отображаться диалог или нет

Повторная сборка приложения и запуск даст:

Приятно ~ Выглядит пока неплохо; так как мы могли скрыть диалог позже? Давайте добавим несколько кнопок и предположим, что нажатие кнопки «отмена» закроет диалог:

Vue.component('create-note-dlg', {
    ...
    template: `
<div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 10; padding-top: 40px; background-color: rgba(230,230,230,0.8);" 
    v-bind:class="getDlgCss()">
    <div style="width: 600px; height: 380px; margin: auto; border: 1px solid #ddd; border-radius: 4px; z-index: 20; overflow: auto; background-color: white;">
        
        <div style="text-align: center; font-size: 1.1em; margin-top: 4px; margin-bottom: 12px;">
            create a note~
        </div>
        
        <div style="padding-left: 12px; padding-right: 12px;">
            <input class="form-control" style="margin-bottom: 4px;" placeholder="position: x coordinate">
            <input class="form-control" style="margin-bottom: 4px;" placeholder="position: y coordinate">
            <textarea class="form-control" rows="5" style="margin-bottom: 4px;" placeholder="content of the note"></textarea>
        </div>
        
        <div class="float-right">
            <button class="btn btn-primary">save</button>
            <button class="btn btn-secondary" 
                    v-on:click="onCancel">cancel</button>
        </div>
    </div>
</div>
    `
});

Мы видим, что добавлены 2 кнопки, кнопка «Отмена» прикреплена к обработчику события с именем «onCancel».

PS. Фон (элемент div) также добавляется для обтекания содержимого диалога; в основном просто для лучшего визуального эффекта :)

...
methods: {

    getDlgCss: function () {
       ...  
    },

    onCancel: function () {
        this.$emit('on-display-dlg-update', false);
    }

},
...

Функция onCancel имеет только 1 строку кода = ›отправку события своему родительскому компоненту - компоненту приложения. Здесь вступает в действие еще одна важная концепция - дочерние компоненты НИКОГДА не могут изменять состояния родительских компонентов; следовательно, вместо того, чтобы напрямую изменять родительские состояния, мы могли бы создать событие и позволить родителю выполнить обновление.

Теперь измените index.html следующим образом:

...
<!-- create note dlg -->
<create-note-dlg v-on:on-display-dlg-update="onDisplayDlgUpdate"
                 v-bind:show="displayDlg"></create-note-dlg>
...

PS. соответствующее переданное событие ДОЛЖНО быть объявлено в дочернем компоненте, как указано выше.

Обновите наш файл app.js:

new Vue({
    el: '#app',
    data: function () {
        return {
            displayDlg: false
        };
    },
    methods: {

        ...

        onDisplayDlgUpdate: function (data) {
            this.displayDlg = false;
        }

    }
});

Вот так просто ... обновите член данных displayDlg обратно на false, и все готово! Повторная сборка приложения и запуск даст:

модель данных в Vue.js

Теперь у нас уже есть рабочий диалог графического интерфейса для ввода данных новой заметки / задачи. Следующее, как записать эти значения для дальнейшей обработки?

В Vue.js есть синтаксис v-model для захвата входных данных из элементов html. Давайте посмотрим, как это работает:

Vue.component('create-note-dlg', {
    props: ['show'],
    data: function() {
        return {
            x: "",
            y: "",
            content: ""
        };
    },
    methods: {
        ...
        onSave: function () {
            alert(this.x+','+this.y+','+this.content);
        }

    },
    template: `
<div style="width: 100%; height: 100%; position: absolute; top: 0; left: 0; z-index: 10; padding-top: 40px; background-color: rgba(230,230,230,0.8);" 
    v-bind:class="getDlgCss()">
        ...

            <input v-model="x" class="form-control" 
                  style="margin-bottom: 4px;" 
                  placeholder="position: x coordinate">
            <input v-model="y" class="form-control" 
                   style="margin-bottom: 4px;" 
                   placeholder="position: y coordinate">
            <textarea v-model="content" class="form-control" 
                      rows="5" 
                      style="margin-bottom: 4px;" 
                      placeholder="content of the note"></textarea>

        
        ...
    </div>
</div>
    `
});

Во-первых, мы снова добавляем параметр «данные» в компонент. Если вы все еще помните, «данные» содержат объявления локальных состояний этого компонента - в этом сценарии:

  • Координата x ноты
  • Координата Y ноты
  • содержание заметки (например, бег трусцой в течение 30 минут, купить Лилиан подарок на день рождения)

Затем необходимо привязать входные значения (из текстовой области HTML и текстового ввода) к локальным состояниям; это достигается с помощью синтаксиса v-model = «local_state_name».

Повторная сборка и запуск приложения даст:

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

Смысл в том, чтобы избежать прямого доступа к значению HTML-элементов; в старые добрые времена jQuery - лучший, а также ЕДИНСТВЕННЫЙ фреймворк, который мы могли использовать для доступа к значению HTML-элементов ПЛЮС… для их ИЗМЕНЕНИЯ. Если мы просто намерены выполнять доступ ТОЛЬКО ДЛЯ ЧТЕНИЯ к значениям, это совершенно нормально; однако, когда нам также необходимо изменить значения - это то, что мы знали как повреждение данных. Возможность прямого изменения значений может вызвать следующие проблемы:

  • без общего места для управления изменением значений - например, проверка новых значений может выполняться перед принятием изменения. Таким образом, прямой доступ означает, что эта логика проверки может быть забыта ИЛИ разбросана повсюду.
  • если вы были единственным разработчиком компонента, то все равно можно было бы поддерживать подход прямого доступа к коду; однако… если бы команда разработчиков работала над одним и тем же компонентом, то очень скоро это могло бы войти в хаос, поскольку мы не знаем, когда и почему разработчики напрямую обращаются к некоторым атрибутам / значениям HTML-элементов и изменяют их. Ремонтопригодность вызывает сомнение

Двигаемся дальше…

В этом уроке / рассказе; мы рассмотрели следующее:

  • использование font-awesome для создания интерактивных иконок; помните, что font-awesome - это глифы, а не ресурсы изображения, поэтому они более эффективны.
  • научились прикреплять обработчики событий к HTML-компонентам (например, значку)
  • создали наш первый модальный диалог; снова благодаря силе css :)
  • узнал, как родительский компонент (например, компонент приложения) может передавать свои локальные состояния дочернему компоненту (например, диалогу) с помощью синтаксиса v-bind
  • узнал, как дочерний компонент может получать информацию о состоянии родителя с помощью параметра «props».
  • использовать значение местного состояния и контролировать внешний вид диалога с помощью выбора класса css (например, чтобы показать или скрыть диалог)
  • научились обновлять информацию о местном штате родителей с помощью событий. Помните, что дочерние компоненты НИКОГДА не могут изменять информацию о состоянии родителей (ТОЛЬКО ДЛЯ ЧТЕНИЯ). Ключ должен запустить «this. $ Emit (‘ event-name ’, {{payload / data}});»
  • узнал, как родительский компонент может быть подтвержден по запросу на изменение состояния. Секрет заключается в следующем: «v-on: {{event-name}} =‘ event-handler-function ’»
  • наконец, как Vue.js реагирует на обработку данных уровня формы - синтаксис v-model.

Это довольно много ~ Наш следующий урок / рассказ будет посвящен тому, как создать компонент Note на основе информации, полученной компонентом диалога. Будьте на связи. :)))