aura:if is not evaluating and aura:iteration not displaying - javascript

I have a Lightning component that listens for an Updated Account event and retrieves the associated Contacts and Users. It then displays which Account the Contacts are for with a total of Contacts and Users, and creates an SLDS Card with repeating Tiles.
It was working fine until I changed the header to say 'Loading' while the Contacts were loading. I added two new Attributes (one to hold the loading status and one to hold the total of Contacts and Users) and added the logic to update these attributes. Somehow this broke it, and I can't figure out how it's breaking.
According to the console and debug logs, everything is fine. The Contacts and Users are being retrieved properly from the Apex Controller, and these values are being written to the Attributes. Nothing is being updated, however.
ContactTable.cmp
<header class="slds-media slds-media--center slds-has-flexi-truncate">
<div class="slds-media__body">
<h2>
<span class="slds-text-heading--large">
<aura:if istrue="{!v.accountName}">
Contacts For {!v.accountName}: {!v.contactsLoadingStatus}
<aura:set attribute="else">
Contacts: - Please Select a Clinic -
</aura:set>
</aura:if>
</span>
</h2>
</div>
</header>
.........
(Contacts Tile Iterator)
.........
<aura:if istrue="{!v.contacts.length > 0}">
<h2 style="padding-left: 1.5rem;"><span class="slds-text-heading--medium">Contacts</span></h2>
<div class="slds-card__body--inner slds-grid slds-wrap" style="margin-bottom: 30px; height: 20rem; overflow: auto; border: 1px solid rgba(128, 128, 128, 0.32); border-radius: 10px;">
<aura:iteration items="{!v.contacts}" var="singleContact">
<c:ContactTableContactCard singlecontact="{!singleContact}" />
</aura:iteration>
</div>
</aura:if>
ContactTableController.js
handleUpdateAccountEvent: function(component, event, helper){
helper.updateAccount(component, event);
component.set('v.selectedContacts', null);
component.set('v.total', 0);
console.log('Account ID: ' + component.get('v.accountID'));
console.log('Account Name: ' + component.get('v.accountName'));
if(component.get('v.accountID')){
console.log('Grabbing contacts and team members');
component.set('v.contactsLoadingStatus', 'Loading...');
helper.setContacts(component, helper);
helper.setTeamMembers(component, helper);
}
else{
component.set('v.contacts', null);
component.set('v.teamMembers', null);
}
},
ContactTableHelper.js
setContacts: function (component, helper) {
var action = component.get("c.getContacts");
var accountID = component.get("v.accountID");
action.setParams({'accountId':accountID});
action.setCallback(this, function(response){
if(component.isValid()){
var state = response.getState();
if(state === 'SUCCESS'){
var contacts = response.getReturnValue();
component.set("v.contacts", contacts);
var total = component.get("v.total");
total += contacts.length;
component.set("v.total", total);
component.set("v.contactsLoadingStatus", total + " Records");
contacts = component.get('v.contacts');
console.log('Grabbing contacts.... Done');
console.log('contacts:');
console.log(contacts);
}
else{
console.log('There was an issue in retrieving the contacts.');
console.log(state);
if(state === 'ERROR'){
var errors = response.getError;
for(var i = 0; i < errors.length; i++){
console.log(errors[i].message);
}
}
}
}
});
$A.enqueueAction(action);
console.log('Grabbing contacts....');
},
updateAccount: function(component, event){
var accountID = event.getParam("accountID");
component.set("v.accountID", accountID);
var accountName = event.getParam("accountName");
component.set("v.accountName", accountName);
console.log('finished updateAccount');
},
I left out setTeamMembers because it is the same, more or less, as setContacts.
Relevant Console log:
finished updateAccount
Account ID: 001F0000013YX5DIAW
Account Name: ~NAME~.
Grabbing contacts and team members
Grabbing contacts....
Grabbing team members....
Grabbing contacts.... Done
contacts:
[Object, Object, Object]
Grabbing team members.... Done
teamMembers:
[Object, Object, Object, Object, Object, Object]

I figured it out. I used my IDE's code formatter which changed
<aura:if isTrue="">
to
<aura:if istrue="">
So if anyone else runs into this issue, check your spelling.

Related

Google Places API: how to listen to error events?

I'm using Google Places Autocomplete API in my frontend JS app like so:
const place = new google.maps.places.Autocomplete(inputRef.value, options)
google.maps.event.addListener(place, 'place_changed', function () {
const { place_id: placeIdGoogle, formatted_address: name } = place.getPlace()
// ...
})
I want to catch errors, especially rate limit errors to handle them gracefully and show something to the user.
Is there an event I can subscribe to?
Programmatically retrieving Place Autocomplete Service predictions and Place details
What you are using is the Autocomplete widget as per the documentation. But since the widget already handles the process for you, there's not much of a freedom in handling the response of the API.
What you can do is try to make one programmatically. Please see documentation to learn more.
In doing so, you can use three classes:
google.maps.places.AutocompleteService() for fetching predictions
google.maps.places.PlacesService() and use the getDetails method to fetch the details of a place your end user chooses.
google.maps.places.AutocompleteSessionToken() to make sure that your bills are optimized(since using session tokens will make your autocomplete queries free. See the documentation to learn more.)
The reason why you should programmatically implement the Places API to be able to handle errors is because the AutocompleteService() and PlacesService().getDetails() have a STATUS parameter which returns a status code / text.
You can do so in this way:
function initService() {
var li;
var input = document.getElementById("location");
var autoService = new google.maps.places.AutocompleteService();
var sessionToken = new google.maps.places.AutocompleteSessionToken();
input.addEventListener("keyup", keyUpFunc);
function keyUpFunc() {
var ul = document.getElementById('results');
while(ul.firstChild)ul.removeChild(ul.firstChild);
autoService.getPlacePredictions({
input: input.value ,
sessionToken: sessionToken
}, displaySuggestions);
}
var displaySuggestions = function(predictions, status) {
// if the status returned is not 'OK', which means an error, it would send an alert with the text code of the error
if (status != google.maps.places.PlacesServiceStatus.OK) {
alert(status);
return;
}
predictions.forEach(function(prediction) {
li = document.createElement('li');
li.setAttribute("data-id", prediction.place_id);
li.appendChild(document.createTextNode(prediction.description));
li.addEventListener("click", function(e) {
var place_id = this.getAttribute("data-id");
input.value = this.innerHTML;
getPlaceDet(place_id);
});
document.getElementById('results').appendChild(li);
});
};
function getPlaceDet(placeId){
var request = {
placeId: placeId,
sessionToken: sessionToken,
fields: ['name', 'vicinity']
};
var placeService = new google.maps.places.PlacesService(document.createElement('div'));
placeService.getDetails(request, callback);
function callback(place, status) {
// if the status returned is not 'OK', which means an error, it would send an alert with the text code of the error.
if (status != google.maps.places.PlacesServiceStatus.OK) {
alert(status)
}
if (status == google.maps.places.PlacesServiceStatus.OK) {
console.log('Name: ' + place.name +' Vicinity: ' + place.vicinity );
sessionToken = new google.maps.places.AutocompleteSessionToken();
}
}
}
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#right-panel {
font-family: 'Roboto','sans-serif';
line-height: 30px;
padding-left: 10px;
}
#right-panel select, #right-panel input {
font-size: 15px;
}
#right-panel select {
width: 100%;
}
#right-panel i {
font-size: 12px;
}
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Autocomplete Service</title>
</head>
<body>
<div id="right-panel">
<input type="text" id="location">
<p>Place suggestions</p>
<ul id="results"></ul>
</div>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initService"
async defer></script>
</body>
</html>
Just take note of where I put my comment // if the status returned is not 'OK', which means an error, it would send an alert with the text code of the error.
Just see this link to know more about PlacesAutocompleteStatus: https://developers.google.com/maps/documentation/places/web-service/autocomplete#PlacesAutocompleteStatus
And this link to know more about PlacesDetailsStatus: https://developers.google.com/maps/documentation/places/web-service/details#PlacesDetailsStatus
Hope this helps!

Angular Count Becomes 0 When Page is Refreshed

In my code, I have a page where the user can scan some labels and gets add to a list. Every time the user scan a label, total and package count above the list gets updated. But when I refresh the page, the counts become 0 as if there is no label on the list. Here is my code. What should I do?
HTML:
<div fxLayout="row" fxLayoutAlign="start center">
<span class="pr-4">{{'Paket : ' + packageCount }}</span>
<span>{{'Adet : ' + totalCount }}</span>
</div>
TS:
constructor(){
this.calculateRemainingValue();
}
addPackage(response: IKoli, barcodeNumber: string): boolean {
this.calculateRemainingValue();
this.isSaved = false;
this.fg1.reset();
return true;
}
calculateRemainingValue() {
if (this.dataSource.data) {
this.packageCount = this.dataSource.data.length;
let totalWeight = 0;
this.dataSource.data.forEach((x) => (totalWeight += Number(x.PaketAdedi)));
this.totalCount = totalWeight;
}
}
How I know this is normal behaviour of angular.
You must save the counter in some place, for example database or you could save it as cookie or save it in localStorage/sessionStorage.
Hope I helped you!

Firebase - Prevent child_added when delete with limitToLast

i want to build mini webchat - When view site i set show 5 messages and if view more, you can click button. All things are fine but when i remove 1 node, firebase auto add last node into, how can i prevent it?
Ex: I have node A,B,C,D,E,F,G. I had loaded list C,D,E,F,G but when i delete 1 in all, it auto add B into list.
<div id="messgesDiv">
<center><h3>Message</h3></center>
</div>
<div style="margin-top: 20px;">
<input type="text" id="nameInput" placeholder="Name">
<input type="text" id="messageInput" placeholder="Message" data-id="">
<input type="text" id="idproject" placeholder="ID Project">
</div>
<button id="delete">Delete Test</button>
<button id="edit">Edit</button>
<button id="loadmore">Load more</button>
<button id="showlastkey">Show last key</button>
My javascript
$('#loadmore').click(function() {
i = 0; old = first;
myDataRef.orderByKey().endAt(first).limitToLast(6).on('child_added', function (snapshot){
if( i == 0)
first = snapshot.key();
var message = snapshot.val();
if(snapshot.key() != old)
displayChatMessage(message.name, message.text, message.idproject, 'old');
i++;
console.log('myDataRef.orderByKey().endAt(first).limitToLast(6)');
});
});
$("#messageInput").keypress(function (e){
if(e.keyCode == 13){ //Enter
var name = $("#nameInput").val();
var text = $("#messageInput").val();
var idproject = $("#idproject").val();
if($("#messageInput").data("id")=='')
{
myDataRef.push({name: name, text: text, idproject: idproject});
}
else
{
myDataRef.child(key).update({name: name, text: text, idproject: idproject});
$('#messageInput').attr('data-id', '');
}
$("#messageInput").val("");
}
});
myDataRef.limitToLast(5).on('child_added', function (snapshot){
if( i == 0)
first = snapshot.key();
var message = snapshot.val();
displayChatMessage(snapshot.key(), message.name, message.text, message.idproject, 'new');
i++;
console.log(snapshot.key());
console.log(' myDataRef.limitToLast(5)');
});
function displayChatMessage(key, name, text, idproject, status){
//console.log(name + " -- " + text + " -- " +idproject);
if( status == 'new')
{
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).appendTo($("#messgesDiv"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
else
{
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).insertAfter($("center"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
}
$('#delete').click(function() {
myDataRef.child(key).remove();
$('#messgesDiv').filter('[data-id="'+key+'"]').remove();
});
Firebase limit queries act like a view on top of the data. So if you create a query for the 5 most recent messages, the Firebase client will ensure that you always have the 5 most recent messages.
Say you start with these messages:
message1
message2
message3
message4
message5
Now if you add a message6, you will get:
child_removed message1
child_added message6
So that your total local view becomes:
message2
message3
message4
message5
message6
Conversely when you remove message 6 again, you get these events:
child_removed message6
child_added message1 (before message2)
So that you can update the UI and end up with the correct list again.
There is no way to change this behavior of the API. So if you want to handle the situation differently, you will have to do this in your client-side code.
Your code currently only handles child_added. If you have add a handler for child_removed you'll see that you can easily keep the user interface in sync with the data.
Alternatively you can detect that the message is already in your UI by comparing the key of the message you're adding to the ones already present in the DOM:
function displayChatMessage(key, name, text, idproject, status){
var exists = $("div[data-id='" + key + "']").length;
if (status == 'new' && !exists) {
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).appendTo($("#messgesDiv"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
else {
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).insertAfter($("center"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
}

Web sql database implementation error

I have the following well running jsFiddle which shows proper implementation of web sql feature:
https://jsfiddle.net/Trae/76srLbwr/
If I copy the very same file on my .html page and try to run. It is not running.
HTML:
<html>
<head></head>
<body>
<h1>WebSQL Example</h1>
<div id="controls">
<p>Add a car to the database</p>
<label>Make:</label>
<input type="text" id="carmake" />
<br />
<label>Model:</label>
<input type="text" id="carmodel" />
<br />
<button type="button" id="addcar" onclick="addCar();">Add Car</button>
</div>
<div id="carlistholder">
<h3>Your Cars</h3>
<ul id="carlist"></ul>
</div>
<p><strong>Note: </strong>You can leave this page and when you return the cars you entered will still be here!</p>
<script>
//Test for browser compatibility
if (window.openDatabase) {
//Create the database the parameters are 1. the database name 2.version number 3. a description 4. the size of the database (in bytes) 1024 x 1024 = 1MB
var mydb = openDatabase("cars_db", "0.1", "A Database of Cars I Like", 1024 * 1024);
//create the cars table using SQL for the database using a transaction
mydb.transaction(function (t) {
t.executeSql("CREATE TABLE IF NOT EXISTS cars (id INTEGER PRIMARY KEY ASC, make TEXT, model TEXT)");
});
} else {
alert("WebSQL is not supported by your browser!");
}
//function to output the list of cars in the database
function updateCarList(transaction, results) {
//initialise the listitems variable
var listitems = "";
//get the car list holder ul
var listholder = document.getElementById("carlist");
//clear cars list ul
listholder.innerHTML = "";
var i;
//Iterate through the results
for (i = 0; i < results.rows.length; i++) {
//Get the current row
var row = results.rows.item(i);
listholder.innerHTML += "<li>" + row.make + " - " + row.model + " (<a href='javascript:void(0);' onclick='deleteCar(" + row.id + ");'>Delete Car</a>)";
}
}
//function to get the list of cars from the database
function outputCars() {
//check to ensure the mydb object has been created
if (mydb) {
//Get all the cars from the database with a select statement, set outputCarList as the callback function for the executeSql command
mydb.transaction(function (t) {
t.executeSql("SELECT * FROM cars", [], updateCarList);
});
} else {
alert("db not found, your browser does not support web sql!");
}
}
//function to add the car to the database
function addCar() {
//check to ensure the mydb object has been created
if (mydb) {
//get the values of the make and model text inputs
var make = document.getElementById("carmake").value;
var model = document.getElementById("carmodel").value;
//Test to ensure that the user has entered both a make and model
if (make !== "" && model !== "") {
//Insert the user entered details into the cars table, note the use of the ? placeholder, these will replaced by the data passed in as an array as the second parameter
mydb.transaction(function (t) {
t.executeSql("INSERT INTO cars (make, model) VALUES (?, ?)", [make, model]);
outputCars();
});
} else {
alert("You must enter a make and model!");
}
} else {
alert("db not found, your browser does not support web sql!");
}
}
//function to remove a car from the database, passed the row id as it's only parameter
function deleteCar(id) {
//check to ensure the mydb object has been created
if (mydb) {
//Get all the cars from the database with a select statement, set outputCarList as the callback function for the executeSql command
mydb.transaction(function (t) {
t.executeSql("DELETE FROM cars WHERE id=?", [id], outputCars);
});
} else {
alert("db not found, your browser does not support web sql!");
}
}
outputCars();
</script>
<style>
body {
font-family: sans-serif;
padding: 10px;
}
h1 {
font-weight: bold;
}
label {
font-size: small;
}
#controls {
padding-bottom: 5px;
border-bottom: 1px solid #000;
}
</style>
</body>
</html>
ERROR:
Can someone help me out when I am copying the same files, and opening in same browser Chrome (Version 51.0.2704.84), why is it not running from an html file?
I have a same problem when browse the html file directly in chrome. I can solve it creating a website in the IIS.
I hope to help you.
Daniel.-

Editing collections in AngularJS - where to keep model for "new item" form?

I am writing an app where I want to edit collection inside of another collection. Because of this, I have a repeating form for each element of the collection.
Full example is http://plnkr.co/edit/SP5IRaZZJfTaE9DISrXs?p=preview below is the key part:
<div ng-repeat="u in users" class="user">
<div>{{u.name}} <button ng-click="delete(users, $index)">X</button></div>
<div class="contacts">Contacts:
<ul><li ng-repeat="c in u.contacts">{{c.name}}
<button ng-click="delete(u.pseudos, $index)">X</button></li>
<!-- This will be users[index].newContactName - ugly... -->
<li><input type="text" ng-model="u.newContactName" />
<button ng-click="addContact($index)">add contact</button></li></ul>
</div>
</div>
<!-- This will be $scope.newUser - no problem -->
<input type="text" ng-model="newUser" /><button ng-click="addUser()">add user</button>
This kind of worked, until I needed to watch the collection - now editing newContactName triggers my "deep" watch:
$scope.$watch('users', function(newUsers, oldUsers) {
// $scope.log = $scope.log + "users updated\n";
}, true);
What is the "canonical" way of doing such things in Angular? Should I have a parallel collection of "new contacts" per each user, so watching users will be unaffected? (keeping it in sync will be weird)
I would watch it this way:
$scope.$watch('users', function(newUsers, oldUsers) {
//detect changes in user list
if (newUsers.length > oldUsers.length) {
alert("user added\n")
//do something
return;
}
if (newUsers.length < oldUsers.length) {
alert("user deleted\n")
//do something
return;
}
//nothing has changed in users. Examine contacts (This is the part I am not proud of, since it may take bloody ages with thousands of users and contacts)
angular.forEach(newUsers, function(user, index) {
if (user.contacts.length > oldUsers[index].contacts.length) {
alert('Contact added to: ' + user.name);
//do something
return;
}
if (user.contacts.length < oldUsers[index].contacts.length) {
alert('Contact deleted from: ' + user.name);
//do something
return;
}
})
//nothing has changed in contacts too? Must be some lousy edit!
}, true);
Not sure if this is canonical angular zen or if the new watchCollection is better suited. But, hey, it works: Forked Plunker with lots of Alerts
For now I ended up going with having a parallel array of forms, and updating it in parallel, I am having other collections under each user and this "new forms" collection can hold fields for all of them: http://plnkr.co/edit/nyrI1zXTP3JyZlqs1fWJ?p=preview (added logging of watch-updates too)
Key parts:
<div ng-repeat="u in users" class="user">
<div>{{u.name}} <button ng-click="delete($index, [users, userNewForms])">X</button></div>
<div class="contacts">Contacts:
<ul><li ng-repeat="c in u.contacts">{{c.name}}
<button ng-click="delete($index, u.contacts)">X</button></li>
<li><input type="text" ng-model="userNewForms[$index].newContactName" />
<button ng-click="addContact($index)">add contact</button></li></ul>
</div>
</div>
<input type="text" ng-model="newUser" /><button ng-click="addUser()">add user</button>
Javascript:
$scope.addContact = function(userIndex) {
var user = $scope.userNewForms[ userIndex ];
$scope.users[ userIndex ].contacts.push( {'name': user.newContactName } );
}
$scope.delete = function(index, arraysOrArray) {
if(arraysOrArray[0] instanceof Array) {
for( var i = 0; i < arraysOrArray.length; ++i ) {
arraysOrArray[i].splice( index, 1 );
}
}
else {
arraysOrArray.splice( index, 1 );
}
}
...
function computeNewContactForms(users) {
var result = [];
for( var u in users ) {
result.push( makeNewContactForm() );
}
return result;
}
function makeNewContactForm() { return { 'newContactName': '' }; }
Most interesting thing was a discovery that if I use for(a in arrayOrArrays) instead of an index-based iteration, then I get an exception. It looks like "in" form triggers immediate update of bindings or something.

Categories

Resources