Apply binding once in knockout component - d3.js

In my web application I have (let's say) 2 tabs, both are custom knockout components. Their visibility is controlled by the following syntax:
<div id="page" data-bind="component: { name: currentTab }"></div>
where currentTab is an observable with the name of the current tab.
In both tabs I have visualisations with D3.js, using custom bindings. The problem is that these custom bindings are reinitialised after opening a tab. Is there a way to only load them once so that they don't need to be redrawn?
Note that the component's viewmodels aren't reinitialised, as they are created with the { instance: new viewModel() } trick:
define(['knockout', 'text!./tab-one.html', 'jquery'], function(ko, template, $) {
function ViewModel() {
var self = this;
};
return {
viewModel: { instance: new ViewModel() },
template: template
};
});

You may need to change slightly your approach:
<div id="page">
<div id="tab-1" data-bind="visible: (currentTab() == 'drawing-1'), component:{name:'drawing-1'}"></div>
<div id="tab-2" data-bind="visible: (currentTab() == 'drawing-2'), component:{name:'drawing-2'}"></div>
</div>
As alternative, you can add some more flexibility to your components if you provide a viewmodel, for example to encapsulate the logic of the visibility inside the component itself, and moreover - if you can also eventually skip the first redraw at component initialization:
This is just an example, but you can see the idea:
ko.components.register('drawing', {
viewModel: function(params) {
// Behaviors
this.active1 = params.currentTab == 'drawing-1';
this.active2 = params.currentTab == 'drawing-2';
this.draw = ko.computed(function() {
if (!ko.computedContext.isInitial()) {
// do some changes to the drawing
}
});
},
template:
'<div data-bind="visible: active1">'+
// drawing 1
'</div>'+
'<div data-bind="visible: active2">'+
// drawing 2
'</div>'
});

Related

Conditional v-if is working only for the first time?

I have this in my view:
<div class="already_voted" v-if="already_voted" >
<p>You already voted or your are not allowed to vote</p>
</div>
This is my method :
upvote: function(com_id) {
var comment_id = {
comment_id :com_id
}
this.$http.post('/blog/article/comment/upvote', comment_id).then(function(response){
upvote_total= response.data.upvote_value;
this.already_voted = response.data.already_voted;
this.$dispatch('child-msg', this.already_voted);
$('.upvote_class_' + com_id ).text(upvote_total);
$('.isDisabledUpvote_' + com_id).addClass('disabled');
$('.isDisabledDownvote_' + com_id).removeClass('disabled');
},function(response){
});
},
Im getting value on click and if its true it need to show this div.
Problem is that this div is showed only for first time when already_voted is true and thats it. Next time when its true nothing happend. Any suggestion?
It looks like you are mixing jQuery and Vue, which should be avoided unless you have a specific reason to do so. Instead you should bind attributes to data. As a basic version of what you are doing you could bind both the disabled attribute and the message to a voted flag:
Markup
<div id="app">
<div v-if="voted">
You have already voted!
</div>
<button v-bind:disabled="voted" #click="vote()">
Vote
</button>
<button v-bind:disabled="!voted" #click="removeVote()">
Un-Vote
</button>
</div>
View Model
new Vue({
el: '#app',
methods: {
vote(){
this.voted = true;
},
removeVote(){
this.voted = false;
}
},
data: {
voted: false
}
});
Here I'm simply binding the disabled attribute using v-bind to the voted flag to disabled the buttons and am using v-if to show a message if the voted flag is true.
Here's the JSFiddle: https://jsfiddle.net/05sbjqLL/
Also be aware that this inside an anonymous function refers to the anonymous function itself, so either assign this to something (var self = this) outside the function or use an arrow function if using ES6.
EDIT
I've updated the JSFiddle to show you how you might handle your situation based on you comments:
https://jsfiddle.net/umkvps5g/
Firstly, I've created a directive that will allow you to initiate your variable from your cookie:
Vue.directive('init', {
bind: function(el, binding, vnode) {
vnode.context[binding.arg] = binding.value;
}
})
This can now be used as:
<div v-init:voted="{{ $request->cookie('voted') }}"></div>
I simply disabled the button to show you how to bind attributes to data, there's loads more that can be done, for example showing the message after a user clicks the button, I've just added a click counter and bound thev-if to that instead, so the message doesn't show until a user clicks the button:
<div v-if="vote_attempts">
You have already voted!
</div>
Then in vote() method:
vote() {
this.voted = true;
this.vote_attempts++;
},
Then data:
data: {
voted: false,
vote_attempts: 0
}

CKEditor - get attribute of element with Onclick

I'm trying to get the value of the attribute data-time-start when I click on the span.
My FIDDLE : http://jsfiddle.net/zagloo/7hvrxw2c/20/
HTML :
<textarea id="editor1"> <span class="sub" id="sub1" data-time-start="0">Hello </span>
<span class="sub" id="sub2" data-time-start="2">My </span>
<span class="sub" id="sub3" data-time-start="6">Name </span>
<span class="sub" id="sub4" data-time-start="8">Is </span>
<span class="sub" id="sub5" data-time-start="12">Zoob</span>
</textarea>
My JS:
var textarea;
$(document).ready(function () {
textarea = $('#ckeditor_block').find('textarea').attr('id');
ckeditor_init();
});
function ckeditor_init() {
CKEDITOR.replace(textarea, {
language: 'fr',
allowedContent: true
});
}
I tried with this:
CKEDITOR.on('click', function (e) {
var element = $(e.target);
console.log(element);
var cursor = element.data("timeStart");
console.log(cursor);
});
But nothing appened ...
How to do that please ? thank you !!
You can't (or better you shouldn't) use the default jQuery event/element handling in this case, because the CKEditor comes with its very own event/ element system.
Update: Based on the comments below, to avoid CKEditor's quirky behaviour, it is better to use attachListener instead of jQuery's 'on' to bind an event listener
Step one: Bind the click event:
var editorInstance = CKEDITOR.instances['editor1'];
editorInstance.on('contentDom', function() {
editorInstance.editable().attachListener(
this.document,
'click',
function( event ) {
// execute the code here
}
);
});
Step two: Find and access the data attribute:
var editorInstance = CKEDITOR.instances['editor1'];
editorInstance.on('contentDom', function() {
editorInstance.editable().attachListener(
this.document,
'click',
function( event ) {
/* event is an object containing a property data
of type CKEDITOR.dom.event, this object has a
method to receive the DOM target, which finally has
a data method like the jQuery data method */
event.data.getTarget().data('time-start');
}
);
});
For more info check the CKEditor docs.
Updated fiddle is here

why phonejs dxContent is not showing?

I am preparing one PhoneJS application. In that I am trying to put one simple butotn inside the view using the following code but not able to see that button and It's showing only loading icon...
home.html
<div data-options="dxView : { name: 'home', title: 'Home' } " >
<div class="home-view" data-options="dxContent : { targetPlaceholder: 'content' } " >
<p>Welcome</p>
<div data-bind="dxButton: { text: 'Click me!', clickAction: showHelloWorld }"></div>
</div>
</div>
home.js
MyApp.home = function (params) {
var viewModel = {
// Put the binding properties here
var showHelloWorld = function() {
alert("Hello world!");
};
ko.applyBindings(myViewModel);
return viewModel;
};
Anyone can help me please?
Your view model code is not well-formed:
MyApp.home = function () {
var viewModel = {
showHelloWorld: function() {
alert("Hello world!");
}
};
return viewModel;
};
viewModel should be valid js object.
You also shouldn't call applyBindings. It will be called by framework.
You should create the HtmlApplication and specify app routing. Not sure, maybe you've done it already.
Here is working fiddle:
http://jsfiddle.net/tabalinas/jb537/
Check out this tutorial "how to build you first PhoneJS app": http://phonejs.devexpress.com/Documentation/Tutorial/Getting_Started/Your_First_Application?version=13_2

Mixedup ajax response on mutliple Form.Request mootools

I have 2 Form.Request in 2 functions that are executed on 2 different buttons clicks
here is fiddle
http://jsfiddle.net/RtxXe/38/
seems like I did not set the events in right order in my functions since they are mixing up the responses. if you hit Clear cache and than Send you still get response from clear cache and vice versa. Unless you reload the page and click again you cant get the right response for each button as it should be .
Since this is not my original form and *I can only change it with js * , i added the clear cache button with new Element. I cant figure out as to why is this happening and any help is appreciated.
this is original html:
<div id="toolbar">
<ul>
<li id="adminsubmit">Send</li>
</ul>
</div>
<div id="response"></div>
<form action="http://www.scoobydoo.com/cgi-bin/scoobysnack" method="post" name="editform" id="myform">
<fieldset>
<!-- form elements go here -->
</fieldset>
<input type="hidden" name="task" value="">
</form>
​ and here is js:
var AdminForm = {
start: function() {
var toolbar = $$('#toolbar ul');
var addbtn2 = new Element('li', {
'id': 'cache',
'class': 'button',
html: 'Clear Cache'
});
addbtn2.inject(toolbar[0], 'top');
var btn1 = $('adminsubmit').getElement('a');
var btn2 = $('cache').getElement('a');
btn1.addEvent('click', function(event) {
event.preventDefault ? event.preventDefault() : event.returnValue = false;
AdminForm.formChange();
});
btn2.addEvent('click', function(event) {
event.preventDefault ? event.preventDefault() : event.returnValue = false;
AdminForm.clearCache();
});
},
formChange: function() {
var adminform = $('myform');
var target = $('response');
var adminsend = new Form.Request(adminform, target, {
onSend: function() {
target.set('html', 'formChange sending');
},
onComplete: function() {
target.set('html', 'formChange sent');
}
});
adminsend.send();
},
clearCache: function() {
var adminform = $('myform');
var target = $('response');
var clearingcahe = new Form.Request(adminform, target, {
onSend: function() {
target.set('html', 'clearCache sending');
},
onComplete: function() {
target.set('html', 'clearCache sent');
}
});
clearingcahe.send();
}
}
window.addEvent('domready', AdminForm.start);​
The Form.Request in Mootools inherits Class.Occlude, see http://mootools.net/docs/more/Class/Class.Occlude
But the Class.Occlude will prevent that several Objects are created and applied to the same DOM Element. That is, it works like a singleton, so the first time you do new Form.Request(adminform, ...) it will return a new instance of Form.Request.
However, the second time you call new Form.Request(adminform, ...) the previous object will be returned instead.
Your fiddle actually demonstrates this very good, because the first one that is clicked of "Clear Cache" or "Send" will be the one that initiates the object. The second time it will discard your options and just return the old object.
So there are two ways to solve this:
Create the Form.Request but don't set the event handlers through the options but through
adminsend.removeEvents('complete'); adminsend.addEvent('complete', ....)
Don't forget to remove the old event handlers before applying the new! otherwise you will just apply more and more eventhandlers.
There are two "buttons" so make two forms, which would be much more semantically correct as well.

MVC & JQuery Multiple Views Multiple JqueryUI Elements

just a quick question about the mvc JqueryUI framework,
i have a _layout.cshtml page which initializes a set of tabs
i have a view which has a jqueryUI datepicker on it.
the View is loaded Dynamically into the tabs and displayed, but if i load a subsequent instance of the View on the Tabs then the datepicker will only populate the first instance of the datepicker.
my question is this
1. MVC uses independent Objects to create independent Views with the same ids as on the views
2. JQueryUI uses the XML Dom with Unique Ids to create its base objects
so how are these supposed to work together.
my View is as follows
<div class="PoCreate">
<div id="pnlProject">
<fieldset>
<legend>Project</legend>
<label for="ProjectNo">
Project #:
</label>
<input type="text" name="ProjectNo" id="ProjectNo" />
<input type="button" name="btnProjectNo" id="btnProjectNo" data-linked-search="#Url.Action("Project", "SearchObj")"
value=".." />
</fieldset>
</div>
</div>
#Url.Script("~/scripts/PageScripts/_PoIndex.js")
The Script file contains
$('.PoCreate').PoCreate({});
and the PO function contains
$.fn.extend({
PoCreate: function (opt)
{
$(this).each(function ()
{
var _self = $(this.parentNode),
_opts = {}, tabIdContext = $(this.parentNode).attr('id');
$.extend(_opts, (opt || {}));
$('.date', _self).each(function ()
{
$(this).attr('id', tabIdContext + '-' + $(this).attr('id'));
$(this).datepicker(Globals.Dates).keypress(function (e) { return false; });
})
$(':button').button().filter('[data-linked-search]').click(function (e)
{
$.extendedAjax({ url: $(this).attr('data-linked-search'),
success: function (response)
{
$('#dialog-form').find('#dialog-search').html(response).dialog(Globals.Dialogs);
}
});
});
});
}
});
I found a way to solve this,
On the Create of the JQuery Widget i have to rename the ID of the DatePicker field so that it is unique for the Tab created.
so my TabId = ui-tab-01
and DatePickerId = DatePicker1
renaming the DatePickerId so that it is now ui-tab-01-DatePicker1

Resources