ng-if only works when referencing var direct from service, instead of var in controller scope - angularjs

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

Related

knockout push item to non-existing observableArray

From within a viewmodel I'm trying to dynamically push items to an observableArray. The ajax returns the data correctly.
html :
<li class="liTagulTagsChild" data-bind="click:$root.GetEmissions">/li>
javascript:
var TagDetail = function (di_titre, di_diffusion) {
this.di_titre = ko.observable(di_titre);
this.di_diffusion = ko.observable(di_diffusion);
}
var testA = [];
this.test = ko.observableArray(testA);
this.GetEmissions = function (c, event) {
var element = event.target;
var tag_id = element.getAttribute('tag_id');
$.AjaxAntiforgery({
url: 'Emission/GetDetailsByTagID/',
data: {
tag_id: tag_id
},
success: function (result) {
for (var i = 0; i < result.length; i++) {
var tD = new TagDetail(result[i].DI_TITRE, result[i].DI_DIFFUSION);
this.test.push(tD);
}
}
});
}
Problem: From within the GetEmissions function I cannot push items to this.test because this.test is null (not defined).
In other words,
this.test.push(tD);
fails. (null reference or not defined)
A solution anyone ?
In Javascript "this" doesn't mean what you think it means. Check out How to access the correct `this` context inside a callback?

Trouble with Controllers to manipulate an Object inside a Service

I have an object which should be accessible in many controllers.
This object is inside a Service, has default values and the controller might change those values later.
My problem is that my object inside the service keep values changed by controllers.
When a controller get the object, I want always that it takes the object with default values. (not with values previously modified by an other controller before...)
I have this inside my service :
this.myObject = {'item1' : 'something', 'item2' : 'other' , .....};
I know that it's not correct because of this.
So I tried to make a method like this :
this.createMyObject = function() {
var obj = myObject;
return obj;
}
And call createMyObject(); in my controllers but this doesn't work too.
I know that the solution might be obvious.
Thanks.
If what you want is a copy of myObject, what you want to do is :
var obj = angular.copy(myObject);
Because var obj = myObject; will just copy the reference of the object, not its content.
Object in Javascript are pass by reference unless copied or cloned. So when you are doing
this.createMyObject = function() {
var obj = myObject;
return obj;
}
The reference of myObject is getting assigned to obj hence, any change in obj will update the myObject as well.
Consider using angular.extend or angular.copy
this.createMyObject = function() {
var obj = {};
angular.copy(myObject, obj);
// or
// obj = angular.copy(myObject);
return obj;
}
Try the below solution:
Service Code:
.service('MyService', function() {
var myObject = {
'item1': '',
'items2': ''
};
/**
* Used to return copy of myObject with some default values
*/
this.createMyObject = function() {
return angular.copy(myObject);
};
this.alterMyObject = function() {
// #TODO here myObject can be used to edit directly
};
});
Note:
"=" operator between two object just used to assign reference of RHS obj to LHS. So any further changes with LHS object will be reflected to RHS obj also.

Create an AngularJS factory / object to hold an array

How can I update this code to hold an array of values? I want to hold FIELDNAME and the VALUE.
I want to set / add to the list by doing the following - add a value to the array/list.
userFilters.setData(' lastname', 'smith');
userFilters.setData(' firstname', 'bob');
userFilters.setData(' Mi', 'D');
And have the object hold an Array of
'lastname','smith'
'firstname','bob'
'mi','D'
App.factory('userFilters', [function () {
var data = {};
var getData = function (field) {
return data[field];
};
var setData = function (field, value) {
data[field] = value;
};
return {
getData: getData,
setData: setData
}
}]);
You could recreate this fairly simply without any need for the methods you are creating.
Javascript Objects are designed to do exactly what you are looking for here.
App.factory('userFilters', function() {
return {};
});
Rather than using a getter and setter, you could instead get and set values with square bracket accessors.
// setting properties
userFilters['lastname'] = 'smith';
userFilters['firstname'] = 'bob';
userFilters['Mi'] = 'D';
// getting properties
userFilters['lastname']; // 'smith'
userFilters['firstname']; // 'smith'
If you want to be able to have full control of what happens at get/set time, you could look at intercepting these calls with internal getters and setters, providing you know the property names before hand.
Finally, you could also wrap your own get and set functions around the object in order to hide it. However, this would make more sense as a Service.
App.service('userFilters', function() {
var store = {};
this.get = function(key) {
return store[key];
};
this.set = function(key, value) {
store[key] = value;
};
});
If it's important that your factory/service exposes an array then I would recommend sticking to using an object to store keys and values, but adding an array export method.
App.service('userFilters', function() {
var store = {};
this.toArray = function() {
var records = [];
return Object.keys(store).map(function(key) {
records.push([key, store[key]]);
});
};
this.get = function(key) {
return store[key];
};
this.set = function(key, value) {
store[key] = value;
};
});
If you want to loop through the properties, you can use a for-in loop.
for(var key in userFilters) {
var value = userFilters[key];
console.log(key, value);
}
You can also check whether there any keys at all, using the Object.keys method.
Object.keys(userFilters); // ['lastname', 'firstname', 'Mi']
This will return an array of all the keys in the object. If it has length 0, then you know it's empty.
In order to use an array for storage you will need to update both getData and setData methods and define data as an array []:
App.factory('userFilters', [function () {
var data = [];
var getData = function (field) {
for(var i=0; i<data.length; i+=2) {
if(data[i] == field) {
return data[i+1];
}
}
return null;
};
var setData = function (field, value) {
data = data.concat([field, value]);
};
return {
getData: getData,
setData: setData
}
}]);

Backbone Model change data in one instance affects another

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.

passing data to a collection in backbone

So I am trying storing product types from a json file before trying to add them to a collection but am getting some strange results (as in I dont fully understand)
on my router page i setup a variable for cached products as well as product types
cachedProductTypes: null,
productType : {},
products : {},
getProductTypes:
function(callback)
{
if (this.cachedProductTypes !== null) {
return callback(cachedProductTypes);
}
var self = this;
$.getJSON('data/product.json',
function(data)
{
self.cachedProductTypes = data;
callback(data);
}
);
},
parseResponse : function(data) {
result = { prodTypes: [], products: [] };
var type;
var types = data.data.productTypeList;
var product;
var i = types.length;
while (type = types[--i]) {
result.prodTypes.push({
id: type.id,
name: type.name,
longName: type.longName
// etc.
});
while (product = type.productList.pop()) {
product.productTypeId = type.id,
result.products.push(product);
}
};
this.productType = result.prodTypes;
console.log( "dan");
this.products = result.products;
},
showProductTypes:function(){
var self = this;
this.getProductTypes(
function(data)
{
self.parseResponse(data);
var productTypesArray = self.productType;
var productList=new ProductsType(productTypesArray);
var productListView=new ProductListView({collection:productList});
productListView.bind('renderCompleted:ProductsType',self.changePage,self);
productListView.update();
}
);
}
when a user goes to the show product types page it runs the showProductsType function
So I am passing the products type array to my collection
on the collection page
var ProductsType=Backbone.Collection.extend({
model:ProductType,
fetch:function(){
var self=this;
var tmpItem;
//fetch the data using ajax
$.each(this.productTypesArray, function(i,prodType){
tmpItem=new ProductType({id:prodType.id, name:prodType.name, longName:prodType.longName});
console.log(prodType.name);
self.add(tmpItem);
});
self.trigger("fetchCompleted:ProductsType");
}
});
return ProductsType;
now this doesnt work as it this.productTypesArray is undefined if i console.log it.
(how am I supposed to get this?)
I would have thought I need to go through and add each new ProductType.
the strange bit - if I just have the code
var ProductsType=Backbone.Collection.extend({
model:ProductType,
fetch:function(){
var self=this;
var tmpItem;
//fetch the data using ajax
self.trigger("fetchCompleted:ProductsType");
}
});
return ProductsType;
it actually adds the products to the collection? I guess this means I can just pass an array to the collection and do not have to add each productType?
I guess this means I can just pass an array to the collection and do not have to add each productType?
Yes, you can pass an array to the collection's constructor, and it will create the models for you.
As far as your caching code, it looks like the problem is here:
if (this.cachedProductTypes !== null) {
return callback(cachedProductTypes);
}
The callback statement's argument is missing this - should be return callback(this.cachedProductTypes).

Resources