Here I am using angular Tree for component. When the data is entering first time it is working fine.
But the data is coming from backend, then the adding row button is not adding.
Plunker
Here is my code.
$scope.newSubItem = function(scope) {
var nodeData = scope.$modelValue;
nodeData.items.push({
id: nodeData.items.length?(nodeData.items[nodeData.items.length-1].id)+1:nodeData.id * 10,
rowId: nodeData.rowId + '.' + ((nodeData.items.length?(parseInt(nodeData.items[nodeData.items.length-1].rowId.split('.').pop()))+1:1)),
items: []
});
};
The error is
TypeError: Cannot read property 'push' of null
at Scope.$scope.newSubItem (app.js:19)
at $parseFunctionCall (angular.js:12332)
at callback (angular.js:22949)
at Scope.$eval (angular.js:14383)
at Scope.$apply (angular.js:14482)
at HTMLAnchorElement.<anonymous> (angular.js:22954)
at HTMLAnchorElement.eventHandler (angular.js:3011)
Before pushing you need to initialize the array if its null,
nodeData.items = [];
$scope.newSubItem = function(scope) {
var nodeData = scope.$modelValue;
if(nodeData && nodeData.items ){
}
};
This is happening because when a node has no child elements you are not returning empty array in items from the server
Either add code on server side which adds an empty array in items
or
#Sajeetharan is right you need to add this code in your newSubItem function
$scope.newSubItem = function(scope) {
var nodeData = scope.$modelValue;
if(!nodeData.items)
nodeData.items =[];
nodeData.items.push({
id: nodeData.items.length?(nodeData.items[nodeData.items.length-1].id)+1:nodeData.id * 10,
rowId: nodeData.rowId + '.' + ((nodeData.items.length?(parseInt(nodeData.items[nodeData.items.length-1].rowId.split('.').pop()))+1:1)),
items: []
});
};
Related
I am running into a peculiar problem.
I have a my view of drop down list as this in HTML:
<div ng-controller='ConfigurableInputController as configurableInput'>
<select class="fixed-input"
ng-model="configurableInput.selectedCriteria"
ng-change="configurableInput.update()"
ng-options="criterion.selected as criterion.name for criterion in configurableInput.criteriaList">
<option value="">--Please select the comparison criteria--</option>
</select>
The above shows me 3 options in drop down list: Store, Channel and Permissions.
When ever I change the option from Store to Channel or Permissions or any combination the view changes (input boxes, certain labels etc.)
My update() method in controller does this:
configurableInput.update = function () {
configurableInput.permission = "";
configurableInput.id1 = "";
configurableInput.id2 = "";
configurableInput.items1 = "";
configurableInput.items2 = "";
configurableInput.defaultValueArray = [];
configurableInput.permissionsArr = [];
}
On selection of the drop down item, I do a ng-click on a button which retrieves information from Db.
There are 2 flavors of methods which are called on ng-click but the one giving me trouble is this one in the Service method:
StoreConfigurableService.$inject = ['$q', '$http', 'ApiBasePath'];
function ConfigurableService($q, $http, ApiBasePath) {
var service = this;
var defaultValueArr = [];
var permissionsArr = [];
var items1 = "";
var items2 = "";
service.retrievePermissions = function (criterion, id1, id2, rlNumber1, rlNumber2) {
$q.all([
$http.get(ApiBasePath + "/" + criterion + "/" + id1 + "/" + rlNumber1),
$http.get(ApiBasePath + "/" + criterion + "/" + id2 + "/" + rlNumber2)
])
.then(function (response) {
items1 = response[0].data;
items2 = response[1].data;
if (criterion === 'Permissions') {
var processed = false;
permissionsArr = makePermissionsArray(permissionsArr, items1, items2, processed);
}
})
.catch(function (errorResponse) {
console.log("Error: " + errorResponse);
throw errorResponse;
});
};
... so on
In my makePermissionsArray() I am doing this:
function makePermissionsArray(arr, items1, items2, processed) {
var map = items1.storePermissions;
var map2 = items2.storePermissions;
var map3 = items1.allPermissions;
for (var key in map) {
arr.push({
'code': key,
'description': map3[key],
'repStorePermission1': map[key],
'repStorePermission2': map2[key]
});
}
}
I get this permissionsArr back to controller through this:
service.getPermissionsArr = function () {
return permissionsArr;
};
NOTE that 'arr' in the argument of method makePermissionsArray() is not initialized because I am getting it by passing permissionsArr in the argument.
Now how the flow goes is:
First I select Store from drop down list, click on button to retrieves configurables for the store. This gives me correct response.
Then I select 'Permissions' from drop down list, click on button to retrieve permission configurables from Db, it gets stuck and does not get me any response.
I thought I am not clearing the arrays correctly in update() method and after some long search I found and changed these array initializing from:
configurableInput.defaultValueArray = [];
configurableInput.permissionsArr = [];
to
configurableInput.defaultValueArray.length = 0;
configurableInput.permissionsArr.length = 0;
in my update() method.
Now what happens is this:
First I select Store from drop down list, click on button to retrieves configurables for the store. This gives me correct response.
Then I select 'Permissions' from drop down list, click on button to retrieves permission configurables from Db, it gives me correct response.
I select Store from drop down list again, click on button and I get correct response.
But when I again select 'Permissions' from drop down list, click on button, it gives me this error - TypeError: Cannot read property 'push' of undefined
Error I get is at this point:
arr.push({
'code': key,
'description': map3[key],
'repStorePermission1': map[key],
'repStorePermission2': map2[key]
});
I am literally tearing my hair out because of this problem. Can someone please help me what's wrong?
In my controller I have created $scope function which returns an array. That function uses data from view with ng-model directive and everything is working fine (when I call this function with the expression in the view I get returned data), except when I want to use that returned array in my controller, I simply get nothing.
Plunker
JS code:
app.controller('CalculatorCtrl', function($scope) {
$scope.deparature = {
'lat_1': '',
'lat_2': '',
'lat_3': '',
'long_1': '',
'long_2': '',
'long_3': ''
}
$scope.arrival = {
'lat_1': '',
'lat_2': '',
'lat_3': '',
'long_1': '',
'long_2': '',
'long_3': ''
}
$scope.select = {
'departure_1': '',
'departure_2': '',
'arrival_1': '',
'arrival_2': ''
}
$scope.depCoordinate = function() {
var cordLat = $scope.deparature.lat_1 + '/' + $scope.deparature.lat_2 + '/' + $scope.deparature.lat_3 + '/';
if ($scope.select.departure_1 === 'S') {
var temp = '-' + cordLat;
var CordLatS = Dms.parseDMS(temp);
} else {
var CordLatS = Dms.parseDMS(cordLat);
};
var cordLong = $scope.deparature.long_1 + '/' + $scope.deparature.long_2 + '/' + $scope.deparature.long_3 + '/';
if ($scope.select.departure_2 === 'W') {
var temp = '-' + cordLong;
var CordLongW = Dms.parseDMS(temp);
} else {
var CordLongW = Dms.parseDMS(cordLong);
};
return [CordLatS.toFixed(5), CordLongW.toFixed(5)];
}
var cord = $scope.depCoordinate();
var lati = cord[0];
console.log(lati);
/*I need to pass the array result to var p1. How I can do that?*/
$scope.distance = function () {
var p1 = new LatLon(50.06632, -5.71475);
var p2 = new LatLon(58.64402, -3.07009);
return p1.distanceTo(p2);
};
});
First, there are some things in your plunker that should be relevant here:
NEVER do this: <p>Test: {{ depCoordinate() }}</p>.
It will call your function several times causing several problems.
Don't use ng-init() for this kind of thing: <div class="list" ng-init="initWaves()">. It should be used only in specific cases (i.e. ng-repeat), check the docs.
Then.. If I understood your question well, you want to use the returned array in your view, so, if I'm correct, here is how-to:
$scope.array = [CordLatS.toFixed(5), CordLongW.toFixed(5)];
return $scope.array;
And in your view just use it:
{{ array }} // or whatever you want to do with it.
What do you mean that "I get nothing"? As far as I see, you wrote $scope.depCoordinate(); on init state of controller. When your controller is executed, that function will work immediately.
I think problem is that initial state. Because you don't have any data on first creation of controller. All of your variables are ''.
So, what you want to do exactly? You can use ngChange directive on your view layer with element which are updating that variables (deparature, arrival, select) with a function that is for watching updates. Then you need to write a function into your controller as follows;
$scope.watchChanges = function() {
var cord = $scope.depCoordinate();
var lati = cord[0];
};
I am trying to understand why my ng-if statement doesn't work when I reference a local variable in my controller that is assigned to a value from a service, but it works properly if assigned directly to the value from that service.
For example, this works:
<div class="map" ng-if="interactiveMap.mapService.esriLoaded">
<esri-map id="map1"
map-options="interactiveMap.mapOptions"
load="interactiveMap.load"
register-as="interactiveMap">
</esri-map>
</div>
with the following controller:
angular.module('tamcApp')
.controller('InteractivemapCtrl', function (map, config) {
var self = this;
self.map = {};
self.mapService = map;
self.mapOptions = {
basemap: 'mcgiStreet',
extent: config.globals.initialExtent,
sliderStyle: 'small'
};
self.load = function(){
map.getMap('interactiveMap').then(function(thisMap) {
console.log(thisMap);
self.map = thisMap;
});
};
});
But if I were to assign the "esriLoaded" var to a local var in the scope, like this:
<div class="map" ng-if="interactiveMap.esriLoaded">
<esri-map id="map1"
map-options="interactiveMap.mapOptions"
load="interactiveMap.load"
register-as="interactiveMap">
</esri-map>
</div>
Controller here:
angular.module('tamcApp')
.controller('InteractivemapCtrl', function (map, config) {
var self = this;
self.map = {};
self.esriLoaded = map.esriLoaded;
self.mapOptions = {
basemap: 'mcgiStreet',
extent: config.globals.initialExtent,
sliderStyle: 'small'
};
self.load = function(){
map.getMap('interactiveMap').then(function(thisMap) {
console.log(thisMap);
self.map = thisMap;
});
};
});
Then it doesn't work. The value for "esriLoaded" is always false (which is the default value for esriLoaded). It's like it isn't updating the value of self.ersiLoaded when the value gets updated in the "map" service. Here is the code for the "map" service, just in case folks need it to answer this question.
angular.module('tamcApp')
.service('map', function (config, esriLoader, esriRegistry, esriMapUtils) {
// AngularJS will instantiate a singleton by calling "new" on this function
var self = this;
self.esriLoaded = false;
self.lazyload = function() {
// Make a call to load Esri JSAPI resources.
// A promise is provided for when the resources have finished loading.
esriLoader.bootstrap({
url: config.globals.esriJS
}).then(function() {
// Set Loaded to be true
self.esriLoaded = true;
// DEFINE CUSTOM BASEMAP USED BY ALL MAPS
esriMapUtils.addCustomBasemap('mcgiStreet', {
urls: ['http://myhost.com/arcgis/rest/services/BaseMap/StreetMap/MapServer'],
title: 'MCGI Street Map',
thumbnailurl: ''
});
});
};
if (!self.esriLoaded) {
self.lazyload();
}
self.getMap = function(id){
return esriRegistry.get(id);
};
});
That is actually not because of angular, but because of JavaScript. map.esriLoaded is a boolean value, a primitive and thus not an object, which leads to your local self.esriLoaded not becoming a reference (as only objects can be referenced), but just a plain copy of the boolean value contained in map.esriLoaded.
A short example to make it more clear:
//Primitive
var a = 5; //primitive
var b = a; //b just copies the value of a
a = 6; //This will change a, but not b
conosle.log(b); //will print 5
//Object
var a = { someValue: 5 }; //a is now a reference to that object
var b = a; //b also becomes a reference to the object above
a.someValue = 1337; //will change the object a is referencing, thus also
//changing the object b is referencing, as its the same object
console.log(b.someValue); //will print 1337
I am using angular.forEach to iterate over an array and for each value in the array, I am using a GET/ to retrieve it's name (array contains key), then push that name into another array. This new array is being used in a dropdown box (ngOptions).
The issue I am having is that the drop down is being created before the GET/ responses have arrived, so it just displays blank options, then when they arrive it displays all options in each drop down.
I have the following code:
angular.forEach($scope.pots, function(value, key) {
$scope.selections = [];
angular.forEach(value, function(val, ky) {
if (ky === "selections") {
angular.forEach(val, function(v, k) {
$http.get('/selections/' + v).then(function(response) {
var response = response.data;
$scope.selection = response[0];
$scope.selectionName = $scope.selection.name;
$scope.selections.push({name : $scope.selectionName});
value["selectionNames"] = $scope.selections;
})
})
}
})
value["selectionNames"] = $scope.selections;
console.log(value);
So I am iterating over a list of pots which contains a list of selections, using the GET/ to get a list of names based on the list of selections and finally pushing that into the pot's scope. I am resetting the selection after each pot.
What's causing the issue is that reponses all come at once, and they bypass the $scope.selections = []; code, so it just pushes into each pot the complete array from all three pots.
Any tips/advice please? Thank you.
So looks like I've fixed it... Can someone confirm that this is good practice please? I'm basically recreating $scope.selections each time I iterate based on the selectionNames value in the pot (value).
angular.forEach($scope.pots, function(value, key) {
$scope.selections = [];
angular.forEach(value, function(val, ky) {
if (ky === "selections") {
angular.forEach(val, function(v, k) {
$http.get('/selections/' + v).then(function(response) {
$scope.selections = [];
var response = response.data;
$scope.selection = response[0];
$scope.selectionName = $scope.selection.name;
var currentSelections = value.selectionNames;
angular.forEach(currentSelections, function(csV, csK) {
$scope.selections.push(csv);
})
$scope.selections.push({name : $scope.selectionName});
value["selectionNames"] = $scope.selections;
})
})
}
})
value["selectionNames"] = $scope.selections;
console.log(value);
I just had a weird bug when using Backbone.Model
so I have the model declaration something like:
var MyMode = Backbone.Model.extend({
defaults:{
'someList': []
},
initialize: function(){
_.bindAll(this, 'toString', 'castFromString');
},
toString: function(){
return this.get('hierarchyNameList').join('+');
},
castFromString: function(str){
var strArray = [], index = 0;
if (str) {
strArray = str.split('+');
}
for (index = 0; index < strArray.length; index++){
this.get('hierarchyNameList').push(strArray[index]);
}
}
});
then I tried to test it
(function () {
'use strict';
var assert = function(condition, message) {
if (condition !== true) {
throw message || "Assertion failed";
}
};
var mA = new MyModel({'someList': ['a','b','c']});
var mB = new MyModel();
mB.castFromString('a+b+c');
//I have a added a equals function to array prototype and tested it
assert(mA.get('someList').equals(mB.get('someList'))); //true
var mC = new MyModel(mA.toJSON()); //behaves as expected
var mD = new MyModel(); //for some reason its list already contains the list from B
mD.castFromString(mB.toString()); //since castFromString used push, now B and D both has array of length 6
assert(mC.equals(mA)); //success
assert(mC.equals(mD)); //fail, mc has arrayLength 3, mD has 6
}).call(this);
The actual code is more complicated than this, but I think this is where I am probably doing something wrong, any suggestion on why this would happen? Thanks in advance!
The problem is with your defaults
defaults:{
'someList': []
},
objects in JavaScript are passed by reference not by value. It means that all instances, for which you didn't explicitly specified someList value will share array created in defaults definition above. To avoid it you can define your defaults as a function:
defaults: function () {
return { 'someList': [] };
},
This will create new array for every instance of MyModel, so they won't share the same array.