I'm working on a project where v-cloak is styled with display: none, and decorates the body. This means everything is hidden until the Vue instance is ready.
I've made a component that inserts a chart (using highcharts). The component is inserted on ready, yet the chart does not display correctly.
Here is a jsfiddle demonstrating the problem.
I could solve the problem by polling until the element is displayed, but is there a more elegant solution?
Use Vue's nextTick to inject the chart, this will guarantee that the DOM is ready to be updated:
ready: function() {
this.$nextTick(function() {
this.chart = new Highcharts.Chart(this.opts);
});
}
http://jsfiddle.net/crabbly/hje7bqrn/
Related
I'm fairly new to Vue.JS and currently having an issue listening to a click event on a component.
JS:
Vue.component('photo-option', {
data: function () {
return {
count: 0
}
},
props: ['src'],
template: `
<img :src=src v-on:click="$emit('my-event')" />
`
});
HTML:
<photo-option :src=uri v-for='uri in aboutUsPhotos' v-on:my-event="foo" />
...where foo is a method on my main Vue instance.
The above is based on the Vue.JS docs for handling component events, and I can't see what I'm doing wrong. The alert doesn't fire, and there's no errors in the console.
Before I found those docs, I also tried simply adding v-on:click='...' to both the JS (i.e. the template) and the HTML, each with no success.
What am I doing wrong?
[EDIT]
This is happening because the code is picked up by a lightbox script and has its DOM position changed. So presumably the binding/event attachment is being lost.
Does Vue have any way of allowing for this, perhaps by 'reinitialising' itself on an element, or something?
I have an ionic project that is using dragula, but I'm having an issue setting the mirrorContainer. I'd like to make the container something other than the default body because I believe that's what is attributing to a strange scrolling problem I'm having while dragging.
I've created my bag in html
<div class="step-container--line" [dragula]='"bag"' id="mirror">
<div class="card">
....
</div>
Then in the JS, I've initialized dragula in the constructor and started to set its options.
constructor(private dragulaService: DragulaService) {
dragulaService.setOptions('bag', {
moves: function (el, container, handle) {
return handle.className === 'step__menu__button';
},
direction: 'vertical',
//mirrorContainer: document.getElementById('mirror')
});
dragulaService.drag.subscribe((value) => {
this.onDrag(value.slice(1));
});
dragulaService.drop.subscribe((value) => {
this.onDrop(value.slice(1));
});
}
The problem is; when I add mirrorContainer: document.getElementById('mirror') to the setOptions, my mirror container comes back as null. I'm assuming because this loads before the DOM does and there's no instance of #mirror yet.
If I moved everything down into ionViewDidLoad(){}, I get an error stating that the bag 'bag' already exists.
I'm not sure the best way to initialize or add to the options after the DOM loads. Any ideas?
I know I'm late to the party but I had the same issue using Angular 11.
After your element is in the DOM and is available you can do this.
ionViewDidLoad() {
(this.dragulaService as any).groups['bag'].options.mirrorContainer = document.getElementById('mirror');
}
I'm not sure why I can't get the button element using my UI hash. This is what my Layout looks like:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
ui: {
btnSave: "#btnSave"
},
events: {
"click #ui.btnSave": "onSave"
},
onInitialize: function () {
this.listenTo(App.vent, "DisableSaveButton", function(val) {
this.disableSaveButton(val);
},this);
},
disableSaveButton: function () {
this.ui.btnSave.prop("disabled",val).toggleClass("ui-state-disabled",val);
},
onSave: function () {
alert("saved!");
}
})
In VS2013, when my breakpoint hits the line inside disableSaveButton method, I entered $("#btnSave") into the Watch window and I was able to get the element back. I could tell because it had a length of 1. From this, I know the button is rendered. However, if I enter this.ui.btnSave into the Watch window, I would get an element with length of 0.
My BaseLayout object is basically a custom object extended from Marionette.Layout
Marionette version: 1.8.8
Any ideas why I can't find the button element using this.ui.btnSave?
Thanks in advance!
Got some help from a coworker and the issue might be because the element is out of scope. Basically, inside the Layout object, 'this' does not contain the element. We were able replace 'this.ui.btnSave' with '$("#btnSave",this.buttonset.el)' and that works fine. buttonset is the region that actually contains the html element.
This seems like an inconsistency because even though the ui hash didn't work, the click event utilizing the ui hash did work.
UPDATE 6/3/2015:
Another coworker of mine provided a better solution. Basically, in my Layout I use a display function to display my view. It looks something like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
}
})
Basically, I'm saying to set the html of my region, which is this.buttonset.el, to my template's html. As of now, my layout doesn't know any of the elements inside the region. It just contains a region which displays the elements. So there is some sort of disconnect between my layout and the elements in my region.
The correct solution, as opposed to my earlier workaround, is to simply add the following line of code at the end:
this.bindUIElements();
From Marionette Annotated Source:
This method binds the elements specified in the “ui” hash inside the
view’s code with the associated jQuery selectors.
So this final code looks like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
this.bindUIElements();
}
})
With this, I was able to finally able to retrieve my element using this.ui.btnSave.
I have a grid column with a custom renderer. Like so.
grid.healthCheckColumn = Ext.create('Ext.grid.column.Column', {
id: 'healthCheckColumn',
header: 'Health Check',
renderer: Ext.bind(me.renderHealthCheckColumn, me)
});
This renderer returns a div with class 'health-check-button', and that renders fine. Here’s my problem.
Once the renderer has populated a grid cell with the div, I want an event to fire which will allow me to render an ExtJS component to that div. Here’s the component I want to render:
var button = Ext.create('Ext.button.Button', {
ui: 'plain',
disable: val,
text: 'Analyze',
renderTo: div_element
});
The reason this won’t work is because an ExtJS component can only render to an element that’s already in the DOM, and that element won’t exist until after the renderer has run.
Obviously, I could put a call to Ext.defer() in my renderer, to fire an event after a timeout, but that seems hackish. That solution (to a similar question) was suggested here:
Ext js event fired or called after renderer
I’ve tried to figure out the order in which events fire, and in what sequence, but that’s not documented and I can’t make sense of the ExtJS source code. beforerender, render, and afterrender all fire before the renderer runs, which isn’t helpful.
Any suggestions? I’m running ExtJS 4.3.
I'm using ExtJS 3.2.1 and I need a component almost identical to the bundled HtmlEditor, with one exception: it must start editing the HTML source code directly. The reason I don't use a normal TextArea is that the user should be able to preview the result of his actions before submitting.
I've tried calling toggleSourceEdit(), as per ExtJS documentation, with no success. Debugging, I see that the editor object has the sourceEditMode property set to true, and the Source Edit button seems as if it was "pressed", but clicking on it does not render the typed HTML, and clicking it again goes to the Source Mode.
I've tried calling toggleSourceEdit() after the container show() method, on the container afterLayout listener and on the editor afterRender listener. I've tried also calling it on another button that I added to the container. The result is the same on every try.
The only other option I see is updating ExtJS to 3.3.0, but I haven't seem anything related on the changelogs. Either way, it's going to be my next step. EDIT: The app had another problems when updating, we'll make a bigger effort to update later. As of right now, we are using the HtmlEditor in its original setting.
Thanks!
ran into the same problem (using 3.3.0 by the way)
stumbled upon a fix by dumb luck. i have no idea why this works, but second time is the charm. call it twice in a row to achieve the desired effect..
HTMLEditor.toggleSourceEdit(true);
HTMLEditor.toggleSourceEdit(true);
hope that helps!
Rather calling toggleSourceEdit(), try to setup the configuration while you create HtmlEditor Object
Using toggleSourceEdit() caused some problems for me. One was that this seemed to put the editor somewhere in limbo between source edit and WYSIWYG mode unless I used a timeout of 250ms or so. It also puts the focus in that editor, and I don't want to start the form's focus in the editor, especially since it's below the fold and the browser scrolls to the focused html editor when it opens.
The only thing that worked for me was to extend Ext.form.HtmlEditor and then overwrite toggleSourceEdit, removing the focus command. Then adding a listener for toggling to the source editor when the component is initialized. This is for Ext 4.1 and up. For older versions, replace me.updateLayout() with me.doComponentLayout().
var Namespace = {
SourceEditor: Ext.define('Namespace.SourceEditor', {
extend: 'Ext.form.HtmlEditor',
alias: 'widget.sourceeditor',
initComponent: function() {
this.callParent(arguments);
},
toggleSourceEdit: function (sourceEditMode) {
var me = this,
iframe = me.iframeEl,
textarea = me.textareaEl,
hiddenCls = Ext.baseCSSPrefix + 'hidden',
btn = me.getToolbar().getComponent('sourceedit');
if (!Ext.isBoolean(sourceEditMode)) {
sourceEditMode = !me.sourceEditMode;
}
me.sourceEditMode = sourceEditMode;
if (btn.pressed !== sourceEditMode) {
btn.toggle(sourceEditMode);
}
if (sourceEditMode) {
me.disableItems(true);
me.syncValue();
iframe.addCls(hiddenCls);
textarea.removeCls(hiddenCls);
textarea.dom.removeAttribute('tabindex');
//textarea.focus();
me.inputEl = textarea;
} else {
if (me.initialized) {
me.disableItems(me.readOnly);
}
me.pushValue();
iframe.removeCls(hiddenCls);
textarea.addCls(hiddenCls);
textarea.dom.setAttribute('tabindex', -1);
me.deferFocus();
me.inputEl = iframe;
}
me.fireEvent('editmodechange', me, sourceEditMode);
me.updateLayout();
}
})
}
Then to use it:
Ext.create('Namespace.SourceEditor', {
/*regular options*/
listeners: {
initialize: function(thisEditor) {
thisEditor.toggleSourceEdit();
}
}
});
htmlEditor.toggleSourceEdit(true);
one time should be enough if you do this listening to the afterrender event of the editor.