"Хочу универсально!" [Часть 1]

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

 

 

Жил-был начинающий разработчик Вася. И было у него три «пунктика»:

  1. Писать по стандартам

  2. Делать универсально

  3. Перепроверять, что все соответствует п.1 и п.2

 

И в зависимости от расположения звёзд на небе, у Василия активизировались те или иные пункты. Например.

 

Есть на форме обработки таблица с колонками. Разработчик столкнулся с, на первый взгляд, примитивной задачей — сделать кнопку, которая будет открывать значение в текущей колонке. Пользователь становится курсором на валюту «Руб.» и нажимает кнопку. Карточка валюты «руб.» открывается. Пользователь становится на другую ячейку и нажимает на кнопку — открывается значение из этой ячейки.

 

И вот программист Вася, вдохновившись тем, что сможет моментально решить задачу, добавляет команду.

 

— А вдруг пользователь нажмёт кнопку в тот момент, когда в таблице вообще не будет строк?

 

И прилежный программист сразу же (как учили) в процедуре делает стандартную проверку.

 

ТаблицаФормы = Элементы.Валюты;

Если ТаблицаФормы.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;

— Ну вот! Уже лучше. Начинаем эксперименты

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

 

ТекущаяКолонка = ТаблицаФормы.ТекущийЭлемент;
Сообщить("Имя текущего поля: " + ТекущаяКолонка.Имя);

Открывает свой любимый тонкий клиент и проверяет:

 



 

— Но ведь сама колонка в таблице называется «Валюта», а не «ВалютыВалюта».

Взмахнув мышкой, Василий открывает редактор формы и видит, что на самом деле происходит.

 

«Валюты» — это имя таблицы, в которой хранятся данные.

«Валюта» — имя колонки этой таблицы.

А «ВалютыВалюта» — это имя поля на форме. Оно не хранит данные, а лишь выводит их. На самом деле данные хранятся в строке таблицы «Валюты» в колонке «Валюта».

 

 



 

 

Так происходит, когда имя поля было сгенерировано автоматически. Например, разработчик вынес его «вручную» на форму обработки. Имя автоматически присвоелось по формуле:

имя таблицы + имя колонки.

 

— И как же мне определить реальное имя колонки у текущего элемента?

Чему зачастую учат программистов в этих ваших интернетах? Прежде чем создавать свой велосипед, нужно поискать чужие. Естественно, в интернете.

 

Недолго думая гугля, Вася находит несколько примеров.

 

— А где-то я уже это видел…

Флэшбеки вспышками имен методов и переменных пролетают в сознании разработчика 1С и, погрузившись в океан жёлтых воспоминаний, он хватает за хвост имя обработки, в которой видел нечто подобное.

 

— Точно, я уже такое видел!

И действительно, в одной из завалявшихся в конфигурации обработок есть это:

 

ТаблицаФормы.ТекущиеДанные[Сред(ТаблицаФормы.ТекущийЭлемент.Имя, СтрДлина(ТаблицаФормы.Имя) + 1)]

Всё просто. Автор этого кода полагает, что имена элементов в его инструменте всегда будут строиться по приведенной ранее формуле: имя таблицы + имя колонки. И вроде бы ничего страшного, ведь так чаще всего и бывает, но:

 

— Я хочу универсальное решение!

Звёзды сошлись. Сегодня Василий решается на серьёзный поступок — не денег ради, но во имя высокой цели, сделать кнопку не «как можно быстрее», а «как можно круче». Естественно, степень крутости определяется им самим.

 

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

 

Порывшись в гугле синтаксис помощнике, Василий находит свойство у поля: ПутьКДанным

 

ПолеФормы (FormField)

ПутьКДанным (DataPath)

Использование:

Чтение и запись.

Описание:

Тип: Строка.
Содержит путь к реквизиту, с которым связан объект.

Доступность:

Сервер, мобильное приложение(сервер).

 

И вроде бы вот оно решение, однако:

 

Доступность:

Сервер, мобильное приложение(сервер).

 

Есть загвоздка — свойство доступно только на сервере.

 

Можно, конечно, написать серверную функцию, которая будет возвращать путь к данным формы:

 

&НаКлиенте
Процедура ОткрытьЗначениеЯчейки(Команда)

ТаблицаФормы = Элементы.Валюты;

Если ТаблицаФормы.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;

ТекущаяКолонка = ТаблицаФормы.ТекущийЭлемент;
Сообщить("Имя текущего поля: " + ТекущаяКолонка.Имя);
Сообщить("Путь к данным текущего поля: " + ПутьКДаннымЭлементаФормы(ТекущаяКолонка.Имя));

КонецПроцедуры


&НаСервере
Функция ПутьКДаннымЭлементаФормы(ИмяЭлемента)
 
ПутьКДаннымЭлемента = Неопределено;
 НайденныйЭлемент    = Элементы.Найти(ИмяЭлемента);
 Если НЕ НайденныйЭлемент = Неопределено Тогда
  ПутьКДаннымЭлемента = НайденныйЭлемент.ПутьКДанным;
 КонецЕсли;
 
Возврат ПутьКДаннымЭлемента;
 
КонецФункции

Но…

— Так не пойдёт!

Вспомнив, что каждое излишнее обращение к серверу — это зло, за которое его ругали в Великой Школе Одинэсников Имени Желтого Чайника, разработчик Вася решает попробовать не плодить серверные вызовы. Тем более с передачей всей формы.

 

Программист добавляет на форму обработки реквизит произвольного типа «ПутиКДаннымЭлементовФормы»

 



 

А при создании формы заполняем эту структуру именами полей и адресами их значений, хранящихся в свойстве «ПутьКДанным»

 

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

ПутиКДаннымЭлементовФормы = Новый Структура;

Для Каждого ЭлементФормы Из ЭтаФорма.Элементы Цикл

Если ТипЗнч(ЭлементФормы) = Тип("ПолеФормы")
ИЛИ ТипЗнч(ЭлементФормы) = Тип("ТаблицаФормы") Тогда

ПутиКДаннымЭлементовФормы.Вставить(ЭлементФормы.Имя, ЭлементФормы.ПутьКДанным);

КонецЕсли;

КонецЦикла;

КонецПроцедуры

Теперь остаётся лишь сделать клиентский метод, который будет возвращать соответствующее значение свойства «ПутьКДанным»

&НаКлиенте
Функция ПутьКДаннымЭлементаФормы(ИмяЭлемента)

ПутьКДаннымЭлемента = Неопределено;
ПутиКДаннымЭлементовФормы.Свойство(ИмяЭлемента, ПутьКДаннымЭлемента);
Возврат ПутьКДаннымЭлемента;

КонецФункции

 

Разработчик оглядывает код и радуется проделанной работе. А заодно и проверяет насколько правильно всё работает:

 

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

ПутиКДаннымЭлементовФормы = Новый Структура;

Для Каждого ЭлементФормы Из ЭтаФорма.Элементы Цикл

Если ТипЗнч(ЭлементФормы) = Тип("ПолеФормы")
ИЛИ ТипЗнч(ЭлементФормы) = Тип("ТаблицаФормы") Тогда

ПутиКДаннымЭлементовФормы.Вставить(ЭлементФормы.Имя, ЭлементФормы.ПутьКДанным);

КонецЕсли;

КонецЦикла;

КонецПроцедуры

&НаКлиенте
Процедура ОткрытьЗначениеЯчейки(Команда)

ТаблицаФормы = Элементы.Валюты;

Если ТаблицаФормы.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;

ТекущаяКолонка = ТаблицаФормы.ТекущийЭлемент;
Сообщить("Имя текущего поля: " + ТекущаяКолонка.Имя);
Сообщить("Путь к данным текущего поля: " + ПутьКДаннымЭлементаФормы(ТекущаяКолонка.Имя));

КонецПроцедуры

&НаКлиенте
Функция ПутьКДаннымЭлементаФормы(ИмяЭлемента)

ПутьКДаннымЭлемента = Неопределено;
ПутиКДаннымЭлементовФормы.Свойство(ИмяЭлемента, ПутьКДаннымЭлемента);
Возврат ПутьКДаннымЭлемента;

КонецФункции

Вернувшись в тонкий клиент, Василий смотрит на результат:

 

 



 

 

 

Уже лучше. Теперь мы можем на клиенте узнать путь к данным формы. Для проверки, что всё на самом деле так, как планировалось, разработчик Вася переименовал элемент формы на «КолонкаСВалютой». Теперь путь к данным и имя элемента непохожи между собой, что поможет понять разницу. Вася любит всё перепроверять.

 



 

 

 

Ну вот. Всё хорошо. Но что теперь делать с этим? Нужно же определить имя колонки из полного пути к данным формы. Василий берёт самый прямой способ — расщепить полный путь на массив и взять его последний элемент:

 


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

ПутиКДаннымЭлементовФормы = Новый Структура;

Для Каждого ЭлементФормы Из ЭтаФорма.Элементы Цикл

Если ТипЗнч(ЭлементФормы) = Тип("ПолеФормы")
ИЛИ ТипЗнч(ЭлементФормы) = Тип("ТаблицаФормы") Тогда

ПутиКДаннымЭлементовФормы.Вставить(ЭлементФормы.Имя, ЭлементФормы.ПутьКДанным);

КонецЕсли;

КонецЦикла;

КонецПроцедуры


&НаКлиенте
Функция ПутьКДаннымЭлементаФормы(ИмяЭлемента)

ПутьКДаннымЭлемента = Неопределено;
ПутиКДаннымЭлементовФормы.Свойство(ИмяЭлемента, ПутьКДаннымЭлемента);
Возврат ПутьКДаннымЭлемента;

КонецФункции


&НаКлиенте
Процедура ОткрытьЗначениеЯчейки(Команда)

ТаблицаФормы = Элементы.Валюты;

Если ТаблицаФормы.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;

ИмяПоляКолонки     = ТаблицаФормы.ТекущийЭлемент.Имя;                 //КолонкаСВалютой
 ПутьКДаннымКолонки = ПутьКДаннымЭлементаФормы(ИмяПоляКолонки);        //Валюты.Валюта
 
 СоставПутиКДанным  = СтрРазделить(ПутьКДаннымКолонки, ".", Ложь);     //Валюты; Валюта
 ИмяКолонкиТаблицы  = СоставПутиКДанным[СоставПутиКДанным.ВГраница()]; //Валюта

Сообщить("Имя колонки таблицы: " + ИмяКолонкиТаблицы);

КонецПроцедуры

 

— Сейчас проверим

 



 

Осталось немного — просто взять значение из колонки и открыть его:

 

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

ПутиКДаннымЭлементовФормы = Новый Структура;

Для Каждого ЭлементФормы Из ЭтаФорма.Элементы Цикл

Если ТипЗнч(ЭлементФормы) = Тип("ПолеФормы")
ИЛИ ТипЗнч(ЭлементФормы) = Тип("ТаблицаФормы") Тогда

ПутиКДаннымЭлементовФормы.Вставить(ЭлементФормы.Имя, ЭлементФормы.ПутьКДанным);

КонецЕсли;

КонецЦикла;

КонецПроцедуры


&НаКлиенте
Функция ПутьКДаннымЭлементаФормы(ИмяЭлемента)

ПутьКДаннымЭлемента = Неопределено;
ПутиКДаннымЭлементовФормы.Свойство(ИмяЭлемента, ПутьКДаннымЭлемента);
Возврат ПутьКДаннымЭлемента;

КонецФункции


&НаКлиенте
Процедура ОткрытьЗначениеЯчейки(Команда)

ТаблицаФормы = Элементы.Валюты;

Если ТаблицаФормы.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;

ИмяПоляКолонки     = ТаблицаФормы.ТекущийЭлемент.Имя;
ПутьКДаннымКолонки = ПутьКДаннымЭлементаФормы(ИмяПоляКолонки);

СоставПутиКДанным  = СтрРазделить(ПутьКДаннымКолонки, ".", Ложь);
ИмяКолонкиТаблицы  = СоставПутиКДанным[СоставПутиКДанным.ВГраница()];

ЗначениеЯчейки     = Неопределено;
 ТаблицаФормы.ТекущиеДанные.Свойство(ИмяКолонкиТаблицы, ЗначениеЯчейки);
 
ПоказатьЗначение(Новый ОписаниеОповещения, ЗначениеЯчейки);

КонецПроцедуры

 

Замечательно! Инструмент заработал. И теперь уже значение будет открываться независимо от того, как называется наш элемент формы.

 

Василий уже прикинул в голове способы применения этого кода. Например, у нас есть табличная часть документа, которую нельзя редактировать. Наш разработчик ставит на таблицу ТолькоПросмотр, а так же подвязывает событие «Выбор», в котором открывает текущее значение.

 

И как только наш начинающий программист подумал об этом, в голову пришла мысль.

 

— Недостаточно удобно!

Да, этого мало. Василий хочет сделать ещё круче! Он решает переспать с этой мыслью, чтобы уже позже продолжить свой путь от простой задачи к универсальному механизму…

 

 

Понравилась статья?

 

Дамы и господа 1Сники. Не будьте равнодушными. Каждая ваша "звёздочка" порадует и автора и программиста Васю. Ведь это показатель того, что кому-то текст зашёл, а значит и время было потрачено не зря. 

 

Ну а после переходите к другим работам.

"Меньше копипаста!", или как Вася универсальную процедуру писал

Сортируем ДанныеФормыДерево на клиенте

Не провоцируйте СКД, или пример "как не надо"

 

 

35 Comments

  1. serg-lom89

    классно написано!)Жду дальше историй про Васю!)

    Reply
  2. genayo

    Нам плюсиков не жалко, они бесплатные :)) Так что пиши ещё :))

    Reply
  3. Arxxximed

    Написано классно, стиль повествования понравился… Но , никогда так не буду делать, потому что структура колонок в реквизите формы — это уже костыль.

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

    2. универсальность может и появилась. Но не очень… а что будет если программист Петя, у которого нету времени вникать в суть процедур Васи захочет программно добавить колонки, не задумываясь что за переменная ПутиКДаннымЭлементовФормы? Он будет думать над своей логикой, и только потом обнаружит что эта «кнопка» уже не совсем работает…

    Reply
  4. Arxxximed

    А можете мне рассказать суть статьи? Это подход к решению задачи, или описание маленькой задачи?

    Извините за критику, надеюсь она будет конструктивной.

    1 Если первое: То как то не расскрыто. Не вижу разбора паттерна , да и самого паттерна подхода к решению задачи.

    2 Если про саму задачу: решение — так себе… Вроде и работает. Но… писал уже выше.

    Пока — статья для статьи.. Что тоже имеет место быть.

    Reply
  5. Rustig

    (0) как-то все сложно…

    Reply
  6. Rustig

    (0) вот видео

    Reply
  7. Arxxximed

    (5) Ну и ? Добавлю колонку со своим именем… Как данные потом найдешь? В статье это как раз и написано. Вот лишь бы написать не почитавши.

    Reply
  8. Arxxximed

    (6) еще и с видео заморочились )))… Там в обработке две строчки. можно же было просто сюда код написать!

    Reply
  9. fredly_nightly

    (5) ну так стоит изменить имя элемента и всё сломается. В этом и вся суть

    Reply
  10. Rustig

    (8) :)))))))))))))))) да уж… вы правы

    &НаКлиенте
    Процедура СообщитьИмяЭлемента(Команда)
    ТекЭлемент = ЭтаФорма.Элементы.Список.ТекущийЭлемент;
    Сообщить(СтрЗаменить(ТекЭлемент.Имя, «Список», «»));
    КонецПроцедуры
    
    Reply
  11. Rustig

    (9) имя какого элемента вы хотите поменять?

    Reply
  12. fredly_nightly

    (3) 1. обращение минимальное. Это не тоже самое, что в добавок ещё и форму всю перекидывать ради одной строчки

    2. ага, выглядит как костылик. Но я вот помню сталкивался с подобной ситуацией… И что-то подобное только и получилось сделать. Как-то вариантов других не нашли (

    Reply
  13. fredly_nightly

    (11) любого. Если элемент будет называться как угодно, не соответствуя шаблону «Список» + ИмяКолонки, то всё сломается.

    Reply
  14. Rustig

    (13) Название табличной части «Список» можно выделить в переменную

    &НаКлиенте

    Процедура СообщитьИмяЭлемента(Команда)

    ИмяТЧ = «Список»;

    ТекЭлемент = ЭтаФорма.Элементы[ИмяТЧ ].ТекущийЭлемент;

    Сообщить(СтрЗаменить(ТекЭлемент.Имя, «ИмяТЧ «, «»));

    КонецПроцедуры

    Reply
  15. Rustig

    (13) Имя табличной части сможете сами вытащить ? и в переменную передавать и далее в процедуру.

    Reply
  16. fredly_nightly

    (14) с таким подходом лучше уж как в статье:

    ТаблицаФормы.ТекущиеДанные[Сред(ТаблицаФормы.ТекущийЭлемент.Имя, СтрДлина(ТаблицаФормы.Имя) + 1)]

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

    Reply
  17. fredly_nightly

    (14) а если элемент будет называться не «СписокВалюта», а «ВалютаУчета» ?

    Или нужно вынести два элемента, привязанного к одной колонке.

    ваш подход работает только с самыми примитивными ситуациями. А тут описывается «универсальное мега супер решение»

    Reply
  18. Arxxximed

    По мне так все таки нормальный вариант Получать ПутьКданным на сервере, и там получать сразу данные… Если не нравится идея передавать всю форму на сервер, используйте НаСервереБезконтекста, куда в параметрах передавать ТаблицуФормы и Данныеформыколлекция

    Reply
  19. SeiOkami

    (18) нельзя сделать при помощи &НаСервереБезконтекста

    Если у Вас выйдет, поделитесь, пожалуйста — это заметно упростит код.

    Reply
  20. Arxxximed

    (19) Да , прошу прощения, Элементы формы не передаются в параметрах на сервер…

    Но тут пришла и другая мысль… А зачем боятся вызова этого сервера в контексте этой задачи? Ведь важно что бы не было частых вызовов, а тут тоолько один , по нажатию кнопки… А если предполагается частый контекстный вызов, то наверное нужно использовать другие события ТаблицыФормы

    Reply
  21. Rustig

    (16)

    &НаКлиенте
    Процедура СообщитьИмяЭлемента(Команда)
    ИмяТЧ = ЭтаФорма.ТекущийЭлемент.Имя;
    ТекЭлемент = ЭтаФорма.Элементы[ИмяТЧ].ТекущийЭлемент;
    Сообщить(СтрЗаменить(ТекЭлемент.Имя, ИмяТЧ, «»));
    КонецПроцедуры
    
    Reply
  22. Rustig

    (17) я не вижу супер-решения.

    Reply
  23. Arxxximed

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

    Конечно , все в зависимости от поставленной задачи.

    Reply
  24. SlavaKron

    (20) Если говорить об универсальности, хотелось бы найти такое решение, которое подошло бы, например, для этой задачи: Открывашка ячеек таблиц. То есть всё должно быть на клиенте. Менять код формы мы не можем, всё что у нас есть — это текущий элемент и ДанныеФормыСтруктура ДанныеФормыЭлементКоллекции (ТекущиеДанные).

    Reply
  25. Infector

    Автор! Вы забыли указать, что Василий это кот 🙂

    Reply
  26. ntemny

    Не совсем понял задачу. ТекущаяСтрока и НайтиПоИдентификатору() в обработке выбора без использования элементов формы религией запрещены?

    Reply
  27. Arxxximed

    (12) Все таки интересно, пример или хотя бы суть такой ситуации, в которой без костылика не обойдешься?

    Могу только предоположить , когда данные на форме объемные, и любая отправка формы на сервер вызывает задержку?

    Reply
  28. Yashazz

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

    Reply
  29. Yashazz

    А вообще, с возрастом и опытом приходит понимание тонкой грани между решением «по месту», кастомизацией, продиктованной сроками, стоимостью, сложностью, перспективами развития и эксплуатации системы, и универсализацией. И вместо сферических универсалов в вакууме гораздо чаще востребованы и удобны оказываются узкоспециализированные решения. Не говоря уже о том, что универсализация на уровне разработчика частенько «выпихивает» проблему настоящей собственно автоматизации процесса на следующий уровень — уровень админа, продвинутого юзера — того, кто настраивает этот супер-пупер-универсал и рулит им. Универсалам нужна подробная справка, и всё равно не читают; универсалов тупо опасаются; универсалы редко «угадывают» настоящие направления работы и оказываются бесполезны…

    Reply
  30. Arxxximed

    (26) А причем тут обработка выбора.. Автор хочет по кнопке получить текущую колонку и текущую строку, а потом по этим данным найти и открыть значение.

    Reply
  31. ntemny

    (30)Ну ок, по кнопке, не по обработке выбора. Разницы нет. В любом случае правильнее работать не с элементами формы а с реквизитом типа ДанныеФормыКоллекция.

    Reply
  32. Leon29

    (29)

    и оказываются бесполезны…

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

    Ведь много идёт по пути универсальности. Например, платформа 1С. Уже есть готовые объекты и действительно надо знать как ими пользоваться. И всякие универсальные вещи ускоряют работу. Например, автомобильные детали используются в различных брендах и надо знать их качество, надежность, крепления, параметры.

    Reply
  33. Yashazz

    (32) Разумеется. Нигде нет смысла бросаться в крайности и возводить в абсолюты. Программирование или нет, тут не суть.

    Reply
  34. Jimbo

    Что за критика ? Универсальное ради универсального. Просто читать — бальзам на душу. Разобран конкретный пример, все с этим столкнутся. Васе зачёт и молочка.

    Reply
  35. mbalyukin

    Для чего вообще делать кнопку и все это? Я правильно понимаю, что задача программиста в данном случае открывать значение из ячейки в таблице? Но из-за того, что установлено свойство «ТолькоПросмотр» = Истина в самой ячейке таблицы не работает кнопка «Открыть»? А почему нельзя убрать «ТолькоПросмотр», тогда естесственное открытие ведь станет работать. Потому что тогда пользователь сможет менять значение в колонке, а это плохо? На мой взгляд решение есть проще.

    Имеем:

    0. Таблица значений на форме, нужно открывать любое значение из колнки стандартной «лупой», но не иметь возможности менять элемент.

    1. У всех ссылочных полей таблицы ставим свойство «Редактирование текста» = Ложь.

    2. У тех же полей создаем событие «Начало выбора», в котором ставим «СтандартнаяОбработка» = Ложь.

    3. У тех же полей создаем событие «Очистка, в котором ставим «СтандартнаяОбработка» = Ложь.

    4. «Только просмотр» не ставим.

    Все. Теперь любое ссылочное значение из таблицы можно открыть без доп. кнопок и кучи кода, простой кнопкой «открыть» из ячейки. А также поменять или очистить значение тоже никак не получится.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *