AngularJS Object [object Object] has no method 'appendChild' - angularjs

I am trying to create a simple bar chart, but when appending the bars to the DOM I get this error
Object [object Object] has no method 'appendChild'
$rootScope.drawChart = function (data,selector,padding){
var max = Math.max.apply(Math, data);
var chart = angular.element(document.getElementById("chartxx"));
var barwidth = ((chart.offsetWidth-(data.length-1)*padding-(data.length)*10)/data.length);
var sum = data.reduce(function(pv, cv) { return pv + cv; }, 0);
var left = 0;
for (var i in data){
var newbar = document.createElement('div');
newbar.setAttribute("class", "bar");
newbar.style.width=barwidth+"px";
newbar.style.height=((data[i]/max)*100)+"%";
newbar.style.left=left+"px";
console.log(chart);
angular.element(document.getElementById("chartxx")).appendChild(newbar);
left += (barwidth+padding+10);
}
}
var values = [85,95,120,100,200,200,230,230,60,320,23,3433,434,45,23,23];
$rootScope.drawChart(values,"#chartxx2",5);
<div class="wrapperx2">
<div id="chartxx"></div>
</div>

appendChild() is a method inherent to native HTML objects, aka the result of document.getElementById. When you give that HTML object to angular.element it becomes a jQuery Object. jQuery objects have a similar method called append()
So you can do document.getElementById("chartxx").appendChild(newbar) or angular.element(document.getElementById("chartxxx")).append(newbar).
So that should answer your question but I can't help but ask myself : why would you do something like that when you're using AngularJS ?
Edit
Ok so here's a very poorly achieved version of what I would have done in your place (if I was ABSOLUTELY unable to use an external library, because using an external library for charts is what I would normally do). I suggest you see and try to understand how the ng-repeat works here and try to apply it to your case.

angular.element doesn't contain method appendChild
angular.element doc
You can try:
chart.append(newbar);

Related

Notable to link javascript object and angularjs in controllers

I am trying to develop a mobile application in which i am getting JSON object using javascript page main.js,now I am trying to print the object using angualjs Controllers,but could not find any way.CAn anyone help me out on this?
function written in Main.js`
function getViewColumnsSuccess(result){
var httpStatusCode = result.status;
if (200 == httpStatusCode) {
var invocationResult = result.invocationResult;
var isSuccessful = invocationResult.isSuccessful;
if (true == isSuccessful) {
var result = invocationResult.text; //var FinalCol=reult;
} else {
alert("Error. httpStatusCode=" + httpStatusCode); } } }
The var result I want to get in DemoController in another page
app.controller('tableCtrlNew', function($scope,$http) { });
First controllers are not for print data, for that you should dataBinding, in views.
For Example:
In your controller you have this var;
$scope.name="John Doe";
So, then to print that in some view, you shoud print that in some view that has that scope, so to print in one view(html) that is simple like:
<span class="labelName"> {{name}}</span>
It is data-binding, with this you automatically print data from a controller into a view, but remember, it must be in the same scope to works.
Regards.
Assign Result to Window object
window.result = invocationResult.text;
In controller you can use $window
app.controller('tableCtrlNew', function($scope,$http,$window) {
console.log($window.result);
});

ngrepeat does not iterate $ attribute

I tried filtering with an object and to print out the filters via ng-repeat = (key, value) in object.
As I tried various filters, I saw that ng-repeat does not seem to work with the object's $ attribute, which is pretty useful if you are filtering.
Is there a possibility to show all attributes of the filtering objects automatically even if you use $
This link shows it doesn't seem to work with objects starting with $
$scope.testObj = {};
$scope.testObj.test = 'test';
$scope.testObj.$ = '$';
$scope.testObj.$test = '$test';
<div ng-repeat = "(key, value) in testObj">
<p>{{key}}: {{value}}</p>
</div>
AngularJS doesn't support it yet. There is an open issue on Github.
However you can make it work with a little code:
app.controller('MainCtrl', function($scope) {
var getProperties = function(input){
var result = [];
for(var propertyName in input) {
result.push({key : propertyName, value : input[propertyName]});
}
return result;
};
$scope.testObj = {};
$scope.testObj.test = 'test';
$scope.testObj.$ = '$';
$scope.testObj.$whatever = '$whatever';
$scope.testObjProperties = getProperties($scope.testObj);
});
Then display it in your view:
<div ng-repeat="property in testObjProperties">
<p>{{property.key}} : {{property.value}}</p>
</div>
Here's a working plunk : http://plnkr.co/edit/LFrfLcpoOg0ScEY89p25?p=preview
Looks like ng-repeat filters out object properties that begin with $.
This is from the source:
for (var itemKey in collection) {
if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {
collectionKeys.push(itemKey);
}
}
This is most likely due to the fact that Angular uses $ to indicate code that is internal to the Angular library.
It seems this will only occur if you are using ng-repeat over an object.
I see them being disallowed specifically in the code:
https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L341
There are no comments around, and I agree this looks more than a bug than a feature, to me. Isn't that what marking properties as non-enumerable and Object.keys is for?
Browser compatibility hell, may be the reason, as always.

How to append a new value to an item within an array in Firebase?

Within Firebase, I have a list of 'ideas.' If a user presses a button associated with the idea, I'd like a value to be appended to that idea under an attribute called 'newValue.'
For example, the below html, uses ng-repeat to show the array of ideas and creates an associated button called 'Append Value.' I want a new value to be appended to the idea's attribute called 'newValue' every time a user presses 'Append Value.'
<body ng-controller="ctrl">
<table>
<tr class="item" ng-repeat="(id,item) in ideas">
<td>{{item.idea}}</td>
<td><input ng-model="newValue"></td>
<td><button ng-click="ValueAppend(id,newValue)">Append Value</button></td>
</tr>
</table>
</body>
Below is my attempt to create this function.
var app = angular.module("app", ["firebase"]);
app.factory("Ideas", ["$firebase", function($firebase) {
var Ref = new Firebase('https://crowdfluttr.firebaseio.com/');
var childRef = Ref.child('ideas');
return $firebase(childRef).$asArray();
}]);
app.controller("ctrl", ["$scope","Ideas", function($scope,Ideas) {
$scope.ideas = Ideas;
$scope.idea = "";
$scope.ValueAppend = function (id,newValue) {
var URL = "https://crowdfluttr.firebaseio.com/ideas/" + id + "newValue";
var IdeaRef = new Firebase(URL);
var IdeaData = $firebase(IdeaRef);
$scope.IdeaAttributes = IdeaData.$asArray();
$scope.IdeaAttributes.$add({
newValue: newValue,
timestamp: Date.now()
});
};
}]);
See my codepen for my working example: http://codepen.io/chriscruz/pen/PwZWKG
More Notes:
I understnad that AngularFire provides $add() and $save() to modify this array, but how could I use these methods so that I can add a new 'string' under an item in an array.
I'm not sure if these are your problems, but they are two typoes of mistakes in the code above and the codepen: typos and conceptual.
Typos
You forgot to inject $firebase into the controller, which leads to:
"ReferenceError: $firebase is not defined"
Solution is simply of course:
app.controller("ctrl", ["$scope","Ideas", "$firebase", function($scope,Ideas,$firebase) {
In addition you seem to be missing a slash before newValue, which means that you're trying to create a new idea instead of adding the value to an existing one. Solution is simple again, add a slash before newIdea as in:
var URL = "https://crowdfluttr.firebaseio.com/ideas/" + id + "/newValue";
If you find yourself making this mistake more often, you might be better server by the child function. Although it typically is a bit more code, it lends itself less to this typo of typo. Creating the ref to the newValue node becomes:
var URL = "https://crowdfluttr.firebaseio.com/ideas/";
var IdeaRef = new Firebase(URL).child(id).child("newValue");
Conceptual
With those trivial typos out of the way, we can focus on the real problem: which is easiest to see if you console.log the URL that you generate:
https://crowdfluttr.firebaseio.com/ideas/0/newValue
Yet if you look up the same data in the Firebase forge (by going to https://crowdfluttr.firebaseio.com/ideas/ in your browser), you'll see that the correct URL is:
https://crowdfluttr.firebaseio.com/ideas/-JbSSmv_rJufUKukdZ5c/newValue
That '0' that you're using comes from the id and it is the index of the idea in the AngularJS array. But it is not the key that Firebase uses for this idea. When AngularFire loads your data with $asArray it maps the Firebase keys to Angular indexes. We need to perform the reverse operation to write the new value to the idea: we need to map the array index (in id) back to the Firebase key. For that you can call [$keyAt(id)][1]. Since you keep the array of ideas in Ideas, it is simply:
var URL = "https://crowdfluttr.firebaseio.com/ideas/";
var IdeaRef = new Firebase(URL).child(Ideas.$keyAt(id)).child("newValue");
So the controller now becomes:
app.controller("ctrl", ["$scope","Ideas", function($scope,Ideas) {
$scope.ideas = Ideas;
$scope.idea = "";
$scope.ValueAppend = function (id,newValue) {
var URL = "https://crowdfluttr.firebaseio.com/ideas/";
var IdeaRef = new Firebase(URL).child(Ideas.$keyAt(id)).child("newValue");
var IdeaData = $firebase(IdeaRef);
$scope.IdeaAttributes = IdeaData.$asArray();
$scope.IdeaAttributes.$add({
newValue: newValue,
timestamp: Date.now()
});
};
}]);
I quickly gave it a spin in your codepen and this seems to work.

How do I loop through the children of a Firebase instance

I want to know how to loop through the children of everyone. I'm using Firebase and AngularJS.
My firebase object looks like:
To me it looks like a dictionary, so from Getting a list of associative array keys I have tried
syncData('everyone').$bind($scope, 'everyone').then(function() {
var keys = $scope.everyone.$getIndex();
for (var key in $scope.everyone) {
console.log("key : " + key + " value : " + $scope.everyone[key]);
}
});
The log does contain the child objects, but it also includes all the methods. Like so
... Before this line is all the other methods.
key : $on value : function (a,c){if("loaded"==a&&b._loaded)return b._timeout(function(){c()}),void 0;if(!b._on.hasOwnProperty(a))throw new Error("Invalid event type "+a+" specified");b._on[a].push(c)} controllers.js:58
key : $off value : function (a,c){if(b._on.hasOwnProperty(a))if(c){var d=b._on[a].indexOf(c);-1!==d&&b._on[a].splice(d,1)}else b._on[a]=[];else b._fRef.off()} controllers.js:58
key : $auth value : function (a){var c=b._q.defer();return b._fRef.auth(a,function(a,b){null!==a?c.reject(a):c.resolve(b)},function(a){c.reject(a)}),c.promise} controllers.js:58
key : $getIndex value : function (){return angular.copy(b._index)} controllers.js:58
key : -JH45WOOAtnZfUZkrJb1 value : [object Object] controllers.js:58
key : -JH45YdfwptGv3y6UqyV value : [object Object] controllers.js:58
key : -JH45_zxptV_dmibyGzL value : [object Object]
Is there a way I can get just the children?
I'm doing this because my code was designed to use an array, but Firebase discourage using arrays (for values that multiple people could change). So I'm trying to loop through the firebase dictionary and copy the objects into an array on the client side. So I don't have to change too much of my code.
UPDATE: As of AngularFire 0.8.x, one can use $asArray() to obtain a sorted array of the records and this answer is no longer necessary
The correct way to iterate values in an angularFire object is by using $getIndex(). You have this in your code above, but did not utilize it in the for loop.
Since you are already using the angularFire lib (syncData is the angularFire-seed service that uses angularFire), there is no need to worry about calling $apply() or any of the other complexities of coaxing data into Angular detailed in the previous answer (which is a good response for a raw Firebase/Angular implementation).
Instead, just change your for loop to iterate the keys instead of the angularFire instance:
syncData('everyone').$bind($scope, 'everyone').then(function() {
var keys = $scope.everyone.$getIndex();
// utilizing Angular's helpers
angular.forEach(keys, function(key) {
console.log(key, $scope.everyone[key]);
});
// or as a for loop
for(var i=0, len = keys.length; i < len; i++) {
console.log(keys[i], $scope.everyone[keys[i]]);
}
});
To utilize the object in the DOM, use ng-repeat:
<ul>
<li ng-repeat="(key,user) in everyone">{{key}}, {{user|json}}</li>
</ul>
The way I do it is pretty simple, you just push all the children into an array when they arrive like this.
Everyone.on('child_added', function(snap) {
implementLogic(snap.val());
$scope.$apply();
});
function implementLogic(person) {
$scope.everyone.push(person);
if (something) {
$scope.todaysPeople.push(person);
}
if (something else) {
$scope.peopleToTest.push(person);
}
...
}
That leaves you with an array of the child objects you want.
Use ng-fire-alarm with collection: true like this:
angular.module('demo', ['ng-fire-alarm']).controller('IndexCtrl', IndexCtrl);
function IndexCtrl ($scope) {
var everyone = new Firebase(URL).child('everyone');
everyone
.$toAlarm({collection: true}) // will transform object into native array
.$thenNotify(function(everyones){ // notify you on ANY changes
$scope.everyones = everyones;
});
}

ExtJs - Get element by div class?

How do I get the ExtJs component object of a Div by class name?
Say my div is:
<div class="abc"></div>
How do I get the ExtJs object of this Div to perform e.g. getWidth()
Note: There is no id given.
What I tried:
Ext.select(".abc")
Ext.query(".abc")
Edit:
Error:
Object has no method 'getWidth'
<div id="main">
<div class="abc"></div>
</div>
var d = Ext.get( "main" );
d.query('.abc').getWidth();
Use
var dom = Ext.dom.Query.select('.abc');
var el = Ext.get(dom[0]);
This will return an Ext.Element. You can then use ext methods on el like so
el.on('click', function(){}, scope);
or
el.getWidth()
I believe you mean Ext.dom.Element (not ExtJs component object). If yes try this code
var d = Ext.get( "main" );
alert(d.down('.abc').getWidth());​
demo
For Ext4 and up you could use the following:
Ext.ComponentQuery.query('panel[cls=myCls]');
And you will get and array of this components
source: http://docs.sencha.com/ext-js/4-1/#!/api/Ext.ComponentQuery
Ext.select('abc');
This method works in EXT 4 also.
In your example, just remove the dot when calling the string of the class name.

Resources