Ionic 2: Unable to navigate from inner function - javascript

I'm trying to navigate to a page , upon clicking on a google map marker. I'm able to do it outside the inner function (Inside initializeMap function only), just having problems doing it in a inner function.
This is my constructor:
static get parameters() {
return [[NavController],[Platform]];
}
constructor(navController, platform, app) {
this.navController = navController;
this.platform = platform;
this.app = app;
this.map = null;
this.markers = [];
platform.ready().then(() => {
this.initializeMap();
});
}
Inside my initializeMap method contains the populateLocks method:
function populateLocks(auth,unauth,map){
for (var k in auth) {
(function (id) {
var auth = new google.maps.Marker({
icon: authImage,
map: map,
position: authLocks[id].latLng
});
google.maps.event.addListener(auth, 'click', function () {
//alert(auth[id].id);
this.navController.push(SettingsPage);
});
})
(k)
}
Using this.navController or navController throws me error like:
for this.navController.push
for navController.push

Either use () => instead of function ()
More information: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Arrow_functions
or use var self = this; outside the function and refer to self.navController instead of this.navController like so:
var self = this;
google.maps.event.addListener(auth, 'click', function () {
//alert(auth[id].id);
self.navController.push(SettingsPage);
});

Related

How to use a variable inside itself

I want to use the variable inside itself and I see other people do it but why does it not work for me?
This is my ES6 file
// Setup module
// ------------------------------
var FullCalendarAdmin = function () {
//
// Setup module components
//
var _componentRender = function () {
// Basic calendar
var _componentFullCalendarAdmin = function (events) {
// Define element
var calendarAgendaViewElement = document.querySelector('.fullcalendar-agenda-admin');
// Initialize
if (calendarAgendaViewElement) {
var calendarAgendaViewInit = new FullCalendar.Calendar(calendarAgendaViewElement, {
plugins: ['dayGrid', 'timeGrid', 'interaction'],
select: function (start, end) {
var title = prompt("Add event:");
var data;
if (title != '') {
data = {
title: title,
start: start,
end: end
};
calendarAgendaViewInit.addEvent(data);
}
}).render();
}
};
//
// Return objects assigned to module
//
return {
init: function () {
_componentRender();
}
}
}();
// Initialize module
// ------------------------------
document.addEventListener('DOMContentLoaded', function () {
FullCalendarAdmin.init();
});
How can I use the calendarAgendaViewInit to call the addEvent function without getting function as an undefined error?
Thanks in advance!
The problem is that you invoke .render immediately.
So your calendarAgendaViewInit is not an instance of FullCalendar.Calendar but the result of the render method.
What you can do is first define the calendarAgendaViewInit variable
var calendarAgendaViewInit = new FullCalendar.Calendar(calendarAgendaViewElement, {
plugins: ['dayGrid', 'timeGrid', 'interaction'],
select: function (start, end) {
var title = prompt("Add event:");
var data;
if (title != '') {
data = {
title: title,
start: start,
end: end
};
calendarAgendaViewInit.addEvent(data);
}
});
and then call calendarAgendaViewInit.render().
This is sort of an expanded explanation to the comment above. It looks like calendarAgendaViewElement is simply a DOM element that you've found and assigned to a variable. The problem here is that you can only call methods on class instantiations that are now objects with methods inside. If you had seen others call addEvent like that, then they were likely calling it on an instantiation of a class meaning that addEvent had been previously declared as part of that class and they are simply calling that method.
See the example below,
If I declare a class as follows:
class Sample {
sayHello(){
console.log('hello')
}
}
Then instantiate a new object of the 'Sample' class:
var sampleClass = new Sample()
Then I can call 'sayHello' by referring to the method inside the object
sampleClass.sayHello() // hello
Hope that helps

not able to get angular 2 scope inside call back functions

I need to update a array object inside a call back function ,i used the following lines but the values are set in the scope of call back loop not as angular variable so my view is not updated.(deviceval) value is changed if i print it inside the callback but outside the value is still the old one.
export class DashboardComponent implements OnInit {
hideTable: boolean = true;
public deviceVal:any;
constructor(private ref: ChangeDetectorRef) {}
ngOnInit() {
this.deviceVal = deviceData;
console.log(this.deviceVal);
var container = $('.map-canvas');
var options = {
center: new google.maps.LatLng(41.676258, -99.683199),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
gmap = new google.maps.Map(container[0], options);
this.drawChart(deviceData);
this.plotMarkers();
}
plotMarkers(){
$.each(deviceData, function(key, val) {
var controller=this;
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseInt(val.lat), parseInt(val.lon)),
map: gmap,
});
google.maps.event.addListener(marker, 'click', function() {
this.deviceVal = val;
});
markerCache.push(marker);
})
}
}
The problem is here:
$.each(deviceData, function(key, val) {
var controller=this;
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseInt(val.lat), parseInt(val.lon)),
map: gmap,
});
google.maps.event.addListener(marker, 'click', function() {
this.deviceVal = val;
});
markerCache.push(marker);
})
when you use function() as a callback function, the 'this' value is changed. You better read here about this.
You can fix this using arrow functions:
plotMarkers(){
$.each(deviceData, (key, val) => {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseInt(val.lat), parseInt(val.lon)),
map: gmap,
});
google.maps.event.addListener(marker, 'click', () => {
this.deviceVal = val;
});
})
}
But you have a lot of other problems, like: you don't need to use jQuery (to be honest, you should avoid jQuery in an ng2 app), the 'gmap' variable is not defined (you can set it as an property of the class, as you have done with 'deviceVal' for example), 'markerCache' was not defined too, there is no drawChart method, 'deviceData' is not defined inside plotMarkers().
I solved it by declaring a global variable before export component like
var controller;
and initialized it in ngoninit(),
controller = this;
and passed the controller to addlistener,
google.maps.event.addListener(marker, 'click', () => {
controller.deviceVal=[];
controller.deviceVal.push(val);
//console.log(controller.deviceVal+"end....................................")
});

Better way to build javascript modules?

Can I get a little advice on my js modules? I'm good with js, but not quite guru status :) Am I refactoring my modules right?
I've been using the js module pattern like this (rough example, I'm just worried about the structure):
sloppy way?
/* Module Code */
var MapModule = (function ($) {
var $_address;
var $_mapContainer;
function loadApi() {
// do something. maybe load an API?
}
function someInternalMethod() {
// do other things
}
var pub = {};
pub.setAddress = function (address) {
$_address = address;
};
pub.getAddress = function () {
return $_address;
};
pub.initialize = function () {
loadApi();
}
})(jQuery);
// usage
MapModule.initialize();
But that usage seems a little sloppy. I like constructors.
I refactored some modules like this:
Better way?
(function ($) {
this.MapModule = function () {
var $_address;
var $_mapSelector;
var $_mapContainer;
function loadApi() {
// do something. maybe load an API?
}
function someInternalMethod() {
$_mapContainer = $($_mapSelector);
// do stuff with the jQ object.
}
var pub = {};
pub.setAddress = function (address) {
$_address = address;
};
pub.getAddress = function () {
return $_address;
};
pub.initialize = function (selector) {
$_mapSelector = selector;
loadApi();
}
}
})(jQuery);
var map = new MapModule();
map.initialize('#mapcontainer');
That usage seems a lot cleaner to me, and it works just fine, but am I going about it properly?
Taking it another step
Say this module does some stuff with a div that wraps Google Maps and jQuery functionality: Any tips on turning that into a jQ plugin so I can use it with a signature like var map = $('mapcontainer').mapModule();
Thanks!
I have modified your snippet and have actually implemented javascript revealing module pattern which gives the opportunity to implement public & private functions using closure.
Hope this will be helpful:
/* Module Code */
var MapModule = (function (module, $, global) {
var $_address;
var $_mapContainer;
// Public functions
function _loadApi() {
// Do something, maybe load an API?
}
function _someInternalMethod() {
// Do other things.
}
function _initialize = function () {
_loadApi();
}
// Private functions
function _setAddress = function (address) {
$_address = address;
};
function _getAddress = function () {
return $_address;
};
$.extend(module, {
loadApi: _loadApi,
someInternalMethod: _someInternalMethod,
initialize: _initialize
});
return module;
})(MapModule || {},this.jQuery, this);
// Usage
MapModule.initialize();
JSFiddle
Just came across this and thought I'd share my approach...
///////////////////////////////
// Module Code
///////////////////////////////
var ExampleModule = (function()
{
////////////////////////////
// Private Properties
////////////////////////////
var whatever = {
data: 'somedata';
};
////////////////////////////
// Private functions
////////////////////////////
function _init()
{
_loadApi();
_bindToUIEvents();
}
function _loadApi()
{
// load an api
}
function _bindToUIEvents()
{
$('#something').on('click', function(){
// Do something cool
});
}
function _getWhatever()
{
return whatever;
}
//////////////////////
// Public API
//////////////////////
return{
init: _init(),
getWhatever: function()
{
return _getWhatever();
}
};
})();
// Usage
ExampleModule.init;

jQuery off not working as I expected

Resolved!! see the end of the question for the result that I used
I am trying to write a function that can handle my apps paging by routes.
I have a function route() that is called with argument being the route(page) to move to.
route is an object that defines a model that it uses that handles its logic.
This model contains 3 functions
indexAction
- This renders my view and appends it to my page.
bindEvents
- This is where I have placed all of my click events
shutDown
- This is instructions to run when moving to a new page
The router function first runs shutdown on the current page, here I have the $(selector).off() and $(selector).remove()
it then runs the enidexAction and bindEvents function.
My issue now is when I return to this page, all my click functions are running twice, then three times etc... its as if the off() never actually unbind from the anchor.
here is an example of one of my models
var NewPageModel = (function() {
var instance;
var modal = 'null';
function createInstance() {
var object = {
indexAction: indexAction,
shutDown: shutDown,
bindEvents: bindEvents
};
return object;
}
function indexAction (data, callback){
var partials = {};
ViewManager.render('pageName',{context:data}, partials,function(html){
ViewManager.appendUnique('#xxx',html,'uniqueID');
callback();
});
}
/**
* Remove modal
*/
function shutDown(){
this.modal.off();
this.modal.remove();
}
function bindEvents() {
if(this.modal!='null'){
return;
}
this.modal = $(PagerManager.pages.newGroup.id);
this.modal.on('click','div.close', function () {
shutDown();
});
this.modal.on('click', 'button.cancel', function () {
shutDown();
});
this.modal.on('click', 'button.submit', function () {
//code that submits form information
});
}
return {
getInstance: function () {
if (!this.instance) {
this.instance = createInstance();
}
return this.instance;
}
};
})();
EDIT!!
So I am still learning about the importance of scopes and how they can be applied to functions
Here is the working code
var NewPageModel = (function() {
var instance;
var modal;
function createInstance() {
var object = {
indexAction: indexAction,
shutDown: shutDown,
bindEvents: bindEvents
};
return object;
}
function indexAction (data, callback){
var partials = {};
ViewManager.render('pageName',{context:data}, partials,function(html){
ViewManager.appendUnique('#xxx',html,'uniqueID');
callback();
});
}
/**
* Remove modal
*/
function shutDown(){
this.modal.off();
this.modal.remove();
this.modal = null;
}
function bindEvents() {
//This is confused logic, if I use off() in shutdown, I don't need to do this as I need to bind all the events again. hence in shutdown modal=null;
if(!this.modal){
return;
}
this.modal = $('#modal');
this.modal.on('click','div.close', function () {
shutDown().apply(this);
}).bind(this);;
this.modal.on('click', 'button.cancel', function () {
shutDown().apply(this);
}).bind(this);;
this.modal.on('click', 'button.submit', function () {
//here I only use the apply(this) if I use another internal function
//code that submits form information
}).bind(this);;
}
return {
getInstance: function () {
if (!this.instance) {
this.instance = createInstance();
}
return this.instance;
}
};
})();
You are losing your this in the event handler functions (this will be the element clicked) so the shutDown is not getting the correct this:
this.modal.on('click','div.close', function () {
shutDown();
});
should be:
var self = this;
this.modal.on('click', 'button.cancel', function () {
self.shutDown();
});
e.g.
function bindEvents() {
var self = this;
if(this.modal!='null'){ /// <<<< !!!!!! WTF
return;
}
this.modal = $(PagerManager.pages.newGroup.id);
this.modal.on('click','div.close', function () {
self.shutDown();
});
this.modal.on('click', 'button.cancel', function () {
self.shutDown();
});
this.modal.on('click', 'button.submit', function () {
//code that submits form information
});
}
Note: I am ignoring the string comparison to null for now as I have no clue what you are doing there :)
As pointed out in comment by #Gurami Dagundaridze you can also retain the correct this using bind (I think the syntax goes like this):
this.modal.on('click', 'button.cancel', shutDown.bind(this));
In the spirit of keeping your syntax and just fixing the bug,
if(this.modal!='null'){ should be if(modal!='null'){
Because this.modal will be undefined at that condition and will just return.
In the spirit of fixing your code, you need to keep a reference to this or it will default to window in the browser.
var modal;
function createInstance() {
var object = {
modal : modal,
shutDown: shutDown,
bindEvents: bindEvents
};
return object;
}
function bindEvents() {
if(this.modal){
return;
}
// ..... //
this.modal.on('click','div.close', function () {
shutDown.apply(this);
}.bind(this));
// ..... //
}
Working demo :
http://jsfiddle.net/uyovgdj3/

javascript object composition syntax

In the following code, I want to be able to call bindClickEvents() like so:
App.Utils.Modal.bindClickEvents();
However, I don't understand the syntax necessary to do this.
Current code:
var App = new Object;
App.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Modal.bindClickEvents();
});
You can do it in one go:
var App = {
Modal : {
bindClickEvents : function () {/* ... */}
}
}
or if you want to break that up to separate steps:
var App = {};
App.Modal = {};
Modal.bindClickEvents = function () {/* ... */};
BTW, in reference to your original question title, this is not object chaining. This is object composition. Object chaining is being able to call methods in an object multiple times in a single statement.
Is this what you're trying to do?
var App = {};
App.Utils = {};
App.Utils.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Utils.Modal.bindClickEvents();
});
Prefer the object literal syntax to the Object constructor; some authors go so far as to call the latter an anti-pattern
Here's the simplest way to set up App.Utils.Modal.bindClickEvents();
var App = {
Utils: {
Modal: {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
}
}
};
Or you can piece it together one step at a time:
var App = {};
App.Utils = {};
App.Utils.Modal = {};
App.Utils.Modal.bindClickEvents = function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
};

Categories