秘银过滤组件列表
Mithril filtering list of components
我在尝试使用 mithril.js 筛选数据列表时遇到问题。
我有两个组件:一个表单和一个 inputWrapper。
- 表单显示了一个包含多个输入字段的表单,每个字段都有一个值。
- 该表单还有一个允许过滤数据的搜索输入。
在表单视图中直接使用 m('input', { value: ...} )
时,过滤效果很好。
当使用包含输入和耦合到其值的 m.prop
的组件时,一切都会出错。
在表格中,我们有类似的东西:m.component( inputWrapper, { value: ... })
尝试过滤此列表时,视图呈现错误的组件。
查看 this codepen 以获得更清晰的图片。
我的猜测是 mithril diff 引擎存储了一组缓存组件并渲染这些组件,而不是在每次重绘时实例化新组件。
然而,我还没有找到解决这个问题的方法。请帮忙。
我不得不稍微重构你的代码以便能够遵循逻辑,但设法让它工作。
缓存问题与控制器有关:重绘时,如果给定类型的组件呈现在与上一次绘制中相同类型的组件相同的位置 — 和组件没有可区分的键 — 那么假定您要检索以前的控制器。对于您的组件,控制器存储视图要使用的输入(而不是视图直接从它自己的 args
输入读取它)。
解决这个问题的最简单方法是简单地摆脱控制器并直接读取输入,因为如果输入应该始终是最新的,它就没有任何理想的目的。
// ---------------------------------
// component with an input
// ---------------------------------
const inputWrapper = {
view : ( ctrl, args ) =>
m('input[type=text]', args )
}
// ---------------------------------
// component that filters list
// ---------------------------------
m.mount( document.body, {
controller : function( attrs ) {
const filterTerm = m.prop( '(?:)' )
return {
filterTerm,
filterFunc : word => {
const term = filterTerm()
if ( _.isString( term ) && _.isString( word ) )
return word.search( new RegExp( term, 'i' ) ) > -1
},
someData : [ "long", "list", "of", "meaningful", "data", "really", "cool", "stuff"]
}
},
view : ( { filterTerm, filterFunc, someData }, attrs ) =>
m('form',
m('p',
m('input[type=text]', {
oninput : m.withAttr( 'value', filterTerm ),
placeholder : 'filter data'
})
),
m('p',
m('ul',
someData
.reduce( ( acc, val ) =>
( ( filterTerm() && filterTerm() == '/(?:)/' ) || filterFunc(val) )
? acc.concat( val )
: acc,
[]
)
.map( value =>
m('li',
m('input', { value } ),
m(inputWrapper, { value } )
)
)
)
)
)
} )
<script src="https://cdnjs.cloudflare.com/ajax/libs/mithril/0.2.5/mithril.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
但是,此修复揭示了其他问题:因为生成列表(和组件)的数据是在每次绘制时即时创建的,输入绑定的数据结构在每次重绘时都会被简单地处理掉, 当父组件中的 reduce 和 map 函数生成新数组时。
该问题的解决方案是在接收数据的父控制器中执行缩减,以便可以在下一次绘制时检索到。
我对代码所做的其他小调整:
- 视图中虚拟 DOM 子项的数组括号是不必要的(我删除了很多标点符号,出于个人喜好:)。
- 您在
let
赋值中执行的解构可以在函数参数签名中执行,从而减少冗长。
- 删除这些赋值允许 'implicit return' 形式的箭头函数表达式,这使得在删除要考虑的变量数量的同时更容易查看视图输出的结构。
- 使用
const
而不是 let
可以清楚地表明分配给引用的值不能更改,这样可以更容易地找到代码可能在何处执行意外操作。
- 将 reduce 表达式的回调缩减为三元结构可以清楚地表明只有 1 个逻辑条件和 2 个可能的结果,并且
concat
允许您在单个表达式中生成新结构(而不是 push
ing 和 然后 return
ing.
在对 mithril 的文档进行了又一轮的挖掘之后,我来这里回答我自己的问题。
如果我在初始化组件时提供了 key:
m.component( InputWrapper, { key: 'distinguishingKey', value: ... } )
一切都会好起来的。
谢谢巴尼的提示:
...and the components do not have a distinguishing key — ...
感谢 Leo Horie 提供了出色的文档。 (要是我几周前才读到就好了……)
http://mithril.js.org/mithril.component.html#data-driven-component-identity
我在尝试使用 mithril.js 筛选数据列表时遇到问题。
我有两个组件:一个表单和一个 inputWrapper。
- 表单显示了一个包含多个输入字段的表单,每个字段都有一个值。
- 该表单还有一个允许过滤数据的搜索输入。
在表单视图中直接使用 m('input', { value: ...} )
时,过滤效果很好。
当使用包含输入和耦合到其值的 m.prop
的组件时,一切都会出错。
在表格中,我们有类似的东西:m.component( inputWrapper, { value: ... })
尝试过滤此列表时,视图呈现错误的组件。
查看 this codepen 以获得更清晰的图片。
我的猜测是 mithril diff 引擎存储了一组缓存组件并渲染这些组件,而不是在每次重绘时实例化新组件。 然而,我还没有找到解决这个问题的方法。请帮忙。
我不得不稍微重构你的代码以便能够遵循逻辑,但设法让它工作。
缓存问题与控制器有关:重绘时,如果给定类型的组件呈现在与上一次绘制中相同类型的组件相同的位置 — 和组件没有可区分的键 — 那么假定您要检索以前的控制器。对于您的组件,控制器存储视图要使用的输入(而不是视图直接从它自己的 args
输入读取它)。
解决这个问题的最简单方法是简单地摆脱控制器并直接读取输入,因为如果输入应该始终是最新的,它就没有任何理想的目的。
// ---------------------------------
// component with an input
// ---------------------------------
const inputWrapper = {
view : ( ctrl, args ) =>
m('input[type=text]', args )
}
// ---------------------------------
// component that filters list
// ---------------------------------
m.mount( document.body, {
controller : function( attrs ) {
const filterTerm = m.prop( '(?:)' )
return {
filterTerm,
filterFunc : word => {
const term = filterTerm()
if ( _.isString( term ) && _.isString( word ) )
return word.search( new RegExp( term, 'i' ) ) > -1
},
someData : [ "long", "list", "of", "meaningful", "data", "really", "cool", "stuff"]
}
},
view : ( { filterTerm, filterFunc, someData }, attrs ) =>
m('form',
m('p',
m('input[type=text]', {
oninput : m.withAttr( 'value', filterTerm ),
placeholder : 'filter data'
})
),
m('p',
m('ul',
someData
.reduce( ( acc, val ) =>
( ( filterTerm() && filterTerm() == '/(?:)/' ) || filterFunc(val) )
? acc.concat( val )
: acc,
[]
)
.map( value =>
m('li',
m('input', { value } ),
m(inputWrapper, { value } )
)
)
)
)
)
} )
<script src="https://cdnjs.cloudflare.com/ajax/libs/mithril/0.2.5/mithril.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
但是,此修复揭示了其他问题:因为生成列表(和组件)的数据是在每次绘制时即时创建的,输入绑定的数据结构在每次重绘时都会被简单地处理掉, 当父组件中的 reduce 和 map 函数生成新数组时。
该问题的解决方案是在接收数据的父控制器中执行缩减,以便可以在下一次绘制时检索到。
我对代码所做的其他小调整:
- 视图中虚拟 DOM 子项的数组括号是不必要的(我删除了很多标点符号,出于个人喜好:)。
- 您在
let
赋值中执行的解构可以在函数参数签名中执行,从而减少冗长。 - 删除这些赋值允许 'implicit return' 形式的箭头函数表达式,这使得在删除要考虑的变量数量的同时更容易查看视图输出的结构。
- 使用
const
而不是let
可以清楚地表明分配给引用的值不能更改,这样可以更容易地找到代码可能在何处执行意外操作。 - 将 reduce 表达式的回调缩减为三元结构可以清楚地表明只有 1 个逻辑条件和 2 个可能的结果,并且
concat
允许您在单个表达式中生成新结构(而不是push
ing 和 然后return
ing.
在对 mithril 的文档进行了又一轮的挖掘之后,我来这里回答我自己的问题。
如果我在初始化组件时提供了 key:
m.component( InputWrapper, { key: 'distinguishingKey', value: ... } )
一切都会好起来的。
谢谢巴尼的提示:
...and the components do not have a distinguishing key — ...
感谢 Leo Horie 提供了出色的文档。 (要是我几周前才读到就好了……) http://mithril.js.org/mithril.component.html#data-driven-component-identity