dojo require and scope - javascript

Can anyone explain to me why when drawSection is called 'this' value becomes the global scope ?.
Is there anyway to use require here without having to save the widget in another variable before i lose it ?
define("my/TextBox", [
"dojo/_base/declare",
"dijit/form/ValidationTextBox"
], function(
declare, ValidationTextBox
) {
function drawSection() {
alert(this);
require(["dojo/dom-construct"], function(domConstruct) {
alert(this); // this = window
});
};
return declare([ValidationTextBox], {
postCreate: function() {
this.inherited(arguments);
drawSection.call(this)
}
});
});

It's quit simple use dojo/_base/lang hitch() function to solve the issue .
because the function inside the require(["dojo/dom-construct"], function(domConstruct) {....}) is refering to global context ,
so use lang.hitch function in the current context (by using this) and probleme is solved
Here is a Fiddle
and above working snippet :
define("my/TextBox", [
"dojo/_base/lang",
"dojo/_base/declare",
"dijit/form/ValidationTextBox"
], function(lang,
declare, ValidationTextBox
) {
function drawSection() {
alert(this);
require(["dojo/dom-construct"], lang.hitch(this,function(domConstruct) {
alert(this); // this = window
}));
};
return declare([ValidationTextBox], {
postCreate: function() {
this.inherited(arguments);
drawSection.call(this)
}
});
});
require([
"dojo/parser",
"my/TextBox",
"dojo/domReady!"
], function(
parser,
TextBox
) {
// important: parse document after the ValidationTextBox was extended
parser.parse();
});
<link href="https://ajax.googleapis.com/ajax/libs/dojo/1.8/dijit/themes/claro/claro.css" rel="stylesheet"/>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>
<body class="claro">
<input type="text" data-dojo-type="my/TextBox" />,
</body>

You need to use dojo/_base/lang lang.hitch like this:
require(["dojo/dom-construct"], lang.hitch(this, function(domConstruct) {
alert(this); // this = window
}));
That's a common closure problem.
See https://dojotoolkit.org/reference-guide/1.10/dojo/_base/lang.html#hitch
As a good practice, I would recommend having the drawSection method inside the widget and the dom-construct required on top (you will always need it as you call it from postCreate so a "on-demand" require is overkill)
define("my/TextBox", [
"dojo/_base/declare",
"dijit/form/ValidationTextBox",
"dojo/dom-construct"
], function(declare, ValidationTextBox, domConstruct) {
return declare([ValidationTextBox], {
postCreate: function() {
this.inherited(arguments);
this.drawSection()
},
drawSection: function() {
alert(this);
//domConstruct.whaever you want
};
});
});

Related

How to use segment.io's analytics.js in a knockout custom bindings

I am using knockout to make a custom binding for analytics.track, but it seems to be having trouble. It seems if the analytics.track is nested in more than 2 functions the track call fails silently. It doesn't hit the callback and it doesn't report in segments debugger. I have provided 2 examples demonstrating the problem here:
Without Closure (works):
function sendTrack(event, props) {
console.log("Enter sendTrack");
analytics.track('Signed Up', {
plan: 'Enterprise'
}, {}, function () {
console.log('track callback logged');
});
}
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor) {
console.log("Init");
var value = ko.unwrap(valueAccessor());
ko.applyBindingsToNode(element, { click: sendTrack });
}
};
ko.applyBindings({});
With Closure (doesn't work):
(function(ko, $, analytics){
'use strict';
function sendTrack(event, props) {
console.log("Enter sendTrack");
analytics.track('Signed Up', {
plan: 'Enterprise'
}, {}, function () {
console.log('track callback logged');
});
}
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor) {
console.log("Init");
var value = ko.unwrap(valueAccessor());
ko.applyBindingsToNode(element, { click: sendTrack });
}
};
ko.applyBindings({});
})(window.ko, window.jQuery, window.analytics);
Edit1: Also note this works with if I move the analytics.track to init:
(function(ko, $, analytics){
'use strict';
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor) {
console.log("Init");
analytics.track('Signed Up', {
plan: 'Enterprise'
}, {}, function () {
console.log('track callback logged');
});
}
};
ko.applyBindings({});
})(window.ko, window.jQuery, window.analytics);
Please advise
This is very likely because of the order things load / are initialized on the window object. Because the iife executes immediately, the analytics variable will be set to whatever window.analytics is the moment the iife is encountered by the browser. In the first case, window.analytics will be resolved when the code is run.
Put differently: the closure captures window.analytics in a scoped analytics variable at the time the iife executes.
Here's a demo showing the problem.
Without closure:
function sendTrack() {
console.log("Tracking...");
analytics.track("stuff");
}
ko.bindingHandlers.segmentTrack = {
init: function(element) {
console.log("Init");
ko.applyBindingsToNode(element, { click: sendTrack });
}
}
ko.applyBindings({ });
// Simulate loading analytics now:
window.analytics = { track: function(txt) { console.log("Tracking " + txt); } };
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div data-bind="segmentTrack: true">CLICK ME</div>
vs with closure:
(function(ko, analytics) {
function sendTrack() {
console.log("Tracking...");
analytics.track("stuff");
}
ko.bindingHandlers.segmentTrack = {
init: function(element) {
console.log("Init");
ko.applyBindingsToNode(element, { click: sendTrack });
}
}
ko.applyBindings({});
})(window.ko, window.analytics); // window.analytics isn't quite okay yet!
// Simulate loading analytics now:
window.analytics = { track: function(txt) { console.log("Tracking " + txt); } };
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div data-bind="segmentTrack: true">CLICK ME</div>
True, in my examples the second scenario throws an error whereas you mention in the question nothing happens, but then again the question doesn't contain an actual repro so it's hard to tell where that difference lies.
So analytics.js asynchronously loads its self in the page. In the mean time it queues all calls to the API with a snub version of the object. Once analytics.js loads it executes all the calls in the queue. Then redefines its self, breaking all refs to the original window.analytics. So any calls that are encountered fast enough to My only work around for this is to make my exposer a function call that returns the current version of the window.analytics.
(function (ko, $, analytics) {
function sendTrack(event, props) {
analytics().track(event, props);
}
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = ko.unwrap(valueAccessor());
ko.applyBindingsToNode(element, { click: function () { sendTrack(value.event, value.options) }});
}
}
})(window.ko, window.jQuery, function () { return window.analytics; });

Custom extended dijit/_TemplatedMixin throws "Invalid template Error"

Here is a simplified example of what I am trying to do:
https://jsfiddle.net/1c5qpke9/2/
require([
'dojo/_base/declare',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
'dojo/domReady!'
], function(declare, _WidgetBase, _TemplatedMixin) {
var _CustomTemplatedMixin = declare([_TemplatedMixin], {
_stringRepl: function() {
console.log('_stringRepl');
this.inherited(arguments);
},
_fillContent: function() {
console.log('buildRendering');
this.inherited(arguments);
}
});
var CustomWidget = declare([_WidgetBase, _CustomTemplatedMixin], {
templateString: '<div class="${baseClass}"><span>Custom Widget "${testName}"</span></div>',
testName: 'TestName'
});
new CustomWidget({}, 'MyWidget');
});
If I execute this, I get the following error:
Error: Invalid template: Custom Widget
"${testName}"
When I use _TemplatedMixin instead of _CustomTemplatedMixin in my CustomWidget Class. It works without errors.
That is because _stringRepl returns a value, since you are subclassing it it should also return a value so return the inheritance value
ex.
_stringRepl: function() {
console.log('_stringRepl');
return this.inherited(arguments);
},

Custom component ExtJS, not work setValue

anybody, please help me with created component for extjs 4.2
Ext.define('mycomponent', {
extend:'Ext.form.field.Display',
alias: 'widget.mycomponent',
initComponent: function() {
this.setValue("some value") // not setup
this.callParent(arguments);
console.log(this)
},
})
i try
Ext.getCmp(this.id).setValue("some")
but html object do not exist, events beforerender e.t.c. not running. how i can set value?
Here's a fully working example, tested with 4.2.1.
Ext.define('Foo', {
extend:'Ext.form.field.Display',
alias: 'widget.mycomponent',
initComponent: function() {
this.setValue("some value") // not setup
this.callParent(arguments);
}
})
Ext.onReady(function() {
new Foo({
renderTo: document.body
})
});
You need to define the getValue() and setValue() methods in your constructor in order to work.
getValue: function () {
var me = this,
value;
return value;
}
setValue: function (value) {
var me = this;
// Example only should't work as it is
me.displayField.setValue(value);
}

Rendering and delete multiple views…

I hope i am clear enough, otherwise ask me for clarifications.
I would like to delete end create view in agreement to a main template…
I did try the following implementation, unsuccessfully.
// main-template.html
<div id=""project>
<div class="main-template">
<div id="view1"></div>
<div class="foldable-block">
<div id="view2"></div>
<div id="view3"></div>
</div>
</div>
</div>
//mainView.js
define([
"js/views/view1",
"js/views/view2",
"js/views/view3",
"text!templates/main-template.html"
], function (View1, View2, View3, mainTemaplte) {
var MainView = Backbone.View.extend({
initialize: function ()
{
this.$el.html(Mustache.render(mainTemaplte));
this.render();
},
el: $("#project"),
render: function ()
{
var options = this.options;
this.view1 = new View1(options);
this.view2 = new View2(options);
this.view3 = new View3(options);
}
});
return MainView;
});
//view1.js
define([… ], function (..) {
var View1 = Backbone.View.extend({
el: $("#view1"),
initialize: function () {
console.log(this.$el) // []
setTimeout( function () {
console.log(this.$el) // []
}, 2000);
}
});
return View1;
});
The issues as you can see from the comments is in view1.js
(this.$el) // []
from my js console it works:
$("#view1") // <div>….</div>
My goals is:
1) when I load the mainView.js module I would like to create a template to which attach my views (view1, view2, view3)
2) when I trigger delete view, every DOM, to which are attached the view, should be deleted.
3) when I call again the mainView.js module the template should be recreated.
if you have other ideas to suggest, please post.
Thanks to #nikoshr advise this.$el, in view1.j, is defined and when I call render in view1.js the this.$el is fill properly
but it is not attached to the body of document.
How can I make it without using append or similar jquery methods to my main-template.html ?
Here my render function:
render: function ()
{
this.$el.html(Mustache.render(myTemplate, this.view);
}
You are attaching your subviews to elements that do not exist at the time you require them. Something like this may be a step in the right direction:
mainView.js
define([
"js/views/view1",
"js/views/view2",
"js/views/view3",
"text!templates/main-template.html"
], function (View1, View2, View3, mainTemaplte) {
var MainView = Backbone.View.extend({
initialize: function ()
{
this.render();
},
render: function ()
{
// render from template and assign this.el to the root of the element
// e.g #project
var html=Mustache.render(mainTemaplte);
this.setElement( $(html) );
// create each view with its el set to the correct descendant
this.view1 = new View1( _.extend( {el:this.$("#view1")} , this.options) );
this.view2 = new View2( _.extend( {el:this.$("#view2")} , this.options) );
this.view3 = new View3( _.extend( {el:this.$("#view3")} , this.options) );
}
});
return MainView;
});
view1.js
define([… ], function (..) {
var View1 = Backbone.View.extend({
initialize: function () {
console.log(this.$el);
}
});
return View1;
});
And you can recreate your view with something like
require(["js/views/mainView"], function(MainView) {
var view=new MainView();
console.log(view.$el);
//attach the view to the DOM
$("body").append(view.$el);
});

requireJS - with ajax requests

index.html
<link href="Styles/bootstrap.css" rel="stylesheet" />
<script data-main="Scripts/app" src="Scripts/require.js"></script>
<a class="btn0">button 0</a>
<div class="target"></div>
app.js
require({
paths : {
jQuery : 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min',
tooltip : 'bootstrap-tooltip'
}
});
require([ 'jQuery' ], function() {
require(['tooltip' ], function() {
$(".btn0").click(function() {
$(".target").load('btn0.html');
});
$('[rel=tooltip]').tooltip();
});
});
btn0.html
Wouldn't it be nice to see a Tooltip here?
In this situation, the tooltip doesn't work. Only works if i cut $('[rel=tooltip]').tooltip(); and paste in btn0.html like:
<script>
$('[rel=tooltip]').tooltip();
</script>
My question is. How organize the javascript code that is needed in btn0.html? It is possible put the JS content of btn0.html in app.js?
You have to call tooltip() function when btn0.html is loaded.
Element with rel="tooltip" does not exist when inicializing that page.
require([ 'jQuery' ], function() {
require(['tooltip' ], function() {
$(".btn0").click(function() {
$(".target").load('btn0.html', function ()
{
$('[rel=tooltip]').tooltip();
})
});
});
});

Categories

Resources