挂载在控制器虚拟机内部不工作
Mount not working inside controller vm
老实说,我不确定为什么这不起作用。似乎是一个非常标准的操作。它没有安装组件,没有抛出错误,也没有直接在它后面 运行 函数。 cfg.AddToCart.vm.addToCart()
一切都好
cfg.AddToCart = {
vm: {
init() {
return;
},
addToCart() {
let parent = document.getElementById('atc-error');
let errEl = document.getElementById('atc-error-component');
if(cfg.state.selections.SIZE) {
m.mount(errEl, null);
} else {
let component = new cfg.selectComponent(cfg.Options, cfg.optionsView);
m.mount(errEl, component);
cfg.util.toggleSlide(parent);
}
}
},
controller() {
cfg.AddToCart.vm.init();
}
};
cfg.AddToCart.view = function() {
return <div id="add-to-cart-container">
<div id="atc-error">
<span>Select a size and add to cart again.</span>
<div id="atc-error-component"></div>
</div>
<div class="small-12 columns">
<button class="large button alert"
onclick={() => {
this.vm.addToCart();
}}>
Add To Cart
</button>
</div>
</div>;
};
我们在整个应用程序中多次使用 new cfg.selectComponent(cfg.Options, cfg.optionsView)
组件,因此这不是一个错误。 #atc-error
设置为 display:none
,但这似乎也不是问题所在。这不是应用程序中唯一的条件挂载,所以这就是为什么我有点难过。
通过将其更改为此模式来工作:
cfg.AddToCart = {
vm: {
init() {
this.errorComponent = m.prop();
},
addToCart() {
let parent = document.getElementById('atc-error');
let errEl = document.getElementById('atc-error-component');
if(cfg.state.selections.SIZE) {
cfg.util.toggleSlide(parent);
setTimeout(() => {
this.errorComponent(null);
}, 400);
} else {
let component = new cfg.selectComponent(cfg.Options, cfg.optionsView);
this.errorComponent(component);
setTimeout(() => {
cfg.util.toggleSlide(parent);
}, 100);
}
}
},
controller() {
cfg.AddToCart.vm.init();
}
};
cfg.AddToCart.view = function() {
return <div id="add-to-cart-container">
<div id="atc-error">
<span>Select a size and add to cart again.</span>
<div id="atc-error-component" class="row">
{this.vm.errorComponent() ? m.component(this.vm.errorComponent()) : ''}
</div>
</div>
<div class="small-12 columns">
<button class="large button alert"
onclick={() => {
this.vm.addToCart();
}}>
Add To Cart
</button>
</div>
</div>;
};
从您构建代码的方式来看,我觉得您错过了很多 Mithril 的好处。特别是:
- 如果您的 'vm' 与控制器没有区别,那么您不需要为此创建和管理一个完整的单独对象。特别是当您使用方法来控制本地组件状态时, 是 控制器的工作。控制器向视图公开一个对象——在那个程度上,这应该被认为是 'vm'。当状态与组件实例外部相关时,拥有一个单独的对象来保存模型状态很有用:您已经在
cfg.state
中拥有它,因此在这种情况下 vm
是多余的。
- Mithril 视图有一个
config
方法,它在每次绘制后公开真正的 DOM 元素。您不需要存储对视图元素的引用,因为您可以在此处执行此操作。这是虚拟 DOM 库如此吸引人的很大一部分原因:视图很聪明,您可以直接在其中引入特定于视图的逻辑。
- 可以直接从视图中调用组件,视图可以使用条件逻辑来确定是否调用它们。
m.mount
仅在初始化 Mithril 应用程序和定义 'top level' 组件时需要;在 Mithril 代码中,您可以直接通过 m
函数调用嵌套组件。
其他一些误解:
- 控制器在之前视图被渲染执行(并且一旦它被执行,它初始化的属性作为第一个参数暴露给你的视图函数),所以你不能在控制器初始化时访问视图创建的元素。
vm
中的 init
函数没有任何作用。
这里是考虑到上述因素的重写代码。我使用普通的 Mithril 而不是 MSX 来避免编译,但你可以轻松地将它转换回来:
// Determine what your external dependencies are
const { state, selectComponent } = cfg
// Define the component
const AddToCart = {
// No need for a separate VM: it is identical in purpose & function to the controller
controller : function(){
// No need to store element references in the model: those are the view's concern.
// Keep the VM / ctrl size to a minimum by only using it to deal with state
this.addToCart = () => {
if( state.selections.SIZE )
this.showSize = false
else {
this.showSize = true
this.slideToErr = true
}
}
},
view : ctrl =>
m( '#add-to-cart-container',
m( '#atc-error', {
// Config exposes the element and runs after every draw.
config : el => {
// Observe state, and affect the view accordingly:
if( ctrl.slideToErr ){
el.scrollIntoView()
// Reset the state flag
ctrl.slideToErr = false
}
}
},
m( 'span', 'Select a size and add to cart again.' ),
// This is an and condition, ie 'if A, then B
ctrl.showSize
// This is how you invoke a component from within a view
&& m( selectComponent )
),
m( '.small-12 columns',
m( 'button.large button alert', {
onclick : () =>
ctrl.addToCart();
},
'Add To Cart'
)
)
)
}
老实说,我不确定为什么这不起作用。似乎是一个非常标准的操作。它没有安装组件,没有抛出错误,也没有直接在它后面 运行 函数。 cfg.AddToCart.vm.addToCart()
cfg.AddToCart = {
vm: {
init() {
return;
},
addToCart() {
let parent = document.getElementById('atc-error');
let errEl = document.getElementById('atc-error-component');
if(cfg.state.selections.SIZE) {
m.mount(errEl, null);
} else {
let component = new cfg.selectComponent(cfg.Options, cfg.optionsView);
m.mount(errEl, component);
cfg.util.toggleSlide(parent);
}
}
},
controller() {
cfg.AddToCart.vm.init();
}
};
cfg.AddToCart.view = function() {
return <div id="add-to-cart-container">
<div id="atc-error">
<span>Select a size and add to cart again.</span>
<div id="atc-error-component"></div>
</div>
<div class="small-12 columns">
<button class="large button alert"
onclick={() => {
this.vm.addToCart();
}}>
Add To Cart
</button>
</div>
</div>;
};
我们在整个应用程序中多次使用 new cfg.selectComponent(cfg.Options, cfg.optionsView)
组件,因此这不是一个错误。 #atc-error
设置为 display:none
,但这似乎也不是问题所在。这不是应用程序中唯一的条件挂载,所以这就是为什么我有点难过。
通过将其更改为此模式来工作:
cfg.AddToCart = {
vm: {
init() {
this.errorComponent = m.prop();
},
addToCart() {
let parent = document.getElementById('atc-error');
let errEl = document.getElementById('atc-error-component');
if(cfg.state.selections.SIZE) {
cfg.util.toggleSlide(parent);
setTimeout(() => {
this.errorComponent(null);
}, 400);
} else {
let component = new cfg.selectComponent(cfg.Options, cfg.optionsView);
this.errorComponent(component);
setTimeout(() => {
cfg.util.toggleSlide(parent);
}, 100);
}
}
},
controller() {
cfg.AddToCart.vm.init();
}
};
cfg.AddToCart.view = function() {
return <div id="add-to-cart-container">
<div id="atc-error">
<span>Select a size and add to cart again.</span>
<div id="atc-error-component" class="row">
{this.vm.errorComponent() ? m.component(this.vm.errorComponent()) : ''}
</div>
</div>
<div class="small-12 columns">
<button class="large button alert"
onclick={() => {
this.vm.addToCart();
}}>
Add To Cart
</button>
</div>
</div>;
};
从您构建代码的方式来看,我觉得您错过了很多 Mithril 的好处。特别是:
- 如果您的 'vm' 与控制器没有区别,那么您不需要为此创建和管理一个完整的单独对象。特别是当您使用方法来控制本地组件状态时, 是 控制器的工作。控制器向视图公开一个对象——在那个程度上,这应该被认为是 'vm'。当状态与组件实例外部相关时,拥有一个单独的对象来保存模型状态很有用:您已经在
cfg.state
中拥有它,因此在这种情况下vm
是多余的。 - Mithril 视图有一个
config
方法,它在每次绘制后公开真正的 DOM 元素。您不需要存储对视图元素的引用,因为您可以在此处执行此操作。这是虚拟 DOM 库如此吸引人的很大一部分原因:视图很聪明,您可以直接在其中引入特定于视图的逻辑。 - 可以直接从视图中调用组件,视图可以使用条件逻辑来确定是否调用它们。
m.mount
仅在初始化 Mithril 应用程序和定义 'top level' 组件时需要;在 Mithril 代码中,您可以直接通过m
函数调用嵌套组件。
其他一些误解:
- 控制器在之前视图被渲染执行(并且一旦它被执行,它初始化的属性作为第一个参数暴露给你的视图函数),所以你不能在控制器初始化时访问视图创建的元素。
vm
中的init
函数没有任何作用。
这里是考虑到上述因素的重写代码。我使用普通的 Mithril 而不是 MSX 来避免编译,但你可以轻松地将它转换回来:
// Determine what your external dependencies are
const { state, selectComponent } = cfg
// Define the component
const AddToCart = {
// No need for a separate VM: it is identical in purpose & function to the controller
controller : function(){
// No need to store element references in the model: those are the view's concern.
// Keep the VM / ctrl size to a minimum by only using it to deal with state
this.addToCart = () => {
if( state.selections.SIZE )
this.showSize = false
else {
this.showSize = true
this.slideToErr = true
}
}
},
view : ctrl =>
m( '#add-to-cart-container',
m( '#atc-error', {
// Config exposes the element and runs after every draw.
config : el => {
// Observe state, and affect the view accordingly:
if( ctrl.slideToErr ){
el.scrollIntoView()
// Reset the state flag
ctrl.slideToErr = false
}
}
},
m( 'span', 'Select a size and add to cart again.' ),
// This is an and condition, ie 'if A, then B
ctrl.showSize
// This is how you invoke a component from within a view
&& m( selectComponent )
),
m( '.small-12 columns',
m( 'button.large button alert', {
onclick : () =>
ctrl.addToCart();
},
'Add To Cart'
)
)
)
}