Canada Post AddressComplete on "populate" not working - javascript

I'm trying to use Canada Post's Address Complete on my form as such
var fields = [
{ element: "street_address", field: "Line1" },
{ element: "city_address", field: "City", mode: pca.fieldMode.POPULATE },
{ element: "postal_code", field: "PostalCode", mode: pca.fieldMode.POPULATE },
{ element: "country", field: "CountryName", mode: pca.fieldMode.COUNTRY }
],
options = {key: KEY},
control = new pca.Address(fields, options);
addressComplete.listen('load', function(control) {
control.listen("populate", function (address) {
if(address.ProvinceCode == "ON"){
console.log("ONTARIO");
document.getElementById('province').selectedIndex = 2;
}
else if(address.ProvinceCode == "QC"){
document.getElementById('province').selectedIndex = 3;
}
});
});
I'm able to search for an address and have some fields auto populate. The Province on my form is a dropdown which is where I want to use the listener as suggested in the website, but it doesn't work? Could someone please let me know what I'm doing wrong?

I tried playing with the API and I couldn't get any events to fire on the addressComplete object but the ready event. However, since we have all ready constructed a control instance, I just removed the load listener and attached the populate event handler directly to the control object we constructed. This seemed to work.
//addressComplete.listen('load', function (control) {
control.listen('populate', function (address) {
// TODO: Handle populated address here.
});
//});

I got error - Uncaught ReferenceError: control is not defined
Once the Canada Post JavaScript is loaded, then the control instance is created - addressComplete.controls[0]
To listen to populate event of the control:
addressComplete.controls[0].listen("populate", function (address) {
// TODO: Handle populated address here.
});
load() and reload() apis are also available.
addressComplete.controls[0].load();
addressComplete.controls[0].reload();
// destroy();
// load();

Related

Using ag-grid with many rows and Autosave

I'm using ag-grid (javascript) to display a large amount of rows (about 3,000 or more) and allow the user to enter values and it should auto-save them as the user goes along. My current strategy is after detecting that a user makes a change to save the data for that row.
The problem I'm running into is detecting and getting the correct values after the user enters a value. The onCellKeyPress event doesn't get fired for Backaspace or Paste. However if I attach events directly to DOM fields to catch key presses, I don't know how to know what data the value is associated with. Can I use getDisplayedRowAtIndex or such to be able to reliably do this reliably? What is a good way to implement this?
EDIT: Additional detail
My current approach is to capture onCellEditingStopped and then getting the data from the event using event.data[event.column.colId]. Since I only get this event when the user moves to a different cell and not just if they finish typing I also handle the onCellKeyPress and get the data from event.event.target (since there is no event.data when handling this event). Here is where I run into a hard-to-reproduce problem that event.event.target is sometimes undefined.
I also looked at using forEachLeafNode method but it returns an error saying it isn't supported when using infinite row model. If I don't use infinite mode the load time is slow.
It looks like you can bind to the onCellKeyDown event. This is sometimes undefined because on first keydown the edit of agGrid will switch from the cell content to the cell editor. You can wrap this around to check if there is a cell value or cell textContent.
function onCellKeyDown(e) {
console.log('onCellKeyDown', e);
if(e.event.target.value) console.log(e.event.target.value)
else console.log(e.event.target.textContent)
}
See https://plnkr.co/edit/XhpVlMl7Jrr7QT4ftTAi?p=preview
As been pointed out in comments, onCellValueChanged might work, however
After a cell has been changed with default editing (i.e. not your own custom cell renderer), the cellValueChanged event is fired.
var gridOptions = {
rowData: null,
columnDefs: columnDefs,
defaultColDef: {
editable: true, // using default editor
width: 100
},
onCellEditingStarted: function(event) {
console.log('cellEditingStarted', event);
},
onCellEditingStopped: function(event) {
console.log('cellEditingStopped', event);
},
onCellValueChanged: function(event) {
console.log('cellValueChanged', event);
}
};
another option could be to craft your own editor and inject it into cells:
function MyCellEditor () {}
// gets called once before the renderer is used
MyCellEditor.prototype.init = function(params) {
this.eInput = document.createElement('input');
this.eInput.value = params.value;
console.log(params.charPress); // the string that started the edit, eg 'a' if letter a was pressed, or 'A' if shift + letter a
this.eInput.onkeypress = (e) => {console.log(e);} // check your keypress here
};
// gets called once when grid ready to insert the element
MyCellEditor.prototype.getGui = function() {
return this.eInput;
};
// focus and select can be done after the gui is attached
MyCellEditor.prototype.afterGuiAttached = function() {
this.eInput.focus();
this.eInput.select();
};
MyCellEditor.prototype.onKeyDown = (e) => console.log(e);
// returns the new value after editing
MyCellEditor.prototype.getValue = function() {
return this.eInput.value;
};
//// then, register it with your grid:
var gridOptions = {
rowData: null,
columnDefs: columnDefs,
components: {
myEditor: MyCellEditor,
},
defaultColDef: {
editable: true,
cellEditor: 'myEditor',
width: 100
},
onCellEditingStarted: function(event) {
console.log('cellEditingStarted', event);
},
onCellEditingStopped: function(event) {
console.log('cellEditingStopped', event);
}
};

SAPUI5 - Model.Remove generates two requests

I am currently facing a strange behavior with my SAPUI5 coding when I do a DELETE with the model (sap.ui.model.odata.v2.ODataModel). I wanted to implement a list, which displays some "Favorites" in a SelectDialog. By pressing the icon, the users can delete a favorite. For the item itself I used a FeedListItem, which is triggering the iconPress-Event _handleIconPressDelete.
<FeedListItem icon="sap-icon://delete" iconActive="true" iconPress="_handleIconPressDelete" text="{Name}" sender="{ID}"/>
The event looks like this:
_handleIconPressDelete: function(oEvent) {
var oModel = oEvent.getSource().getModel();
oModel.remove(oEvent.getSource().getBindingContext().getPath(), {
success: function(data) {
// success handling
},
error: function(e) {
// error handling
}
});
}
But when this event is triggered, two identical delete requests are generated and causing an error, because with the current changeset coding in the backend, I am only allowed to do one request at the same time.
The strange thing is, this behavior only appears when I open the dialog the first. When I close and reopen it, everything works fine.
Do you have any ideas, what I might do wrong here so that two requests are generated? I also checked, if the event is triggered multiple times, but that wasn't the case.
As current workaround I am using deferredGroups as shown in the snipped below so that the two request are separated, but I think there must be better ways to solve this.
_handleIconPressDelete: function(oEvent) {
var oModel = oEvent.getSource().getModel();
oModel.setDeferredGroups(["group1"]);
oModel.remove(oEvent.getSource().getBindingContext().getPath(), {
groupId: "group1",
success: function(data) {
// success handling
},
error: function(e) {
// error handling
}
});
oModel.submitChanges({
groupId: "group1"
});
}
I too experienced the same issue where the event associated with iconPress of FeedListItem triggers twice though user click only once..
Following is a workaround which you can implement using custom coding.
Declare the following variable in view controller's onInit()
this._bFirstTrigger = true;//SETTING FOR THE FIRIST TIME
Use this in FeedListItem's iconPress event to ensure that the relevant code executes only once as follows:
_handleIconPressDelete: function(oEvent) {
if (this._bFirstTrigger) {
var oModel = oEvent.getSource().getModel();oModel.setDeferredGroups(["group1"]);
oModel.remove(oEvent.getSource().getBindingContext().getPath(), {
groupId: "group1",
success: function(data) {
// success handling
},
error: function(e) {
// error handling
}
});
oModel.submitChanges({
groupId: "group1"
});
}
this._bFirstTrigger = false;
}
else{
this._bFirstTrigger = true;
}
Regards,
Fahad Hamsa

Mouse over Popup on multiple markers leaflet.js?

so I have a leaflet map with lot of markers placed on it. I want to have a popup with like the status of asset etc on 'hover' over the marker. I see some examples on google and try to implement but none of them is firing any events. here is my code with my attempt. how can i achieve this feature? do i have to use somekind of tooltip instead of popup?
buildMarkerLayer = (rawAssetsObjects) => {
let markersGroup = null;
var self = this;
markersGroup = L.markerClusterGroup({
spiderfyOnMaxZoom: true,
showCoverageOnHover: true,
zoomToBoundsOnClick: true,
spiderfyDistanceMultiplier: 2
});
self.$localForage.getItem('showAllAreas').then((_showAll) => {
if(_showAll){
this.loadAllAreas();
}else{
this.hideAllAreas();
}
});
angular.forEach(rawAssetsObjects, function(_asset) {
if(_asset.latitude && _asset.longitude){
markersGroup.addLayer(L.marker(L.latLng(_asset.latitude,
_asset.longitude), {
id: _asset.id,
icon: L.divIcon({
html: self.siMarkers.createHTMLMarker(_asset)
})
}).on('click', function(e) {
//dismiss the event timeline
self.$mdSidenav('right').close();
self.centerOnClick(_asset);
//set the selected asset to a shared service for availability in
//other controllers
self.siMapRam.setActive(_asset);
//inform detail controller of a newly selected asset to query
self.$rootScope.$broadcast('ActiveAssetChange');
self.dgModal.display();
}).bindPopup('work').on('mouseover',function(ev) {
markersGroup.openPopup();
}));
};
});
return markersGroup
}
so I added the mouseover function and is responding on the console with error, so at least i know the listening part is working
I was close to the answer, while following many examples on google they made L.Marker into a variable like var marker = L.marker. Then call marker.openPopup(). My case, as you can see, I straight called L.marker. Problem was calling L.marker.openPopup() or L.marker(openPopup()) throws error saying openPopup is undefined. so the solution was pretty straight forward and make L.marker into a variable. like below. I also added small popup formatting like where pop-up should display using popAnchor and HTML formatting, for future flowers
angular.forEach(rawAssetsObjects, function (_asset) {
let marker = L.marker(L.latLng(_asset.latitude,
_asset.longitude), {
id: _asset.id,
icon: L.divIcon({
html: self.siMarkers.createHTMLMarker(_asset),
popupAnchor: [0,-80]
})
});
if (_asset.latitude && _asset.longitude) {
let content = "<b>"+_asset.name+"</b>"+"<br>"+"<b>"+'Status: '+"</b>"+_asset.status
markersGroup.addLayer( marker.bindPopup(content)
.on('mouseover', function (e) {
self.siMapRam.setActive(_asset);
self.$rootScope.$broadcast('ActiveAssetChange');
marker.openPopup()
})
.on('click', function (e) {
//dismiss the event timeline
self.$mdSidenav('right').close();
self.centerOnClick(_asset);
//set the selected asset to a shared service for availability in
//other controllers
self.siMapRam.setActive(_asset);
//inform detail controller of a newly selected asset to query
self.$rootScope.$broadcast('ActiveAssetChange');
self.dgModal.display();
}));
};
});
return markersGroup
}

Trigger input change on website using Angular with Chrome Extension content script

I'm creating a content-script which will populate
https://www.moneysupermarket.com/shop/car-insurance/questionset/#?step=highimpactquestions
with predefined data.
But I have problem with first step, the first field on the page, registration number.
I'm using jQuery inside my script, and I can set input's value to what I want using
$('input#regnumber').val(data['car_reg'])
But when I simulate click on find car button it seems like for website that field is empty, as 'Please enter a registration number' warning appears instead of showing car info.
I looked through the code, and saw that website uses angular, so I guess it has something to do with its binding.
I tried triggering change,keyup,input events on the $('input#regnumber') but it doesn't change anything.
EDIT:
Also tried with
$('input#regnumber').val(data['car_reg']).dispatchEvent(new Event("input", { bubbles: true }));
without success
AngularJS inputs use an ng-model directive to connect an input with the model:
<input id="regnumber" type="text" ng-model="formData.item" />
To set the model from outside the framework use:
var elem = $('input#regnumber');
var scope = angular.element(elem).scope();
scope.$apply(function() {
scope.formData.item = value;
});
For more information, see
AngularJS angular.element API Reference
AngularJS scope API Reference ($apply)
AngularJS Developer Guide - Scope (Integration with the Browser Event Loop)
You are getting validation error, because maybe the value of data['car_reg'] is undefined. If the data is an array of objects, then use like data[0].car_reg. Use console to verify the value.
I found a way finally.. this is what I did in my background script for the tab where this page is loaded:
setTimeout(function() {
chrome.tabs.executeScript(tab.id, { 'file': '/jquery-2.2.4.min.js', 'runAt': 'document_start' })
chrome.tabs.executeScript(tab.id, { 'file': '/angular.min.js' })
setTimeout(function() {
chrome.tabs.executeScript(tab.id, { 'code': 'angular.reloadWithDebugInfo();' });
setTimeout(function() {
chrome.tabs.executeScript(tab.id, { 'file': '/jquery-2.2.4.min.js' });
chrome.tabs.executeScript(tab.id, { 'file': '/formfiller.js' })
}, 5000);
}, 5000);
}, 8000)
And in my formfiller.js I do this in order to fill that field:
var scrpt = document.createElement('script');
scrpt.innerText = "var scp = angular.element($('#regnumber')).scope();scp.regnumber = '12345';scp.$apply(); $('.car-registration__button').click();"
document.head.appendChild(scrpt);
scrpt.onload = function() {
scrpt.parentNode.removeChild(scrpt);
};

Hide Approve/Reject Buttons in Fiori Master Details Page

I am looking to hide the Approve/Reject Buttons in the Details Page of a Fiori App based on certain filter conditions. The filters are added in the Master List view (Left hand side view) thru the view/controller extension.
Now, if the user selects certain type of filter ( Lets say, Past Orders) - then the approve/reject button should not be displayed in the Order Details Page.
This is how I have defined the buttons in the Header/Details view
this.oHeaderFooterOptions = {
oPositiveAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_APPROVE"),
id :"btn_approve",
onBtnPressed: jQuery.proxy(that.handleApprove, that)
},
oNegativeAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_REJECT"),
id :"btn_reject",
onBtnPressed: jQuery.proxy(that.handleReject, that)
},
However at runtime, these buttons are not assigned the IDs I mentioned, instead they are created with IDs of __button0 and __button1.
Is there a way to hide these buttons from the Master List View?
Thank you.
Recommended:
SAP Fiori design principles only talk about disabling the Footer Buttons instead of changing the visibility of the Button.
Read More here about Guidelines
Based on filter conditions, you can disable like this:
this.setBtnEnabled("btn_approve", false);
to enable again: this.setBtnEnabled("btn_approve", true);
Similarly you can change Button text using this.setBtnText("btn_approve", "buttonText");
Other Way: As #TobiasOetzel said use
this.setHeaderFooterOptions(yourModifiedHeaderFooterOptions);
you can call setHeaderFooterOptions on your controller multiple times eg:
//Code inside of the controller
_myHeaderFooterOptions = {
oPositiveAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_APPROVE"),
id :"btn_approve",
onBtnPressed: jQuery.proxy(that.handleApprove, that)
},
oNegativeAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_REJECT"),
id :"btn_reject",
onBtnPressed: jQuery.proxy(that.handleReject, that)
}
},
//set the initial options
onInit: function () {
this.setHeaderFooterOptions(this._myHeaderFooterOptions);
},
//modify the options in an event
onFilter : function () {
//remove the negative action to hide it
this._myHeaderFooterOptions.oNegativeAction = undefined;
this.setHeaderFooterOptions(this._myHeaderFooterOptions);
},
//further code
so by manipulating the _myHeaderFooterOptions you can influence the displayed buttons.
First, you should use sId instead id when defining HeaderFooterOptions, you can get the footer buttons by sId, for example, the Approve button.
this._oControlStore.oButtonListHelper.mButtons["btn_approve"]
Please check the following code snippet:
S2.view.controller: You have a filter event handler defined following and use EventBus to publish event OrderTypeChanged to S3.view.controller.
onFilterChanged: function(oEvent) {
// Set the filter value, here i use hard code
var sFilter = "Past Orders";
sap.ui.getCore().getEventBus().publish("app", "OrderTypeChanged", {
filter: sFilter
});
}
S3.view.controller: Subscribe event OrderTypeChanged from S2.view.controller.
onInit: function() {
///
var bus = sap.ui.getCore().getEventBus();
bus.subscribe("app", "OrderTypeChanged", this.handleOrderTypeChanged, this);
},
getHeaderFooterOptions: function() {
var oOptions = {
oPositiveAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_APPROVE"),
sId: "btn_approve",
onBtnPressed: jQuery.proxy(that.handleApprove, that)
},
oNegativeAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_REJECT"),
sId: "btn_reject",
onBtnPressed: jQuery.proxy(that.handleReject, that)
}
};
return oOptions;
},
handleOrderTypeChanged: function(channelId, eventId, data) {
if (data && data.filter) {
var sFilter = data.filter;
if (sFilter == "Past Orders") {
this._oControlStore.oButtonListHelper.mButtons["btn_approve"].setVisible(false);
}
//set Approve/Reject button visible/invisible based on other values
//else if(sFilter == "Other Filter")
}
}

Categories