cannot set property of undefined angular js - javascript

I have a list of json objects, $scope.phones, and a folder full of json files with additional data about each phone. I am trying to iterate through the files to grab additional information to put in my list about each phone:
phonecatControllers.controller('PhoneListCtrl', ['$scope', 'Phone',
function($scope, Phone) {
$scope.phones = Phone.query();
var myTimer = window.setTimeout(function() {
for (var i = 0; i < $scope.phones.length; i++){
var weight = 0;
Phone.get({phoneId: $scope.phones[i].id}, function( phone) {
weight = phone.sizeAndWeight.weight;
$scope.phones[i].weight = weight;
});
};
} , 1000 );
$scope.orderProp = 'age';
}]);
The timer is so that the function doesn't run until after scope.phones is set, (I realize its a bit of a hack but its not causing issues.) I get an error cannot set property of undefined on:
$scope.phones[i].weight = weight;
If i try to access this outside of the .get method there isn't a problem, however the new value of weight does not exist outside of the get method.

wait for the list of phones before looping through them:
Phone.query().then(function(phones){
for(var i=0; i<phones.length; i++){
var weight = 0;
Phone.get({phoneId: phones[i].id}, function( phoneDetails) {
weight = phoneDetails.sizeAndWeight.weight;
phones[i].weight = weight;
});
}
}

Related

Build JavaScript Options Array From Multiple Returned Values

I am trying to build a baseline JS file to include in my some of my pages to return information from my SharePoint site. I would like to make three ajax calls to the server for the following: siteGroups, siteRoles and currentUser information and build an options array that I can use later by accessing it using dot notation. (e.g. options.siteGroup, options.siteRoles, options.currentUser.UserName, etc)
var options = (function(){
var currentUser = (function(){
var userInfo = [];
function complete(data){
for(i = 0; i < data.d.Groups.results.length; i++){
var user = {groupId : data.d.results[i].groupId, groupName :
data.d.results[i].groupName, UserName : data.d.UserName, UserId :
data.d.UserId};
UserInfo.push(user);
}
};
getCurrentUser(url, query, complete);
return userInfo;
})();
// Get All Site Permission Groups
var siteGroups = (function(){
function complete(data){
var siteGroups = [];
for(j = 0; j < data.d.results.length; j++){
var group = {groupName : data.d.results[j].Title, groupId :
data.d.results[j].Id};
siteGroups.push(group);
};
$('.SiteGroups').append(option);
};
getSiteGroups(url, complete);
return siteGroups;
})();
// Get All Role Definitions to be used for role assignment
var siteRoles = (function(){
var roles = [];
function complete(data){
var option;
for(s = 0; s < data.d.results.length; s++){
var role = {roleId : data.d.results[s].Id, roleName :
data.d.results[s].Name};
roles.push(role);
}
}
getRoleDefinitions(url, complete);
return roles;
})();
return {currentUser, siteRoles, siteGroups};
})();
When I console.log(options); I get an array with the correct values, however every combination (options.currentUser, options.currentUser.userName, etc) has yielded either userName is undefined or currentUser[0] cannot find value at position 0. Appreciate the help in advance!
http://imgur.com/Ubtb5nD "Image of console.log"

How to get the right index for a delayed trigger?

Following problem: I have an angular module with $http.get to get some google coordinates. This function triggers another function. This function again triggers another function. It will all make sense in a moment.
Angular Module:
var myApp = angular.module('wmw', []);
myApp.controller('MainCtrl', function ($scope, $http) {
//Angular Method
$scope.getTargetCords = function (data) {
$http.get(data).success(function (response) {
$(document).triggerHandler('my_func:data-received', [response]);
});
};
});
onSucess:
var onSuccess = function(position) {
currentLat = position.coords.latitude ;
currentLng = position.coords.longitude;
for(i = 0; i<locations.length;i++){
var destUrl = 'http://maps.googleapis.com/maps/api/geocode/xml?address=' + locations[i][ 'street' ] + ',' + locations[i][ 'city' ] + ',Deutschland' + '&sensor=true';
var MyAngularScope = angular.element($("#MainCtrlId")).scope();
MyAngularScope.getTargetCords('http://maps.googleapis.com/maps/api/geocode/xml?address=' + locations[i][ 'street' ] + ',' + locations[i][ 'city' ] + ',Deutschland' + '&sensor=true');
}
};
navigator.geolocation.getCurrentPosition(onSuccess, onError);
The two triggers:
$(document).on('my_func:data-received', function(event, response) {
map[s] = response;
s++;
if(s === locations.length){
$(document).triggerHandler('allData');
}
});
$(document).on('allData', function(){
var thecoords = [];
var distance = [];
$('#filter-list').empty();
for(var i = 0; i < locations.length; i++){
thecoords[0] = $(map[i]).find('lat').first().text();
thecoords[1] = $(map[i]).find('lng').first().text();
distance[i] = calculateDistance(currentLat, currentLng, thecoords[0], thecoords[1]);
locations[i]['distance'] = distance[i];
}
locations.sort(function(a,b)
{ return a.distance - b.distance;}
);
for(var i = 0;i < locations.length; i++){
distance[i] = locations[i]['distance'].toFixed(2);
distance[i] += ' KM';
locations[i]['distance'] = distance[i];
}
$('.loading').hide();
for(var i = 0; i<=5; i++){
addItemToList(locations[i]);
}
});
What's happening? With those functions I retrieve the current location, the dest location and calculate the difference in KM via a lat./long. calc function which I found on the web. .loading is just a div with "Calculating route..." and a transparent grey background. So once everything is finished, The "Distance" of every route will change to the calculated distance.
The problem with this: in my ".on('my_func:data-received')" I am using the variable "s" which is 0 at the start. In my logic I thought, that this would then put the responses one after another in my "map". But now I realised, that the "data-received" are not called one after another, but each time when data is retrieved. So when locations[0] is calling the $http.get and then after this locations[1] is calling the $http.get, it could happen, that locations[1] retrieves the data earlier. How could I have my "s" always be the right number? So that when I have locations[1] calling $http.get map[1] will be locations[1] response?
My head is exploding, as I cant find a solution to this problem, although it seems to be so basic.
Thank you in advance!
Since restructuring your application is not an option, another reasonably quick way of getting the right order is mapping the response data to the original array. The response contains the url which is built using data from the array which might give you what you need.

parsing CSV in Javascript and AngularJS

So I'm trying to create a basic angular application that parses some CSV input, and fills a table with the parsed data.
You can see a plunker of what I'm trying to achieve here - http://plnkr.co/edit/6QFT4AcV4KpiSG23EdOS
Basically - as you can see - I have a <textarea> where the user will paste in some CSV, and the table below should then be filled with the data.
<div class="excelArea">
<textarea name="excelData" ng-model="excelData"></textarea>
</div>
This is the javascript I have so far, but I'm struggling with a few things
1. Seperating the email from the name
2. Pushing the data back into the $scope.inviteList;
app.controller("InviteController", function($scope) {
//Initliase objects
$scope.excelData = "";
$scope.errorMessage = "";
$scope.inviteList = [];
$scope.$watch("excelData", function() {
var lines, lineNumber, data, length;
lines = $scope.excelData.match(/[^\r\n]+/g);
lineNumber = 0;
for (var i = lines.length - 1; i >= 0; i--) {
l = lines[i];
lineNumber++;
data = l.split(/\t/);
var email = ? ? ?
var name = ? ? ?
$scope.inviteList.push({
name: name,
email: email,
status: "not sent"
});
};
});
});
Some basic information:
The CSV will be two columns (name, email) and will look like this:
John Thompson,john#thompson.com
Robin Peters, robin#peters.com
Bill Bob, bill#bob.com
Many problems in your code :
You could juste use split on your input instead of regexes, it makes everything easier
Your HTML isn't valid td should be inside tr and not the other way around
Your array was never cleared
Your bindings inside ng-repeat didn't use variable i defined here : i in inviteList
You should avoid unscoped variables (without var keyword) whenever possible
Otherwise, when you split a string, just access the splitted elements through their index.
Corrected code :
JS
$scope.$watch("excelData", function() {
var lines, lineNumber, data, length;
$scope.inviteList = [];
lines = $scope.excelData.split('\n');
lineNumber = 0;
for (var i = lines.length - 1; i >= 0; i--) {
l = lines[i];
lineNumber++;
data = l.split(',');
var name = data[0];
var email = data[1];
$scope.inviteList.push({
name: name,
email: email,
status: "not sent"
});
}
});
HTML
<table>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
<tr ng-repeat="i in inviteList">
<td>{{i.name}}</td>
<td>{{i.email}}</td>
<td>{{i.status}}</td>
</tr>
</table>
Your code (especially JS) can still be improved a lot and i encourage you to read docs/tutorials more.
And here is the plunker to your working code.
I would create a filter to parse the data and reuse it anywhere I want it to.
Logic:
app.controller('MainCtrl', function($scope, $filter) {
$scope.data = 'John Thompson,john#thompson.com\nRobin Peters, robin#peters.com\nBill Bob, bill#bob.com';
$scope.$watch('data', function(val){
$scope.inviteList = $filter('csvToObj')(val);
});
});
app.filter('csvToObj', function() {
return function(input) {
var rows = input.split('\n');
var obj = [];
angular.forEach(rows, function(val) {
var o = val.split(',');
obj.push({
name: o[0],
email: o[1],
status: "not sent"
});
});
return obj;
};
});
Sample Demo:http://plnkr.co/edit/SOtMMLft3amlVm4RROd0?p=preview
When you split a string, the return value is an array.
I believe that you only have to access the correct indexes of the data array:
app.controller("InviteController", function($scope) {
//Initliase objects
$scope.excelData = "";
$scope.errorMessage = "";
$scope.inviteList = [];
$scope.$watch("excelData", function() {
var lines, lineNumber, data, length;
lines = $scope.excelData.match(/[^\r\n]+/g);
lineNumber = 0;
for (var i = lines.length - 1; i >= 0; i--) {
l = lines[i];
lineNumber++;
data = l.split(/\t/);
var email = data[1];
var name = data[0];
$scope.inviteList.push({
name: name,
email: email,
status: "not sent"
});
};
});
});

Dynamically Change Option Set Values in CRM

I am using CRM Online 2013.
I am trying to remove 3 values from an optionset under a certain condition.
The optionset has six options by default: they are listed at the top of my JS code below.
When I run my code, the correct amount of options appear; but they all say undefined.
Here is what I have at the moment:
var customer = 100000000;
var partner = 100000001;
var partnerCustomer = 100000002;
var customerAndBeta = 100000003;
var partnerAndBeta = 100000004;
var partnerCustomerAndBeta = 100000005;
function populateBetaOptionSet(beta) {
var options = Xrm.Page.getAttribute("intip_websiteaccess").getOptions();
var pickListField = Xrm.Page.getControl("intip_websiteaccess");
for(i = 0; i < options.length; i++)
{
pickListField.removeOption(options[i].value);
}
if (beta == false) {
pickListField.addOption(customer);
pickListField.addOption(partner);
pickListField.addOption(partnerCustomer);
}
pickListField.addOption(customerAndBeta);
pickListField.addOption(partnerAndBeta);
pickListField.addOption(partnerCustomerAndBeta);
}
This is being called from another function which is wired up to a separate field's onchange event. I am sure this is working correctly as I am getting the correct beta value through when it is called.
I am removing all the options before re-adding them to avoid duplicates.
Any idea what I am doing wrong here/or know of a better way of doing this?
Re-wrote your function to match the criterion. The option is an object with both text and value. This is why you see undefined (missing text);
So instead of
var customer = 100000000
it needs to be
var customer = { value : 100000000 , text : "Customer" };
The code below saves each option in global scope and uses it each time you call populateBetaOptionSet
function populateBetaOptionSet(beta) {
var xrmPage = Xrm.Page;
var pickListField = xrmPage.getControl("intip_websiteaccess");
var options = pickListField.getOptions();
//save all options
if (!window.wsOptions)
{
window.wsOptions = {};
wsOptions.customer = pickListField.getOption(100000000);
wsOptions.partner = pickListField.getOption(100000001);
wsOptions.partnerCustomer = pickListField.getOption(100000002);
wsOptions.customerAndBeta = pickListField.getOption(100000003);
wsOptions.partnerAndBeta = pickListField.getOption(100000004);
wsOptions.partnerCustomerAndBeta = pickListField.getOption(100000005);
}
//clear all items
for(var i = 0; i < options.length; i++)
{
pickListField.removeOption(options[i].value);
}
if (beta == false) {
pickListField.addOption(wsOptions.customer);
pickListField.addOption(wsOptions.partner);
pickListField.addOption(wsOptions.partnerCustomer);
}
pickListField.addOption(wsOptions.customerAndBeta);
pickListField.addOption(wsOptions.partnerAndBeta);
pickListField.addOption(wsOptions.partnerCustomerAndBeta);
}
Example use Xrm.Page.getControl(..).addOption :
var low = {value : 100000000, text : "Low"};
var medium = {value : 100000001, text : "Medium"};
var high = {value : 100000002, text : "High"};
var pickList = Xrm.Page.getControl("control_name");
var options = pickList.getOptions();
for (var i = 0; i < options.length; i++)
pickList.removeOption(options[i].value);
pickList.addOption(low);
pickList.addOption(medium);
pickList.addOption(high);

Web db - getting the data back

Here is the code
onError = function(tx, e) {
alert('Something unexpected happened: ' + e.message );
}
var webdb = openDatabase(dbName, '1.0' , dbDesc , dbSize);
var colourArray = new Array();
webdb.transaction(function(tx) {
tx.executeSql('SELECT * FROM colours',
[],
function(tx,rs) {
var ctn = rs.rows.length;
for (var i=0; i < ctn; i++) {
var row = rs.rows.item(i);
colourArray.push([row.id , row.title]);
}
},
onError);
});
/**
* the array looks like [[1,'red'],[2,'white'],[3,'black'] ...]
*/
var ColourStore = new Ext.data.ArrayStore({fields: ['key', 'val'],
data: colourArray});
The table "colours" contain colour name and hash code. And it was suppose to be use by a ExtJS Ext.data.ArrayStore then populate other drop downs on a massive form.
My problem is - I couldn't get the data back as an array. The variable "colourArray" is empty ... I know I hit some javascript closure , loop problem ... but just couldn't figure out how to get that inner loop value back. Try a lot of return -> return -> return function and more return. None of them works.
executeSQL is asynchronous. You need to create ColourStore in the function(tx,rs) callback function. If you need the data available globally, you can't init your app until executeSQL calls the callback function. Example:
Ext.onReady( function() {
var webdb = openDatabase(dbName, '1.0' , dbDesc , dbSize);
var colourArray = new Array();
var ColourStore;
webdb.transaction( function(tx) {
tx.executeSql('SELECT * FROM colours',
[], function(tx,rs) {
var ctn = rs.rows.length;
for (var i=0; i < ctn; i++) {
var row = rs.rows.item(i);
colourArray.push([row.id , row.title]);
}
//INIT YOUR APP HERE IN ORDER TO HAVE ACCESS TO colourArray values
ColourStore = new Ext....
YourApp.init();
},
onError);
});
}, this);

Categories