I have the following code in Angular, which is an attempt to allow for multiple instances of the same Controller on the same web page. The problem is, every time a new instance is created, it adds itself to the instance object, but erases the other properties (i.e, instances) on the instance object. I think this is a simple javascript syntax problem unrelated to Angular, but I can't figure it out. Any advice?
angular.module('mean.root').controller('ContentA1Controller', ['$scope', '$location', 'Global', 'Data', '$timeout', '$interval', 'Clients', 'Modals', '$element', function ($scope, $location, Global, Data, $timeout, $interval, Clients, Modals, $element) {
// Defaults
$scope.instances = { A: {}, B: {}, C: {}, D: {}, E: {}, F: {} };
// Methods
$scope.createNewInstance = function() {
var instance = $($element).closest('.content-container').attr('id');
// Add ID to content
$($element).find('.content').attr( "id", "contentA1instance"+instance );
Data.priceByDay(function(prices){
// Load Chart Data
$scope.instances[instance].data = [1,2,3,4,5];
// Log Instance Object
console.log($scope.instances);
So, when I add one instance to the controller, it works and I log:
$scope.instances = { A: { data: [1,2,3,4,5] }, B: {}, C: {}, D: {}, E: {}, F: {} }
Then when I add another instance, running the createNewInstance() method again, it will log:
$scope.instances = { A: {}, B: { data: [1,2,3,4,5] }, C: {}, D: {}, E: {}, F: {} }
var instance = $($element).closest('.content-container').attr('id');
....
$scope.instances[instance].data = [1,2,3,4,5];
it's choosing the id of the closest .content-container to the element where the controller is bound in the page. Since you said you've used the controller twice, one of those bindings is closer to .content-container#A and one is closer to .content-container#B. This looks like the code is "working as designed" but you may not have "designed what you meant".
I recommend using a different controller for each instance and keeping shared data in $rootScope, or better yet use one contoller for the whole page and having each instance operate on only one map item.
Related
I have a dropdown with a checkbox that its values is an object. (see attached)
Each time I select an object there is a watch which pastes this new values (which is an array of selected objects) to another object that display it in a directive (binding) -- tiles
$scope.$watch('skills', function(newVal, oldVal) {
if (newVal) {
console.log('changed!!! old value: ' + oldVal + ' new val ' + newVal);
if (newVal === undefined) {
return;
}
$scope.tiles = _.cloneDeep(newVal);
}
}, true);
angular.module('dsadad')
.component('tiles', {
templateUrl: 'tile.tpl.html',
bindings: {
tiles: '<',
onDelete: '&?'
},
controller: function ($state, _, $scope, $log, Utils, $translate, moment) {
'use strict';
var $ctrl = this;
}
});
I get: rangeError Maximum call stack size exceeded
for some reason one of the value of the array of selected objects is an array also instaed of an object as you can see....
Your issue is most likely here $scope.tiles = _.cloneDeep(newVal);
if newVal has a reference to itself, it will recursively try to clone, until you end up with a stackoverflow.
e.g. You probably have a circular object structure like this:
const objectB = {
id: 'B',
otherData: null;
};
const objectA = {
id: 'A',
otherData: objectB;
};
objectB.otherData = objectA;
_.deepClone(objectB) // This will blow up due to the circular reference
Otherwise, newVal is nested so deep that it causes the issue.
E.g.
const objectA = {
a: {
b: {
c: {
// etc. etc.
}
},
}
};
To fix this, you can either remove this circular reference from your data structure, or you can do a shallow copy if it suits your data needs by doing {...newVal}
I got a vue object like this:
var vm = new Vue({
el: '#app',
data: {
items: [],
index: 0
},
});
Inside items array i will push items like:
item1 = {
a: 1,
b: 'type',
c: '3.556'
}
...
itemN = {
a: n,
b: 'type',
c: '5.226'
}
then i will update one of the item's "c" property and i would like to set up a watcher that warn me as soon as one of this property changes.
EDIT: i also want to know witch item has changed
You can use deep watch, but... it does not provide ease way to determine which item has changed.
...
watch: {
items: {
handler: function (val, oldVal) {
},
deep: true
}
}
...
One of possible workarounds is mentioned in this answer,
idea behind this solution is to wrap each item in component and listen to event from component.
You can also store cloned items array and update that clone in watch handler, you can use that clone to filter item that has changed.
With the following setup I try to update data that is being held in a service and shared with the controller to assign it to the view.
In the example you can see 2 variables. One containing an array, another just a string.
What I don't understand is why is the array updated and consumed in the view and the string is not?!
JavaScript:
function fooService() {
var mystring = 'old string';
var myarray = [];
var updateArray = function(data) {
myarray.push(data);
};
var updateString = function(data) {
mystring = data;
};
return {
myarray: myarray,
mystring: mystring,
updateString: updateString,
updateArray: updateArray
}
}
function MainCtrl($scope, fooService) {
this.myarray = fooService.myarray;
this.mystring = fooService.mystring;
}
function fooDirective(fooService) {
function link(scope) {
fooService.updateArray(scope.vm.name);
fooService.updateString('new string');
}
return {
restrict: 'EA',
replace: true,
template: '<h2 style="color: {{vm.color}};">{{vm.name}}</h2>',
scope: {},
controller: 'MainCtrl',
controllerAs: 'vm',
bindToController: {
name: '#',
color: '#'
},
link: link
};
}
angular
.module('app', [])
.service('fooService', fooService)
.controller('MainCtrl', MainCtrl)
.directive('fooDirective', fooDirective);
HTML:
<div ng-app="app">
<div ng-controller="MainCtrl as vm">
{{vm.myarray}}
{{vm.mystring}}
<foo-directive data-name="Markus" data-color="red"></foo-directive>
<foo-directive data-name="Nemanja" data-color="green"></foo-directive>
<foo-directive data-name="Luke" data-color="blue"></foo-directive>
</div>
</div>
It might just be that I understand it the wrong way but services should hold data that is shared across the app right?
Here is the working example: http://jsfiddle.net/markus_falk/f00y3tL3/6/
services should hold data that is shared across the app right?
This is correct, however when you do
return {
myarray: myarray,
mystring: mystring,
// ...
}
you return new object (that will be your service instance) that has reference to myarray and copy of mystring. So since there is reference to myarray (all objects are passed as a reference) it updates in the service just fine. However, it will not for with the string (primitive types are not-mutable, passed as values), because service returns just a copy of it.
Instead of modifying a string (primitive value) use getter/setter approach.
I'm trying to figure out how to create a nested $resource. I have come up with the following, which seems to work..ish, but seems awfully wrong. Hoping someone can point me in the right direction.
My data structure is:
allData = [{
id:1,
info:'someinfo',
foo :'foobar',
bar : {...},
baz : [{...}, {...}, {...}]
},
{id:2, ...},
{id:3, ...}
];
I would like each object in allData to be a $resource object. I also want each object in the baz array to be a $resource object as well.
What I have come up with is:
var InfoService = angular.module('InfoServices', ['ngResource']);
// The inner resource
InfoService.factory('Baz', ['$resource',
function($resource) {
var baz = $resource('/server/baz', {});
// custom baz methods
baz.prototype.getBaz = function() {};
return baz;
}]);
// Outer resource
InfoService.factory('Info', ['$resource',
function($resource) {
var info = $resource('/server/info', {});
// custom data methods
info.prototype.getInfoStats = function() {};
return info;
}]);
// Array of resources.
InfoService.factory('AllInfo', ['$resource','Info', 'Baz',
function($resource,Info,Baz) {
var resource = $resource('/server/allinfo', {},
{ query : {
method:'get',
isArray:true,
transformResponse:function(data) {
var allinfo = JSON.parse(data);
for (var i=0;i<allinfo.length;i++) {
allinfo[i] = new Info(allinfo[i]);
for (var j=0;j<allinfo[i].baz.length;j++) {
allinfo[i].baz[j] = new Baz(allinfo[i].baz[j]);
}
}
return allinfo;
}
});
return resource;
}]);
Like I said..seems awfully wrong, what's the Angular way to achieving the above?
InfoService.factory('resources', function($resource) {
return {
id: $resource(...),
info: $resource(...),
//...and so on...
}
})
//example usage
InfoService.controller('ctrl', function(resources) {
this.id = resources.id.query();
})
If you want to make everything a resource, this is the way to go. But ask yourself...is it really necessary to make every attribute a separate resource? What about grouping in a resource all attributes that are strictly related? I ask because I am not sure of what these data rapresent to you.
var config = {
a: 'test',
b: {
c: // SOmeway to access config a value
}
}
IS there any way to access value from class b?
Neither of those things is a class. They're objects.
No, there's no way to access config.a where you have c: in your code, all within the same object literal. You can do it after the fact:
var config = {
a: 'test',
b: {
}
};
config.b.c = config.a;
Note that config.b.c will receive the value of config.a. If someone updates config.a later, config.b.c will not be updated.
If c were a function, you could access config.a from within it:
var config = {
a: 'test',
b: {
c: function() {
// You can use `config.a` here
}
}
};
There, each time you call c, you'll be using the current value of config.a.
Try this i found it in search in for javascript
this.base or this.base()