Custom jQuery function - selector doesn't find element - javascript

I'm playing around with making a REST api and I'm working on some javascript functions.
The idea here is to run for example: $('#main').get('car/ford'); and the data returned will be added in the element provided.
Here is all the javascript:
$.fn.extend({
get: function (path) {
request(this, 'GET', path);
}
});
function request(element, type, path) {
var dees = $(element);
$.ajax({
type: type,
url: '/request/'+path,
success: function(data) {
console.log('Success');
a = $(element);
b = $('#fileList'); // this is a control
dees.html(data);
}
});
}
(function() {
console.log('running');
$('#fileList').get('car/ford');
})();
The problem I'm having is that when I run a.html(data);
Nothing will change. But if i run b.html(data);
Everything works like it should.
So there is a difference between those two selectors.
On a the element is not found a.length == 0
and on b the element is found b.length == 1
Why isn't the element found by the selector and how can I fix it?

The problem was solved by adding $ in front of the calling function.
From:
(function() {
console.log('running');
$('#fileList').get('car/ford');
})();
To:
$(function() {
console.log('running');
$('#fileList').get('car/ford');
});

You could try the following:
function request(element, type, path) {
var dees = $(element);
$.ajax({
type: type,
url: '/request/'+path,
success: function(data) {
console.log('Success');
dees.html(data);
}
});
}
in case the $(this) variable is conflicting with own $(this) variable of ajax() block.

Change element to $(element)
When request is call request(this, 'GET', path); this represents javascript object and it should be jQuery object. You need to pass jquery object or convert it to jquery object after being pass as I did.
$.fn.extend({
get: function (path) {
alert(this.tagName);
var objToPass = $(this);
request(objToPass, 'GET', path);
}
});
function request(javascriptObj, type, path) {
element = $(javascriptObj);
$.ajax({
type: type,
url: '/request/'+path,
success: function(data) {
console.log('Success');
a = $(element);
b = $('#fileList'); // this is a control
a.html(data);
}
});
}
Update
The call to get function should be instantiated on document.ready which could be done by simply adding $
Change
(function() {
console.log('running');
$('#fileList').get('car/ford');
})();
To
$(function() {
console.log('running');
$('#fileList').get('car/ford');
})();

Related

Method undefined when calling it from $ajax.success

When I call a method from the $ajax.success callback I get an undefined.
var someObj = {};
someObj.someMethod = function() {
//code code
}
someObj.ajaxCall = function() {
$.ajax({
//ajax options
})
.done(function( data ) {
this.someMethod();
});
}
As our good friend, Mr. SLaks has pointed out, you have a scope issue with regards to this
One solution other than the one posted could be saving a reference to the scope before the callback :
someObj.ajaxCall = function() {
var _this = this;
$.ajax({
//ajax options
})
.done(function( data ) {
_this.someMethod();
});
}
Or, you can use the context option with $.ajax() to control the setting of the this value:
someObj.ajaxCall = function() {
$.ajax({
context: this,
// other ajax options
})
.done(function( data ) {
this.someMethod();
});
}
You should use the call method of the function object:
someObj.ajaxCall = function() {
$.ajax({
//ajax options
})
.done(function( data ) {
someMethod.call(someObj);
});
}
Inside the success callback the this object is pointing the $ajax object which haven't a someMethod function defined.

How to create a object/function with defined setting when create new?

I wish to do something like this:
function Student (id, class) {
var id = id
var class = class
this.get = function (subject) {
$.ajax({
url: 'myurl',
data: { id: id, class: class, subject: subject },
success: function (r) { return r }
})
}
this.set = function (subject, mark) {
$.ajax({
url: 'myurl',
method: 'post',
data: { id: id, class: class, subject: subject, mark: mark },
success: function (r) { return r }
})
}
}
my question is how can I modify my function so that I can create new student as below
var s1 = new Student (22, 4) // to set predefined id & class
but, I want the set and get as below (like jquery set & get)
s1("math") // to get
s1("history", 70) // to set
**
so i think the answer is not possible to work as an object to store attribute id & class and call like a function without function name. thanks for your answer guys.
**
You can check how many arguments the caller has provided. Or check for undefined values.
function test(a, b) {
// both ifs check b was not provided
if (typeof b === "undefined") {
}
if (arguments.length == 1) {
}
}
Your current functions probably won't work because you are returning from a callback. AJAX is (in most cases) asynchronous. So in your case, you have to add another argument for providing a callback.
this.get = function (subject, callback) {
$.ajax({
url: 'myurl',
data: { id: id, class: class, subject: subject },
success: function (r) { callback(r); }
})
}
FYI, class is a reserved keyword by the ECMAScript specification.
function sample(x,y){
id=x;
subjectClass =y;
if(arguments.length == 1){
//getter
}
else{
//setter
}
}
call getter
sample("maths")
call setter
sample("history",70);
Note :
class is a reserved keyword, so please remove it and you can use some other variable name
but, I want the set and get as below
That would mean s1 would be a function, not a Student instance. So your constructor would need to return that.
function student(id, klass) {
// no need to declare variables here that are parameters already
return function(subject, mark) {
var data = {id: id, class: klass, subject: subject},
opt = {url: 'myurl', data: data};
if (arguments.length > 1) { // something was given to `mark`
data.mark = mark;
opt.method = "post";
}
return $.ajax(opt);
};
}
Btw, since you cannot return the response from an ajax call, the function will return the jqXHR promise:
var s1 = student(22, 4); // `new` is unnecessary now
s1("math").then(function(r) { console.log("got maths result:", r); });
s1("history", 70).then(function(r) { console.log("successfully set marks"); });

Scoping of 'this' in TypeScript

I have a very simple class, but already run into pain with the definition of ‘this’ in Typescript:
Typescript
/// <reference path='jquery.d.ts' />
/// <reference path='bootstrap.d.ts' />
module Problem {
export class Index {
detailsUrl: string;
constructor() {
$('.problem-detail-button').click((e) => {
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: this.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
Javascript
var Problem;
(function (Problem) {
var Index = (function () {
function Index() {
var _this = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(_this).closest('tr');
var problemId = $row.data('problem-id');
$.ajax({
url: _this.detailsUrl,
data: {
id: problemId
},
type: 'POST',
success: function (result) {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
}
});
});
}
return Index;
})();
Problem.Index = Index;
})(Problem || (Problem = {}));
Now the problem is that the line
var $row = $(this).closest('tr'); //this must be that of the callback
and this line
this.detailsUrl, //this must be the instance of the class
conflict in the meaning of 'this'
How do you handle the mixture of the 'this'?
module Problem {
export class Index {
detailsUrl: string;
constructor() {
var that = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: that.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
Explicitly declare that = this so you have a reference for that.detailsUrl, then
don't use a fat arrow for the click handler, so you get the correct this scope for the callback.
You need to fallback to the standard way of javascript. i.e store the variable as :
var self = this;
Then you can use function instead of ()=> and use this to access variable in callback and self to access the instance of the class.
Here is the complete code sample:
module Problem {
export class Index {
detailsUrl: string;
constructor() {
var self = this;
$('.problem-detail-button').click(function(e){
e.preventDefault();
var $row = $(this).closest('tr'); //this must be that of the callback
var problemId: number = $row.data('problem-id');
$.ajax({
url: self.detailsUrl, //this must be the instance of the class
data: { id: problemId },
type: 'POST',
success: (result) => {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
},
})
});
}
}
}
// Creating
var foo:any = {};
foo.x = 3;
foo.y='123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
// Reading
interface Bar{
x:number;
y?:string;
}
var baz:Bar = JSON.parse(jsonString);
alert(baz.y);
And your generated javascript:
var Problem;
(function (Problem) {
var Index = (function () {
function Index() {
var self = this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var $row = $(this).closest('tr');
var problemId = $row.data('problem-id');
$.ajax({
url: self.detailsUrl,
data: {
id: problemId
},
type: 'POST',
success: function (result) {
$('#details-modal-placeholder').html(result);
$('#details-modal-placeholder modal').modal('show');
}
});
});
}
return Index;
})();
Problem.Index = Index;
})(Problem || (Problem = {}));
var foo = {
};
foo.x = 3;
foo.y = '123';
var jsonString = JSON.stringify(foo);
alert(jsonString);
var baz = JSON.parse(jsonString);
alert(baz.y);
If you're only supporting browsers that have .addEventListener, I'd suggest using that to associate your data with your elements.
Instead of implementing your code, I'll just give a simple example.
function MyClass(el) {
this.el = el;
this.foo = "bar";
el.addEventListener("click", this, false);
}
MyClass.prototype.handleEvent = function(event) {
this[event.type] && this[event.type](event);
};
MyClass.prototype.click = function(event) {
// Here you have access to the data object
console.log(this.foo); // "bar"
// ...and therefore the element that you stored
console.log(this.el.nodeName); // "DIV"
// ...or you could use `event.currentElement` to get the bound element
};
So this technique gives you an organized coupling between elements and data.
Even if you need to support old IE, you can shim it using .attachEvent().
So then to use it, you just pass the element to the constructor when setting up the data.
new MyClass(document.body);
If all the logic is in your handler(s), you don't even need to keep a reference to the object you created, since the handlers automatically get it via this.
I normally bind this to a variable as soon as I have it in the scope I want.
However the this you are after could be found like this:
constructor() {
var class_this=this;
$('.problem-detail-button').click(function (e) {
e.preventDefault();
var callback_this=e.target;
Late to the thread, but I have something different to suggestion.
Instead of:
var $row = $(this).closest('tr'); //this must be that of the callback
Consider using:
var $row = $(e.currentTarget).closest('tr');
As in this example, anywhere you might want to use this in a jQuery callback, you have access to a function parameter you can use instead. I would suggest that using these parameters instead of this is cleaner (where "cleaner" is defined as more expressive and less likely to be turned into a bug during future maintenance).
module Problem {
export class Index {
constructor() {
$('.classname').on('click',$.proxy(this.yourfunction,this));
}
private yourfunction(event){
console.log(this);//now this is not dom element but Index
}
}
}
check about jquery.proxy().
just remind you there is another way.

pass this local js variable as an argument to a new function

I've read several posts on passing local function variables to new functions in javascript but am still having trouble in my own particular case.
I'm trying to pass the term argument given to data: function (term, page) to the generateUrl function below it. this.term = term and window.term = term (which I know is bad practice) aren't working. Should I try to declare a term variable outside of $(document).ready(function) or outside of the two inner functions, or or should I move the generateUrl definition inside of the $("#example").select2 function?
$(document).ready(function(){
$("#example").select2({
ajax: {
url: generateUrl(),
data: function (term, page) {
this.term = term; // i want to pass this local variable to generateUrl
}
}
});
function generateUrl(term) {
(function ($) {
var args = 'keywords=' + term;
return args;
}
(jQuery));
}
});
$(document).ready(function(){
$("#example").select2({
ajax: {
url: generateUrl(), /* You are calling generateUrl without parameter,
this will cause error
Did you actualy mean generateUrl(term) ? */
data: function (term, page) {
this.term = term; // i want to pass this local variable to generateUrl
generateUrl( this.term ); /* Is this what you want to do? */
}
}
});
function generateUrl(term) {
(function ($) {
var args = 'keywords=' + term;
return args;
}
(jQuery));
}
});
You should take a look into the select2 documentation, the example on section "Loading Remote Data" seems to be exactly what you're looking for. Based on that, I believe your code should be:
$(document).ready(function(){
$("#example").select2({
ajax: {
url: "", // replace that empty string with your ajax base url
// (without any parameters)
data: function (term, page) {
return { keywords : term };
}
}
});
});

Understanding closure and scope

For some reason (probably because i don't understand closures) function inResult always returns false and the loop is never executed. Of course i'm sure that result contains has the right properties.
function hasId() {return $(this).prop('id');}
function inResult(res) { return res.hasOwnProperty($(this).prop('id'));}
$.ajax({
url : opt.url,
data : $.extend(true, opt.data, {ids: ids}),
context : this, // A collection of elements
type : 'POST',
dataType : 'json',
success : function(result) {
// Filter elements with id and with a property in result named "id"
this.filter(hasId).filter(inResult(result)).each(function() {
console.log($(this).prop('id'));
});
}
});
EDIT: working code solution (thanks to Šime Vidas for poiting me in the right direction):
// Use closures to change the context later
var hasId = function() { return $(this).prop('id'); };
var inResult = function(res) { return res.hasOwnProperty($(this).prop('id')); };
$.ajax({
url : opt.url,
data : $.extend(true, opt.data, {ids: ids}),
context : this, // A collection of elements
type : 'POST',
dataType : 'json',
success : function(result) {
// Filter elements with id and with a property in result named "id"
var filtered = this.filter(function() {
// Note the context switch and result parameter passing
return hasId.call(this) && isBinded.call(this, result);
});
filtered.each(function() { console.log($(this).prop('id')); });
}
});
Try this:
this.filter( hasId ).filter( function () {
return inResult( result );
}).each( function () {
console.log( this.id );
});
In your code you have .filter(inResult(result)) which won't work because you're invoking inResult immediately and passing the result of that invocation (which is a Boolean value) to filter(), which doesn't work with Boolean values.
You could also do it like so:
var keys = Object.keys( result );
var filtered = this.filter( function () {
return this.id && keys.indexOf( this.id ) > -1;
});
Object.keys( result ) returns an array of all own property names from result.

Categories