我有一个组件,它被插入到DOM中作为''标签(例如,默认行为)。该组件的工作是包装第三方jQuery工具,我试图确保它响应"调整大小"事件所以我想明确设置 width 和 height 样式属性。
在组件中,很容易成为style属性:
attributeBindings: ['style'],
style: function() {
return "width: auto";
}.property('widthCalc'),
在这种情况下,这样可行,但没有做任何有用的事情,因为样式只返回一个静态字符串(width: auto
)。
相反,我想做的是 - 根据对计算属性widthCalc
的任何更改 - 根据新值设置宽度。所以这是下一个合乎逻辑的步骤:
style: function() {
var width = $('body')[0].offsetWidth;
return 'width: ' + width + 'px';
}.property('widthCalc'),
这也是有效的,动态地将DIV设置为身体宽度的宽度(注意:这不是我想要的,但它确实证明了这个简单的绑定有效)。现在我真正想要的是从组件上的计算属性中获取宽度值,但我甚至不必走那么远就遇到麻烦;请注意,我切换到本地化的组件范围选择器而不是全局jQuery选择器:
style: function() {
var width = this.$().offsetWidth;
return 'width: ' + width + 'px';
}.property('widthCalc'),
不幸的是,这会导致页面无法加载并出现以下错误:
未捕获错误:您执行的某些操作导致视图在渲染后但在插入DOM之前重新渲染。
我想这是Ember run-loop juju,但我不确定如何继续。任何帮助将不胜感激。
答案 0 :(得分:1)
由于在将组件添加到dom之前无法在组件中调用this.$()
,因此请提供初始值,直到组件准备就绪。
例如,
为属性style
和didInsertElement
事件设置默认值会重新打开该类,并使用style
this.$()
定义为计算属性
http://emberjs.jsbin.com/delexoqize/1/edit?html,js,output
<强> JS 强>
App.MyCompComponent = Em.Component.extend({
attributeBindings:["style"],
style:"visibility:hidden",
prop1:null,
initializeThisStyle:function(){
this.set("style","visibility:visible");
this.reopen({
style:function(){
// var thisOffsetWidth = this.$().get(0).offsetWidth;
return "visibility:visible;color:red;background-color:lightgrey;width:"+this.get("prop1")+"px";
}.property("prop1")
});
}.on("didInsertElement")
});
或者处理this.$()
引发的错误并提供默认值。之后,当添加组件时,将按计划计算属性。
http://emberjs.jsbin.com/hilalapoce/1/edit?html,js,output
<强> JS 强>
App.MyCompComponent = Em.Component.extend({
attributeBindings:["style"],
style:function(){
try{
this.$();//this will throw an erro initialy
return "visibility:visible;color:red;background-color:lightgrey;width:"+this.get("prop1")+"px";
}catch(e){
return "color:blue";
}
}.property("prop1"),
prop1:null
});
答案 1 :(得分:1)
对于我试图解决的组件,我结束了一个对我来说似乎有效的解决方案,我将在下面分享。要了解我收到错误的原因以及如何更直接地解决该错误,请参阅上面@melc的评论。
我正在解决的是调整包含在Ember组件中的jQuery组件的大小。在许多情况下,仅通过CSS优雅地处理调整大小,但是一些jQuery组件 - 包括来自knob的非常好的aterrien组件 - 具有直接涉及的JS,因此需要容器宽度和高度属性由Ember组件明确设置,以便它做出适当的反应。
解决这个问题时,我意识到我的用例有两个问题:
解决方案的第一部分是监听页面的页面级调整大小。我这样做有以下几点:
resizeListener: function() {
var self = this;
self.$(window).on('resize', Ember.run.bind(self, self.resizeDidHappen));
}.on('didInsertElement'),
当在“页面”级别完成调整大小时,我现在希望我的组件检查调整大小对组件的影响:
resizeDidHappen: function() {
Ember.run.debounce(this, function() {
// get dimensions
var newWidth = Number(this.$().parent().get(0).offsetWidth);
var newHeight = Number(this.$().parent().get(0).offsetHeight);
// set instance variables
this.set('width', newWidth);
this.set('height', newWidth);
// reconfigure knob
this.$('.knob').trigger(
'configure',
{
width: newWidth,
height: newWidth
}
);
}, 300);
}
这解决了页面调整大小问题,如果它存在于隔离状态,但为了使组件成为一个好主意,也可以解决可见性用例(当然在我的情况下它很关键) 。
为什么呢?好吧,我能想到两个原因:
一个问题是,可见性更改没有DOM级事件,那么我们如何在不轮询间隔的情况下对可见性的变化做出反应?在大多数情况下,将会有一个控制可见性状态的UI元素。在我的例子中,它是Bootstrap的标签栏,在这种情况下,它们具有在标签可见时触发的事件。大。这是Bootstrap选择器的选择器(假设您在新显示的选项卡的内容区域内):
visibilityEventEmitter: function(context) {
// since there is no specific DOM event for a change in visibility we must rely on
// whatever component is creating this change to notify us via a bespoke event
// this function is setup for a Bootstrap tab pane; for other event emmitters you will have to build your own
try {
var thisTabPane = context.$().closest('.tab-pane').attr('id');
var $emitter = context.$().closest('.tab-content').siblings('[role=tabpanel]').find('li a[aria-controls=' + thisTabPane + ']');
return $emitter;
} catch(e) {
console.log('Problem getting event emitter: %o', e);
}
return false;
},
visibilityEventName: 'shown.bs.tab',
然后我们只需要添加以下代码:
_init: function() {
var isVisible = this.$().get(0).offsetWidth > 0;
if (isVisible) {
this.visibilityDidHappen();
}
}.on('didInsertElement'),
visibilityListener: function() {
// Listen for visibility event and signal a resize when it happens
// note: this listener is placed on a DOM element which is assumed
// to always be visibile so no need to wait on placing this listener
var self = this;
Ember.run.schedule('afterRender', function() {
var $selector = self.get('visibilityEventEmitter')(self);
$selector.on(self.get('visibilityEventName'), Ember.run.bind(self, self.visibilityDidHappen ));
});
}.on('didInsertElement'),
visibilityDidHappen: function() {
// On the first visibility event, the component must be initialised
if(!this.get('isInitialised')) {
this.initiateKnob();
} else {
// force a resize assessment as window sizing may have changed
// since last time component was visible
this.resizeDidHappen();
}
},
请注意,这也会导致我们调整大小侦听器的微小重构,从didInsertElement
事件中删除它的触发器,而是由initiateKnob
触发,这不会在Ember组件加载时发生,而是懒惰在DOM的第一个可见点加载。
initiateKnob: function() {
var self = this;
this.set('isInitialised', true);
var options = this.buildOptions();
this.$('.knob').knob(options);
this.syncValue();
this.resizeDidHappen(); // get dimensions initialised on load
console.log('setting resize listener for %s', self.elementId);
self.resizeListener(); // add a listener for future resize events
},
resizeListener: function() {
this.$(window).on('resize', Ember.run.bind(this, this.resizeDidHappen));
},
在很大程度上但并非完全。这是有效的:
什么行不通的是:
我得到的错误是:
vendor.js:13693 Uncaught TypeError: undefined is not a function
Backburner.run vendor.js:13716
Backburner.join vendor.js:34296
run.join vendor.js:34349
run.bind vendor.js:4759
jQuery.event.dispatch vendor.js:4427
jQuery.event.add.elemData.handle
不知道该怎么做...任何帮助将不胜感激。完整代码可以在这里找到: