Callbacks with Require JS - javascript

So i'm trying to make two functions that allow a user to move an item from their cart to the "saved cart" and vise versa. These functions depend on the "cart group item" module which also contains the events for the button clicks. My question is, i'm unsure how to correctly call these functions to allow the click event to take place in my current js file. Hopefully someone can help!
Event's in module:
cartGroupItem.prototype.createMoveEvent = function (elem) {
if (undefined !== elem && null !== elem) {
var button = elem.querySelector('.cartGroupItem__move');
if (button !== null) {
button.addEventListener('click', function (e) {
ajax('GET',
'/resources/ajax/cart.aspx?sub=3&Saved=0&Cart_Group_ID='+this.cartGroupId,
true, {}, function () {
this.moveToCartCallback();
}.bind(this), function () {
this.showErrorDiv();
}.bind(this));
}.bind(this));
}
}
};
cartGroupItem.prototype.createSaveEvent = function (elem) {
if (undefined !== elem && null !== elem) {
var button = elem.querySelector('.cartGroupItem__save');
if (button !== null) {
button.addEventListener('click', function (e) {
ajax('GET',
'/resources/ajax/cart.aspx?sub=3&Saved=1&Cart_Group_ID='+this.cartGroupId,
true, {}, this.saveForLaterCallback.bind(this), this.showErrorDiv.bind(this));
}.bind(this));
}
}
};
Move functions:
function moveToSaved(cartGroupId) {
for (var i = 0, l = activeCartList.length; i < l; i++) {
if (activeCartList[i].cartGroupId === cartGroupId) {
activeCartList.remove();
savedCartList.push(activeCartList[i]);
}
}
}
function moveToActive(cartGroupId) {
for (var i = 0, l = savedCartList.length; i < l; i++) {
if (savedCartList[i].cartGroupId === cartGroupId) {
savedCartList.remove();
activeCartList.push(savedCartList[i]);
}
}
}

Your Event module probably define the function cartGroupItem right?
What you need to do is pass this function from its file to your current file, and then "instanciate" a carteGroupItem:
// In your current JS file
var myCartGroupItem = new cartGroupItem();
myCartGroupItem.createMoveEvent();
myCartGroupItem.createSaveEvent();
We also would need to see this function "constructor" (where it is defined) as it probably takes a few callbacks as parameters. Otherwise you can add them manually:
myCartGroupItem.moveToCartCallback = function() {
// do what you want when the item is moved to cart
};
// Same for the other callbacks
myCartGroupItem.saveForLaterCallback = function() { ... };
myCartGroupItem.showErrorDiv = function() { ... };
Finally, the way to pass things with RequireJS is that for instance, your Event module returns cartGroupItem so in your file module:
define(['Event'], function(cartGroupItem) {
// Your code where you can use cartGroupItem
});

Related

Chaining function not work when inside another function

I try to create chaining function using vanilla javascript, its work if just chaining, but if inside other function its stop working.
var doc = document,
M$ = function(el) {
var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
var m = expr.exec(el);
if(m[1]) {
return doc.getElementById(m[1]);
} else if(m[2]) {
return doc.getElementsByTagName(m[2]);
} else if(m[3]) {
return doc.getElementsByClassName(m[3]);
}
},
$ = function (el) {
this.el = M$(el);
// event function
this.event = function(type,fn) {
this.el.addEventListener(type,fn,false);
return this;
}
// forEach function
this.forEach = function(fn,val) {
for(var i = this.el.length - 1; i >= 0; i--) {
fn.call(val, i, this.el[i]);
}
return this;
}
if(this instanceof $) {
return this.$;
} else {
return new $(el);
}
};
//use
$("button").forEach(function(index, el)
// when i use function event, its not work
el.event("click", function() {
alert("hello");
});
// if i'm using addEventListener its work, but i want use event function
});
My question is, how to be event function working inside forEach function?
Thanks for help!
First off, there is an issue with brackets in your code after $("button").forEach(function(index, el) you are missing {;
Then the problem is that when you try to call click-callback on your elements (buttons), in fact, due to the this issues the elements (buttons) don't have event() property. They are not even defined themselves since this.el = M$(el); goes outside forEach(). I tweaked and cleaned a little your code, check it out. I guess now it does what you want:
var doc = document,
M$ = function(el) {
var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
var m = expr.exec(el);
if(m[1]) return doc.getElementById(m[1]); else if(m[2]) return doc.getElementsByTagName(m[2]); else if(m[3]) return doc.getElementsByClassName(m[3]);
},
$ = function(el) {
this.forEach = function(fn,val) {
// assign this.el and this.el[i].event inside forEach(), not outside
this.el = M$(el);
for(var i = this.el.length - 1; i >= 0; i--) {
this.el[i].event = function(type,fn) { this.addEventListener(type,fn,false); };
fn.call(val, i, this.el[i]);
}
}
return this;
};
$("button").forEach(function(index, el) {
el.event("click", function() { alert("hello, " + this.textContent); });
});
<button>btn1</button>
<button>btn2</button>
UPDATE
While the previous solution is fine for the particular purpose of setting click handlers on buttons, I think what you really want is to emulate Jquery and chain function calls. I improved your attempt right in this way:
var doc = document,
M$ = function(el) {
var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
var m = expr.exec(el);
if(m[1]) return doc.getElementById(m[1]);else if(m[2]) return doc.getElementsByTagName(m[2]); else if(m[3]) return doc.getElementsByClassName(m[3]);
},
$ = function (el) { //console.log(this);
this.el = M$(el);
this.event = function(type,fn) {
for(var i = this.el.length - 1; i >= 0; i--) this.el[i].addEventListener(type,fn,false);
}
this.forEach = function(fn) {
fn.call(this);
}
return this;
};
$("button").forEach(function() {
this.event("click", function() {
alert("hello, " + this.textContent);
});
});
<button>btn1</button>
<button>btn2</button>
The key to understanding here is that your this object should always be equal to $ {el: HTMLCollection(2), event: function, forEach: function}. So,
calling $("button") you initially set it to $ {el: HTMLCollection(2), event: function, forEach: function} - with HTML Collection and event&forEach functions;
calling $("button").forEach(fn) you keep forEach's context equal to this from previous step;
calling fn.call(this); inside forEach() you call your callback fn and pass the same this to it;
inside the callback fn you call this.event() - it works because your this is always the one from the first step.
in this.event() which is just $.event() we just traverse our HTMLCollection and set handlers for click event on buttons. Inside $.event() this will be equal to a button element because we call it in such a context on click event, so, this.textContent takes the buttons' content.
Thanks, really good question!
First things first.
1.
this.el = M$(el);
M$ = function(el) {
var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
var m = expr.exec(el);
if(m[1]) {
return doc.getElementById(m[1]);
} else if(m[2]) {
return doc.getElementsByTagName(m[2]);
} else if(m[3]) {
return doc.getElementsByClassName(m[3]);
}
}
As you defined M$ you can either have a HtmlCollection if you get elements by tag name or by class name or just one element if you get element by id.
Then you suppose that your el is one when it can be a collection.
this.event = function(type,fn) {
this.el.addEventListener(type,fn,false);
return this;
}
You probably receive a collection if you try to get all buttons.
2.
If you try to run posted code you will receive an Unexpected identifier error because you missed a { after forEach(function(index, el).
3.
If you put that { in there you will receive a el.event is not a function error because you don't have an event function on el, but you have that on $(el).
4.
If you change your code to:
$("button").forEach(function(index, el)
{
// when i use function event, its not work
$(el).event("click", function() {
alert("hello");
});
// if i'm using addEventListener its work, but i want use event function
});
You'll receive an error because you didn't handled multiple elements. See 1 problem.
Have a look at this.
var doc = document,
M$ = function(el) {
var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
var m = expr.exec(el);
if(m[1]) {
return Array.apply([],[doc.getElementById(m[1])]);
} else if(m[2]) {
return Array.apply([],doc.getElementsByTagName(m[2]));
} else if(m[3]) {
return Array.apply([],doc.getElementsByClassName(m[3]));
}
},
$ = function (el) {
if(! (this instanceof $)) {
return new $(el);
}
this.els = M$(el);
// event function
this.event = function(type,fn) {
this.forEach(function(index, el){
el.addEventListener(type,fn,false);
});
return this;
}
// forEach function
this.forEach = function(fn,val) {
for(var i = this.els.length - 1; i >= 0; i--) {
fn.call(val, i, this.els[i]);
}
return this;
}
return this;
};
//use
$("button").event("click", function() {
alert("hello");
});
Here the M$ function is made to return an array to keep things consistent.
So, the $().event function is changed to iterate through all the elements in this.els.
Hence, you could simply call $("button").event function instead of $("button").forEach function to register event listeners.
Refer: Demo
This one works. But, Is this what you want? I am not sure.

Display dialogs in a loop and act on the accept event

In my Nativescript app I have a loop and want to display a dialog for each item being iterated over. When the dialog displays it contains "Accept" and "Reject" options, both of which when clicked I would like to call a method which I pass the iterated item into. The issue is since the option selection returns a promise I lose the reference to the iterated item. What can I do to get around this? Here's an example of my code.
EDIT: I also really don't like that I'm declaring a function in the loop after the promise returns.
function _showPendingConnections() {
for (var i = 0; i < ViewModel.pendingConnections.length; i++) {
var pendingConnection = ViewModel.pendingConnections[i];
dialog.confirm({
message: pendingConnection.PatientFirstName + " would like to share their glucose readings with you.",
okButtonText:"Accept",
cancelButtonText:"Reject"
}).then(function(result) {
if(result === true) {
ViewModel.acceptConnection(pendingConnection);
} else {
ViewModel.removeConnection(pendingConnection);
}
});
}
}
The following change worked for me (I have probably created different viewModel but however the idea is the same) - all I have done is to change when your item index is passed.
For example:
// main-page.js
"use strict";
var main_view_model_1 = require("./main-view-model");
var dialogModule = require("ui/dialogs");
var viewModel = new main_view_model_1.MyViewModel();
viewModel.pendingConnections = [{ PatientFirstName: "John" }, { PatientFirstName: "Merry" }, { PatientFirstName: "Abygeil" }];
// Event handler for Page "navigatingTo" event attached in main-page.xml
function navigatingTo(args) {
// Get the event sender
var page = args.object;
page.bindingContext = viewModel;
for (var index = viewModel.pendingConnections.length - 1; index >= 0; index--) {
connectionDealer(index);
}
}
exports.navigatingTo = navigatingTo;
function connectionDealer(index) {
var pendingConnection = viewModel.pendingConnections[index];
dialogModule.confirm({
message: pendingConnection["PatientFirstName"] + " would like to share their glucose readings with you.",
okButtonText: "Accept",
cancelButtonText: "Reject"
}).then(function (result) {
if (result === true) {
// your code follow.. pass pendingConnection[index] to your method
console.log("accepted by " + pendingConnection["PatientFirstName"]);
}
else {
// your code follow.. pass pendingConnection[index] to your method
console.log("Rejected by " + pendingConnection["PatientFirstName"]);
}
});
}
// main-view-model.js
"use strict";
var observable = require("data/observable");
var MyViewModel = (function (_super) {
__extends(MyViewModel, _super);
function MyViewModel() {
_super.apply(this, arguments);
}
Object.defineProperty(MyViewModel.prototype, "pendingConnections", {
get: function () {
return this._pendingConnections;
},
set: function (value) {
if (this._pendingConnections !== value) {
this._pendingConnections = value;
}
},
enumerable: true,
configurable: true
});
return MyViewModel;
}(observable.Observable));
exports.MyViewModel = MyViewModel;

how to run a function with parameters

I have a function that simply validates forms (for old browsers). The function works just fine except that I have to pass the parameters every time I call this function, where in fact I already specified the default parameters in 'config'.
So by logic, If I called the function as: validateMe(); it should run as validateMe({requiredClass: '.required', verifiedClass: 'invalid'});
but unfortunately calling the function without parameters doesn't work correctly ( in my case the form triggers the submission event) (it doesn't reach return false).
so what is missing in the code to run the function with the default settings??
function validateMe(vform, settings) {
var vform, //form name or id
config = {
'requiredClass': '.required',
'verifiedClass': 'invalid'
};
if (settings) {
$.extend(config, settings);
}
$(vform).on('submit', function(){
var inputs = $(this).find(config.requiredClass),
required = [];
for (i=0; i < inputs.length; i++) {
if (inputs[i] != null) {
if ($(inputs[i]).val().replace(/^\s+|\s+$/g, '') == '') {
required.push($(inputs[i]).index());
}
}
}
if (required.length > 0) {
$(this).find('input').removeClass(config.verifiedClass);
for(n=0;n<required.length;n++) {
$(inputs[n]).addClass(config.verifiedClass);
}
return false;
}
});
}
Any help?
Thanks.
function validateMe(vform, settings) {
this.vform = vform || 'default',
this.setting = 'whatever',
this.private = ''
}
var newInstance = new validateMe();
now you have an instance of it, so you can define it as you go.

Why is this element null?

Clarification:
I'm not interested in using jQuery methods.
Summary
I use a method to only run my modules after the html has finished loading. It looks like this. page_complete is the id of the last element on the page.
$A.finish('page_complete', function () {
// page has finished loading
});
Finish is implemented like this. It is just a timer that checks of the existence of the last element. Once it finds it, it initializes all the modules.
I don't understand why the element is NULL as FF is telling me.
/*finish
** Once an element has been loaded an HTML focus event is fired
** on that ID. It can not be cancelled and bubbles up
**
*/
$A.finish = function (id, callback) {
var htm_finished = document.getElementById(id);
if (htm_finished) {
$A.initAll(); // intilAll will fire the init function of all modules.
$A.makeEvent('focus', htm_finished);
callback();
} else {
setTimeout(function () {
$A.finish(id, callback);
}, 10);
}
};
Error in Firefox
...TypeError: this.E.ready is null # ...
Note I put a comment where the error is.
Module with error
/*MUserAny
**
**
**
*/
$A.module({
Name: 'MUserAny',
S: {
DynSma: SDynSma,
DynTwe: SDynTwe,
DynArc: SDynArc,
AniFlipMediaPane: SAniFlipMediaPane,
AniFlipPage: SAniFlipPage,
ClientStorage: SClientStorage
},
E: {
ready: $A('#page_complete')[0]
},
init: function () {
var pipe = {},
this_hold = this;
this.E.ready.addEventListener("focus", function (event) { // error is here.
pipe = $A.definePipe(this_hold.Name);
$A.machine(pipe);
}, false);
},
pre: function (pipe) {
var h_token = this.S.ClientStorage.get('h_token');
if ((h_token === '0') || (h_token === 'undefined') || (h_token === null)
|| (h_token === undefined)) {
this.S.AniFlipPage.run('sp');
pipe.state = false;
} else {
pipe.server.smalls.h_token = h_token;
pipe.state = true;
}
this.S.AniFlipMediaPane.run('mi_cover');
return pipe;
},
post: function (pipe) {
this.S.DynSma.run(pipe.server.smalls);
this.S.DynArc.run(pipe.server.arcmarks);
this.S.DynTwe.run(pipe.server.tweets);
this.S.AniFlipPage.run(this.S.ClientStorage.get('page'));
return pipe;
},
finish: function (pipe) {
}
});
It looks like
E: {
ready: $A('#page_complete')[0]
}
is being evaluated as part of the object literal, and if this is occuring before the page is complete you get your error.
One quick and dirty solution may be to change E.ready to a function, which will only be called during init, which you know happens after page complete, something like
E: {
ready: function() { return $A('#page_complete')[0]; }
},
init: function () {
var pipe = {},
this_hold = this;
this.E.ready().addEventListener("focus", function (event) { ...

do you know any javascript api with many premise handling? like in artificial intelligence

JQuery can handle events implementing Observer Design Pattern, but it can handle only one event for one callback function
I was wondering, if it could handle many events: when all this events was triggered, or when all boolean premises became true then call a function
It would be much better to develop dynamic applications
Do you know if already exist something like that?
If not: do you think it would be nice if i develop?
EDIT:
i would like to do something like this:
when((a==b && c!=0), function(){
//do something when a==b and c!=0
alert("a==b && c!=0");
});
EDIT 2:
I've found a great API that allow listen for variable changes. You just have to do something like this:
obj.watch(function(){
alert("obj changes");
});
http://watch.k6.com.br/
Sounds like Promises to me. Most of the libraries developed with that concept (there are many for JavaScript, including jQuery.deferred) can simply build a new Promise for the event that some others got fulfilled.
You can easily do this manually:
var flag1 = false;
var flag2 = false;
var flag3 = false;
var checkAllFlags = function () {
if (!flag1 || !flag2 || !flag3) return;
checkAllFlags = function () { };
allFlagsSet();
}
var allFlagsSet = function () {
// Execute here when all flags are set
}
// When you set a flag do it like this:
flag2 = true;
checkAllFlags();
Or you could use this class:
window.flagger = function (count, callback, once) {
var curr = this;
curr.callback = callback;
if (once)
curr.called = false;
curr.flags = [];
curr.flagsLeft = count;
for (var i = 0; i < count; i++)
curr.flags[i] = false;
this.flag = function (index) {
if (!curr.flags[index]) {
curr.flags[index] = true;
if (--curr.flagsLeft <= 0 && (!once || !curr.called)) {
if (once) curr.called = true;
curr.callback();
}
}
};
this.unflag = function (index) {
if (curr.flags[index]) {
curr.flags[index] = false;
curr.flagsLeft++;
}
};
this.reset = function (force) {
for (var i = 0; i < count; i++)
curr.flags[i] = false;
curr.flagsLeft = count;
if (once && force)
curr.called = false;
};
this.isFlagged = function (index) {
return curr.flags[index];
};
}
And use it like this:
var myFlagger = new flagger(
/* The amount of flags that need to be set: */8,
/* The function that needs to be called when the flags are all set: */
function () { alert('Callback function called'); },
/* If the callback function should be called again when a flag is unflagged and reflagged.
Optional. Default: false */false);
// You can now use these functions:
myFlagger.flag(0); // Use this to set a flag, index ranges from 0 to count - 1.
myFlagger.unflag(0); // Unflags an index.
myFlagger.reset(); // Resets all flags to false.
myFlagger.reset(true); // Using this forces the reset, returning to full start
// state, causes callback function to be called again
// even if 'once' is specified.
alert('0 is flagged: ' + myFlagger.isFlagged(1)); // Returns if flag is flagged.
I hope this somewhat helps you.

Categories