Активная двусторонняя привязка данных с выражениями

Я хотел бы, чтобы input представлял список примитивных (строковых, числовых) значений, чтобы вы могли вводить их как значения, разделенные запятыми, но обновлять модель как массив:

"Transforms": [
        {
            "Fn": "TheFunctionName",
            "Args": [ "arg1", 2, "arg3" ]
        },
        {
            "Fn": "AnotherMethod",
            "Args": [ 4.678 ]
        },
    ]

Будет управляться с:

{{#each Transforms:i}}
   <li>
       <input value="{{Fn}}" placeholder="Function Name" />
       <input value="{{implode(Args)}}" placeholder="Function Arguments" />
   </li>
{{/each}}

и отобразит что-то вроде:

* [ TheFunctionName ] [ "arg1", 2, "arg3" ] * [ AnotherMethod ] [ 4.678 ]

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

Я мог бы использовать свойство «заполнитель» для привязки данных и, вероятно, наблюдать его для обновления фактического свойства, но тогда мне нужно было бы отфильтровать его при «сериализации» базовой модели. Похоже, я мог бы использовать вычисляемые свойства, у которых есть геттер и сеттер, но это из документации не ясно, как это работает с вложенными свойствами в списке (т.е. в data есть много записей со списком Transforms).




Ответы (1)


Уловка здесь в том, чтобы не использовать двустороннюю привязку, а использовать более традиционные методы обработки событий. В этом примере прослушиваются события change на втором входе (а не события input, которые происходят при каждом нажатии клавиши, поскольку это приведет к тому, что курсор будет прыгать, пока пользователь все еще набирает текст) в каждом <li> и пытается оценить его содержимое. Тем временем модуль форматирования принимает аргументы и превращает их обратно в строку:

var ractive = new Ractive({
    el: 'main',
    template: '#template',
    data: {
        transforms: [
            {
                fn: 'TheFunctionName',
                args: [ 'arg1', 2, 'arg3' ]
            },
            {
                fn: 'AnotherMethod',
                args: [ 4.678 ]
            }
        ],
        format: function ( args ) {
            return args.map( JSON.stringify ).join( ', ' );
        }    
    },
    updateArgs: function ( index, str ) {
        var keypath = 'transforms[' + index + '].args',
            args = this.get( keypath );
        
        try {
            // or use JSON.parse, if you don't want to eval
            this.set( keypath, eval( '([' + str + '])' ) );
        } catch ( err ) {
            // reset
            this.set( keypath, null );
            this.set( keypath, args );
        }
    }
});
<script src="http://cdn.ractivejs.org/edge/ractive.js"></script>

<main></main>

<script id='template' type='text/ractive'>
    <h2>input</h2>
    <ul>
        {{#each transforms:i}}
            <li>
                <input value='{{fn}}' placeholder='function name'/>
                <input
                    twoway='false'
                    value='{{format(args)}}'
                    placeholder='function arguments'
                    on-change='updateArgs(i,event.node.value)'
                />
            </li>
        {{/each}}
    </ul>
    
    <h2>output</h2>
    <ul>
        {{#each transforms}}
            <li>
                <p>function name: <strong>{{fn}}</strong></p>
                <p>args:</p>
                <ul>
                    {{#each args}}
                        <p>{{JSON.stringify(this)}} ({{typeof this}})</p>
                    {{/each}}
                </ul>
            </li>
        {{/each}}
    </ul>
</script>

person Rich Harris    schedule 18.12.2014
comment
Я как раз собирался прийти к такому же выводу, но не сделал этого. Я не уверен в правильном способе заявить об этом. Меня также обеспокоило предупреждение консоли Two-way binding does not work with expressions... - это просто дружеское напоминание, и его можно игнорировать? - person drzaus; 19.12.2014
comment
Да, это просто отладочное сообщение на случай, если кто-то попробовал сделать <input value='{{number*2}}'> и был удивлен, что ввод 10 не привел к number стать 5. Это не вызовет никаких проблем с вашим приложением, и сообщение (должно?) Быть подавлено. в следующей версии, если у элемента есть twoway='false'. - person Rich Harris; 19.12.2014
comment
ах ... Я думаю, что мои проблемы связаны с тем, что я использую последнюю версию CDN, а не Edge - я получал сообщение об ошибке Uncaught TypeError: Cannot read property 'split' of undefined при каждом нажатии клавиши - person drzaus; 19.12.2014
comment
Так что это может быть глупый вопрос, но если вы передаете event в качестве аргумента, почему бы не ссылаться на event.context непосредственно для обновлений, а не на получение / установку в keypath; или, если на то пошло, использовать event.keypath, а не восстанавливать его? - person drzaus; 19.12.2014
comment
Мне удалось воспроизвести мою последнюю проблему - это против that, где второй так часто выдает Uncaught TypeError: Cannot read property 'str' of undefined, а следующий щелчок сбрасывает недостающие строки - person drzaus; 19.12.2014