Kendo custom widget with MVVM and init event - how? - events

I try to implement a custom kendo widget with "Init" event. If the widget is initialized with JavaScript, the event is triggered. But when the widget is initialized with data attributes - it doesn't. What do I miss?
Sample: http://dojo.telerik.com/UQoWi
Note: As you can see, I've tried to handle the "init" event according to the KendoUI's documentation:
... data-init="onInit" data-bind="events: { init: onInit }" ...

Suppliment: I'd like to add that i completely understand WHY there is not an init binder already. It doesn't really make sense. As a ViewModel can be BOUND to many different things in Kendo, an init callback inside a Viewmodel for a Widget just doesn't make sense. That's why the dataBinding and dataBound is used. When the widget is created it DOES invoke the handler for data-init="", it just has to be in scope. That's the one time call for a widget. The ViewModel Observable can be bound and unbound to many different widgets. How would the system know that the Observable ViewModel is supposed to be strictly tied to that one specific widget? It wouldn't unless you update the Widget to had an internal data source and observable.
If this Observable is bound to multiple of your custom widgets, the onInit will be called for each widget. It will not be called for any of the built in widgets as they don't trigger(INIT). That's easily fixed by overloading the fn.init function, or extending them. Or, even creating a global onInit function that is used in the data-init element attribute, since you can get access to the widget itself through the parameters. $(element).data("kendoWidget") etc.
Hope this helps! It's certainly helped me understand everything.
I've been able to do what you want. It seems I was greatly mistaken about what the data-bind="events: {}" is for. The handlers within that definition appear to be handlers for the HTML events. So we can add a new 'init' handler to the Widget system itself. I've been able to get your example to work with the following code below.
The key is to add a NEW binder for a Kendo Event called 'init'.
kendo.data.binders.widget.init =
kendo.data.Binder.extend({
function(widget, bindings, options) {
Binder.fn.init.call(this, widget.element[0], bindings, options);
this.widget = widget;
},
refresh: function () {
var init = this.bindings.init.get();
}
});
Then we can use the data-bind="init: onInitFromMVVM" to bind to a one time even call. This will only be a one time call because it's only triggered in the init of the Widget.
I went through the source code over and over and the events: {} confused me until I started reading the documentation. Then I realized that almost all the handlers inside events: {} were for HTML events. Things like visible, onmouseover, onmouseexit, etc. So I then created a new binder called 'init', and your code worked after that.
The code below produces the following output:
widget init proof
global init proof
init mvvm proof
It even calls the handler for data-init in the global scope.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled</title>
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.rtl.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.default.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.mobile.all.min.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/angular.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/jszip.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/kendo.all.min.js"></script>
<script>
var binders = kendo.data.binders,
Binder = kendo.data.Binder,
proxy = $.proxy,
ui = kendo.ui,
Widget = ui.Widget,
INIT = 'init';
(function ($) {
kendo.data.binders.widget.init =
kendo.data.Binder.extend({
init: function(widget, bindings, options) {
Binder.fn.init.call(this, widget.element[0], bindings, options);
this.widget = widget;
},
refresh: function () {
var init = this.bindings.init.get();
}
});
var MyWidget = Widget.extend({
options: {
name: 'MyWidget',
},
events: [
INIT
],
init: function (element, options) {
var that = this;
console.log('widget init proof');
Widget.fn.init.call(this, element, options);
that.trigger(INIT);
}
});
ui.plugin(MyWidget);
})(jQuery);
$(document).ready(function () {
var observableModel = new kendo.data.ObservableObject({
onInitVM: function (e, opts) {
console.log('init mvvm proof');
}
});
kendo.bind($('#myWidgetMVVM'), observableModel);
});
</script>
</head>
<body>
<div>Open console...</div>
<input type="hidden" id="myWidgetMVVM" data-init="onInit" data-role="mywidget" data-bind="init: onInitVM" />
<script>
function onInit(element, options) {
console.log("global init proof");
}
</script>
</body>
</html>

Related

how to pass data from xamarin shared project to an html

I have a webview in my page where I'm showing a chart(Fusion Chart).
I wrote a html page with js function as follows:
<html lang="en">
<head>
<script src="file:///android_asset/fusioncharts.js"></script>
<script src="file:///android_asset/fusioncharts.charts.js"></script>
</head>
<body>
<div id="chart-container"></div>
<script>
function loadChart(chartData) {
const dataSource = {
chart: {
theme: "fusion",
"showPlotBorder": "1",
"plotBorderColor": "#ffffff"
},
data: chartData
};
FusionCharts.ready(function () {
var myChart = new FusionCharts({
type: "column2d",
renderAt: "chart-container",
width: "100%",
height: "100%",
dataFormat: "json",
dataSource
}).render();
});
}
loadChart(chartData);
</script>
</body>
</html>
I need to pass data to the loadchart function from my shared project with the help of my webview. Is this possible?
There are so many ways of doing this on Android, though I would caution you to test it well on different devices depending on what default browser is being used for the WebView.
Luckily, since you are using Xamarin Forms, you could just use the Javascript Evaluation function that enables you to just put your JS code inside the function. And then time it right, and you can call a JS function which will take values from your C#.
Here's the documentation by MS on the functions
Eval() or EvaluateJavaScriptAsync().
Here are some examples of it's usage:
webView.EvaluateJavaScriptAsync(string.Format("document.getElementById(\"{0}\").value = \"{1}\"", "langs1", from));
webView.EvaluateJavaScriptAsync(string.Format("DDMENU(0)"));
Let me know if you have any questions!

polymer iron-list only loading 20 items

I have an a test array that I am retrieving from iron-ajax with around 1000 items. I set that return array to the people property of my custom polymer element. Iron-list is displaying the first 20 results, but when i scroll down the rest of the results are not being loaded.
<link rel="import" href="/bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="/bower_components/iron-list/iron-list.html">
<link rel="import" href="/bower_components/iron-flex-layout/classes/iron-flex-layout.html">
<dom-module id="people-list">
<template>
<iron-list items="[[people]]" as="item" class="flex">
<template>
<div style="background-color:white; min-height:50px;">[[item.city]]</div>
</template>
</iron-list>
<iron-ajax
auto
id="ajaxPeopleList"
method="POST"
url="/people/list"
handle-as="json"
last-response="{{people}}">
</iron-ajax>
</template>
<script>
Polymer({
is: 'people-list',
properties: {
people: {
type: Array
}
}
});
</script>
</dom-module>
I think it may have to do with the height of the screen/iron-list element, but I am stuck and not sure how to proceed.
I can get it load all items if I set the height of the iron-list elements in pixels. But I just want it to fit on the screen. The docs make it look like you can use the iron-flex layout and use the class flex, which is what I tried to do in my sample code, but it has no effect.
This is because nothing is firing the iron-resize event to tell the iron-list to redraw the items. According to the docs:
Resizing
iron-list lays out the items when it recives a notification via the iron-resize event. This event is fired by any element that implements IronResizableBehavior.
By default, elements such as iron-pages, paper-tabs or paper-dialog will trigger this event automatically. If you hide the list manually (e.g. you use display: none) you might want to implement IronResizableBehavior or fire this event manually right after the list became visible again. e.g.
document.querySelector('iron-list').fire('iron-resize');
I've added the following to your code (which is probably a bit of a hack) and got it working:
ready: function () {
document.addEventListener("scroll", function () {
// fire iron-resize event to trigger redraw of iron-list
document.querySelector('iron-list').fire('iron-resize');
});
}
Ben Thomas is right. You're getting the json elements but it's not displayed because the iron-list needs to be redrawn. Since i had the same problem, i went to google polymers website and found THIS code example.
In short for your example:
<script>
// Only execute after DOM and imports (in our case the json file) has been loaded
HTMLImports.whenReady(function() {
Polymer({
is: 'people-list',
properties: {
people: {
type: Array
}
},
attached: function() {
// Use the top-level document element as scrolltarget
this.$.list.scrollTarget = this.ownerDocument.documentElement;
}
});
});

why isn't the checkbox change event triggered when i do it programatically in d3js

So I have a function that changes the checkbox
somefunction() {
d3.select("input").property("checked", true);
}
and in another function I have that detects a change in the checkbox like this:
d3.selectAll("input").on("change", change);
The problem I have is when it changes the checkbox without me clicking on it, it will not detect it as a "proper" change. What can I do to make it detect something else changing it as a "proper" change?
d3 is using the standard addEventListener in it's on method. The problem you have is that changing the checked property doesn't raise any events for the addEventListener to hear. The proper way would be to just call the handler yourself after setting the proptery:
var myInput = d3.select("input");
myInput.property("checked", true);
myInput.on("change")(); // call it yourself
Full working code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<input type="checkbox" />
<script>
var checked = false;
setInterval(function(){
checked = !checked;
var myInput = d3.select("input");
myInput.property("checked", checked);
myInput.on("change")();
}, 2000);
d3.select("input").on("change", function(d){
console.log('checkbox changed');
});
</script>
</body>
</html>
The solution suggested by Mark works flawlessly however just alternatively I would also like to mention that rather than calling the event handler yourself you can trigger the event yourself and leave rest of the code as it is. The onchange event is triggered on following sequence focus-valueChange-unfocus. Now this does not happen when you change it without clicking on it, the value changes but no focus. Anyway so instead of relying on that you can just trigger onchange event yourself. I created a fiddle here which explains it. Hope it helps: https://jsfiddle.net/xcjje9r3/6/
window.clickMe = function() {
tmp = d3.select('#cbox');
if (tmp.property('checked') == false) tmp.property('checked', true);
else tmp.property('checked', false);
tmp[0][0].dispatchEvent(evt); //MANUALLY TRIGGER EVENT
}
//tmp[0][0] is becoz d3.select returns an array and [0][0] position stores the actual DOM element in this array.
window.listenerInitialize = function() {
evt = document.createEvent("HTMLEvents"); //CREATE NEW EVENT WITH TYPE
evt.initEvent("change", false, true); //THE EXACT EVENT TO FIRE
d3.select('#cbox').on('change', alerting); //ADDING LISTENER FOR EVENT
}
window.alerting = function() {
alert('testEvent');
}

Twitter Bootstrap 3 dropdown menu disappears when used with prototype.js

I have an issue when using bootstrap 3 & prototype.js together on a magento website.
Basically if you click on the dropdown menu (Our Products) & then click on the background, the dropdown menu (Our Products) disappears (prototype.js adds "display: none;" to the li).
Here is a demo of the issue:
http://ridge.mydevelopmentserver.com/contact.html
You can see that the dropdown menu works like it should without including prototype.js on the page at the link below:
http://ridge.mydevelopmentserver.com/
Has anyone else ran into this issue before or have a possible solution for the conflict?
EASY FIX:
Just replace Magento's prototype.js file with this bootstrap friendly one:
https://raw.github.com/zikula/core/079df47e7c1f536a0d9eea2993ae19768e1f0554/src/javascript/ajax/original_uncompressed/prototype.js
You can see the changes made in the prototype.js file to fix the bootstrap issue here:
https://github.com/zikula/core/commit/079df47e7c1f536a0d9eea2993ae19768e1f0554
NOTE: JQuery must be include in your magento skin before prototype.js.. Example:
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/prototype/prototype.js"></script>
<script type="text/javascript" src="/js/lib/ccard.js"></script>
<script type="text/javascript" src="/js/prototype/validation.js"></script>
<script type="text/javascript" src="/js/scriptaculous/builder.js"></script>
<script type="text/javascript" src="/js/scriptaculous/effects.js"></script>
<script type="text/javascript" src="/js/scriptaculous/dragdrop.js"></script>
<script type="text/javascript" src="/js/scriptaculous/controls.js"></script>
<script type="text/javascript" src="/js/scriptaculous/slider.js"></script>
<script type="text/javascript" src="/js/varien/js.js"></script>
<script type="text/javascript" src="/js/varien/form.js"></script>
<script type="text/javascript" src="/js/varien/menu.js"></script>
<script type="text/javascript" src="/js/mage/translate.js"></script>
<script type="text/javascript" src="/js/mage/cookies.js"></script>
<script type="text/javascript" src="/js/mage/captcha.js"></script>
I've also used code from here: http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework but without a need to modify any source. Just put code below somewhere after prototype and jquery includes:
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip',
'hide.bs.popover',
'hide.bs.tab'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
Late to the party, but found this github issue which links to this informational page which links to this jsfiddle which works really nicely. It doesn't patch on every jQuery selector and is, I think, the nicest fix by far. Copying the code here to help future peoples:
jQuery.noConflict();
if (Prototype.BrowserFeatures.ElementExtensions) {
var pluginsToDisable = ['collapse', 'dropdown', 'modal', 'tooltip', 'popover'];
var disablePrototypeJS = function (method, pluginsToDisable) {
var handler = function (event) {
event.target[method] = undefined;
setTimeout(function () {
delete event.target[method];
}, 0);
};
pluginsToDisable.each(function (plugin) {
jQuery(window).on(method + '.bs.' + plugin, handler);
});
};
disablePrototypeJS('show', pluginsToDisable);
disablePrototypeJS('hide', pluginsToDisable);
}
Using the * selector with jQuery is not advised. This takes every DOM object on the page and puts it in the variable.
I would advice to select the elements that use a Bootstrap component specific. Solution below only uses the dropdown component:
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('.dropdown');
jQuery.each(['hide.bs.dropdown'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
Very late to the party: if you don't feel like having extra scripts running, you can add a simple CSS override to prevent it from getting hidden.
.dropdown {
display: inherit !important;
}
Generally the use of !important in CSS is advised against, but I think this counts as an acceptable use in my opinion.
see http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework/.
It's quite an easy fix to validate the namespace of the element clicked.
Add a validation function to prototype.js:
and after that, validate the namespace before the element will be hidden:
hide: function(element) {
element = $(element);
if(!isBootstrapEvent)
{
element.style.display = 'none';
}
return element;
},
Replacing Magento's prototype.js file with the bootstrap friendly version suggested by MWD is throwing an error that prevents saving configurable products:
Uncaught TypeError: Object [object Array] has no method 'gsub' prototype.js:5826
(Running Magento Magento 1.7.0.2)
evgeny.myasishchev solution worked great.
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
This answer helped me to get rid of bootstrap and prototype conflict issue.
As #GeekNum88 describe the matter,
PrototypeJS adds methods to the Element prototype so when jQuery tries
to trigger the hide() method on an element it is actually firing the
PrototypeJS hide() method, which is equivalent to the jQuery
hide() method and sets the style of the element to display:none;
As you suggest in the question itself either you can use bootstrap friendly prototype or else you can simply comment out few lines in bootstrap as bellow,
inside the Tooltip.prototype.hide function
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
I realise that this is a pretty old post by now, but an answer that no-one else seems to have mentioned is simply "modify jQuery". You just need a couple of extra checks in jQuery's trigger function which can be found around line 332.
Extend this if statement:
// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
... to say this:
// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
// Check for global Element variable (PrototypeJS) and ensure we're not triggering one of its methods.
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) &&
( !window.Element || !jQuery.isFunction( window.Element[ type ] ) ) ) {
"#6170" only seems to be mentioned in jQuery once so you can do a quick check for that string if you're working with a compiled (complete) jQuery library.

Click event not firing in main view in Backbonejs

I have really weird problem. I am trying to implement "root" view which also works as some namespace structure. Same principle introduced in codeschool.com course part II. In this root view, I want to catch event "click button", but that's the problem. When I click on button nothing happened.
window.App = new (Backbone.View.extend({
el: $("#app"),
Collections: {},
Models: {},
Views: {},
Routers: {},
events: {
'click button' : function(e) {
alert("Thank god!");
}
},
render: function(){
//for test purposes
console.log($("#app").find("button"));
console.log(this.$el.find('button'));
},
start: function(){
this.render();
new App.Routers.PostsRouter();
Backbone.history.start({pushState: true});
}
}))();
$(document).ready(function() { App.start() });
The HTML look like this
<body>
<div id="app">
<div id="posts"></div>
<button>Click me</button>
</div>
</body>
And what's really weird is output from console.log in render function. Both selectors are same, even the context is same, so where is problem?
console.log($("#app").find("button")); //this returns correct button
console.log(this.$el.find('button')); //this returns 0 occurences (WTF?)
EDIT:
After little change at el: "#app", still same problem. Problem was (thanks to #nemesv) in instantiating this class before DOM is loaded. But however, it's not possible to instantiating after DOM is loaded, because then it's not possible to use that namespace structure (eg. App.Model.Post = Backbone.Model.extend() ). But this "main view with namespace structure" is introduced in codeschool.com course as some sort of good practice. Solution can be found there http://jsfiddle.net/BckAe
You have specified your el as a jquery selector but because you are inside an object literal it evaluates immediately so before the DOM has been loaded.
So the el: $("#app"), won't select anything.
You can solve this by using one of the backbone features that you can initilaize the el as a string containing a selector.
So change your el declaration to:
el: "#app"
Your click event is not triggered because you instantiate your view before the DOM is loaded so backbone cannot do the event delegation your you.
So you need separate your view declaration and creation into two steps. And only instantiate your view when the DOM is loaded:
var AppView = Backbone.View.extend({
el: "#app",
//...
});
$(document).ready(function()
{
window.App = new AppView();
App.start()
});
Demo: JSFiddle.

Resources