im trying to hook the battery manager object to a angular controller however the controller object doesnt seem to be updating when the promise by navigator.getBattery() complete. here is what i came up with
(function(){
var app=angular.module('appBattery',[]);
app.controller('batteryController',['$window',function($window){
this.bat={};
this.level=this.bat.level;
$window.navigator.getBattery().then(function(battery){
setBattery(battery);
});
function setBattery(battery){
this.bat=battery;
console.log(this.bat);
}
console.log(this.bat);
}]);
})();
with this html
<div ng-app='appBattery'>
<div id="battery-status-bar" ng-controller='batteryController as battery'>
<div class="battery">
<div class="power">
{{battery}}
<div class="level"></div>
</div>
</div>
<div class="percentage">{{battery.bat.level}}</div>
<div class="time">{{battery.bat.chargeTime +':'+battery.bat.dischargeTime}}</div>
</div>
</div>
it can also be found on jsfiddle here
You can keep your controller as syntax. Check out my updated fiddle at https://jsfiddle.net/fnnruzjw/1/. There were a couple of issues at hand:
You needed to use $scope.$apply.
$window.navigator.getBattery().then(function(battery){
$scope.$apply(function() {
setBattery(battery);
});
});
Your this was referring to the wrong this (I changed it to be vm as is a popular convention).
You were losing the reference to your battery object when you were reassigning it. I used angular.copy to keep the reference.
function setBattery(battery){
angular.copy(battery, vm.bat);
console.log(vm.bat);
}
Assign battery to $scope.bat (inject $scope in your controller also). And you will probably need to do $scope.$apply() too. Pretty much replace this with $scope.
Related
my controller is loaded multiple times in my AngularJS 1.5 code:
<div ng-repeat="conditionForMultipleRows">
<div data-ng-if="$first">
<div co-my-component></div>
</div>
</div>
export function coMyComponent(): ng.IDirective {
return {
template: coMyComponentTemplateHtml,
controller: 'MyComponentController',
controllerAs:'$ctrl'
}
}
export class MyComponentController{
state: MyStateClass;
static $inject = [someServices]
constructor(someServices) {
document.getElementById("myComponent").addEventListener("myEvent", (ev: CustomEvent) => {
doStuff()
}
The HTML Part is only called once, so there should be no issue. Only my controller is loaded multiple times.
The angular.module only loads the controller once and the directive only once, so there is no issue. Also there is no other place where the controller or webcomponent is called in the code.
I'm not very familiar with AngularJS so you can also point out other parts if you see something wrong here. Please refer to a source if it was resolved there. I didnt find any helpful answer
Thanks in advance guys
Each time your directive is instantiated, it will received a brand new controller.
The ng-repeat directive instantiates a template once per item from a collection.
In your case, if conditionForMultipleRows is an array having four items inside, you will instantiate four times the template
<div data-ng-if="$first">
<div co-my-component></div>
</div>
Each template instance gets its own scope and own controller, thus calling the constructor four times.
The answer for my issue here is:
<div ng-repeat="conditionForMultipleRows track by $index ">
<div data-ng-if="$first">
<div co-my-component></div>
</div>
</div>
With adding track by $index, the controller of my component only loaded once.
Also learned that in a ng-repeat it should always be added a "track by". There are only a few egde cases where this isnt required. Correct me if I'm wrong
For below code, using angularJS,
<script type="text/javascript">
angular.module('app').controller('add', ['$scope',function($scope) {
$scope.name = "Bonita Ln";
}]);
</script>
corresponding Javascript code to access $scope variable member name is,
<div ng-app="app" ng-controller="add">
<script type="text/javascript">
var dom_el = document.querySelector('[ng-controller="add"]');
var ng_el = angular.element(dom_el);
var ng_el_scope = ng_el.scope();
var name = ng_el_scope.name;
</script>
</div>
Below is the angular code, accessing ng-init variable name using angular expression,
<body ng-app="">
<div ng-init="name='Bonita Ln'">
{{name}}
</div>
</body>
How do I access the ng-init variable name using JavaScript?
you can do this by accessing variable $scope.name in your controller, but it has to be define inside your scope.
<div ng-init="name='Bonita Ln'">
{{name}}
<div ng-controller="MyCtrl">
Hello, {{name2}}!
</div>
</div>
angular.module('myApp',[]).controller('MyCtrl',function($scope) {
$scope.name2 = $scope.name;
})
this works, as you have define name in parent scope to controller, and is being inherited
but if for same controller html template will look like that:
<div ng-controller="MyCtrl">
<div ng-init="name='Bonita Ln'">
{{name}}
Hello, {{name2}}!
</div>
</div>
then it won't work, as variable was undefined when controller function was invoked
You could use it in exactly the same way, but you shouldn't.
I've downvoted your question because it is really really really bad practice, and I mean like every line of code you provided is bad practice.
I'm struggling trying to find out what you'd like to do, so I can't really provide you with better code to do so, but I can provide you with some links you must check out before continuing with whatever you're coding now.
Shaping up with Angular is a free codeschool course provided by the angular team, it is a really good course that will give you more insight on how to use angular than the phone-tutorial on the angular website.
Papa Johns Angular styleguide a great guide on how to write maintainable code.
thinkster.io a step by step guide to learn to master Angular
egghead.io a collection of good video tutorials
I know that my comment sounds quite harsh, but future you will thank you if you write more standardized Angular, and keep to a defined style-guide.
Also remember, in angular, your code should not know about the DOM, you don't need to specify eventListeners to DOM-elements, just use the appropriate directives like ng-click.
(yes I am aware that there can be exceptions to the rule)
I would like to save an object in a ngRepeat so that I can use that object in its children, like shown in this code:
<div ng-repeat="bar in foo.bar>
<div ng-repeat="qux in baz.qux" myvalue="{'item1':foo.var1, 'item2':qux.var2}">
<div ng-click="myFirstFunction(myvalue)"></div>
<div ng-click="mySecondFunction(myvalue)"></div>
</div
</div
The object I want to generate and then use is rather large and I'd rather not define it repeatedly for each ngClick directive.
I considered saving it into a scope variable but the object will change for each iteration of the ngRepeat.
Is there a directive or an other way that I can use to store this value for later use?
To avoid the repetition of what is probably a long variable definition, you can use the ngInit directive, whose content will be executed each time a corresponding element is created.
<div ng-repeat="bar in foo.bar>
<div
ng-repeat="qux in baz.qux"
ng-init="myValue = {'item1':foo.var1, 'item2':qux.var2 }"
>
<div ng-click="myFirstFunction(myValue)"></div>
<div ng-click="mySecondFunction(myValue)"></div>
</div>
</div>
However, a complex code in a template is rarely a good idea. Contemplate moving your logic inside a controller, as advised by the documentation:
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
You can just do the naive thing here and it'll work:
<div ng-repeat="bar in foo.bar>
<div ng-repeat="qux in baz.qux">
<div ng-click="myFirstFunction(foo, quz)"></div>
<div ng-click="mySecondFunction(foo, quz)"></div>
</div>
</div>
Angular will know the scope of the repeat when you click.
You could store it in local storage using ng-storage.
https://github.com/gsklee/ngStorage
This would allow you to store it, then use it anywhere in the application.
cookies, you also have ng-cookies
https://docs.angularjs.org/api/ngCookies
try this out! or cookieStorage
I've discovered that when I use ng-include it gets called far too often.
Every time you click one of the Nothing buttons or change Views, or type something in the input box, getView gets run several times. Up to 6 times when changing views. Basically doing anything that alters the $scope will generate a call to getView.
I've created a plunker to show the behavior I'm describing: plnkr.co
Is this normal behavior and is there any way to make it only run once? I'm worried I may be losing performance due to this.
MY CODE:
index.html
<body ng-app="includeExample">
<div ng-controller="ExampleController">
<div class="row">
<input type="text" ng-model="unrelated">
</div>
<div class="row">
<tabset>
<tab heading="View 1">
<ng-include src="getView('template1')"></ng-include>
</tab>
<tab heading="View 2">
<ng-include src="getView('template2')"></ng-include>
</tab>
</tabset>
</div>
</div>
</body>
Script.js
angular.module('includeExample', ['ngAnimate', 'ui.bootstrap'])
.controller('ExampleController', ['$scope',
function($scope) {
$scope.getView = function(filename) {
console.log("getView " + new Date());
return filename + ".html";
};
}
]);
Template1.html
Content of template1.html
<button type="button" class="btn btn-primary" ng-click="">Nothing</button>
Angular is calling your getView method each time a digest runs to make sure the value hasn't changed. This is part of the "magic" for how angular binds the model and the view together. If you look at the network tab for your development tools you will see that the template is not actually being retrieved by the browser each time the digest runs. Knowing that this is how the digest actually works - you should develop code accordingly running intensive operations in methods that are not directly bound to the view!
For more information on the digest itself see:
http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/
https://docs.angularjs.org/guide/scope (search for "Scope Life Cycle")
https://www.ng-book.com/p/The-Digest-Loop-and-apply/
Hope that helps!
Yeah, angularJS will go through getView() every time you do anything. A good first iteration would be to assign it to a json object instead of using a get method. Actually, it's better if you don't use any get type methods in the html. Then if you really want it to not change, you can remove the 2 way binding (Render value without data-binding).
So, I can change a model value from a child controller, but when the child controller is in ng-switch then it doesn't work, why? I created an example to demonstrate it.
One way to avoid this is to use the . in the model name, like bunnies.kills. Is this a bug or this is a feature ?
Using Angular 1.0.6
Using your code structure, in your child controllers you would need to change:
$scope.$parent.kills++;
to
$scope.$parent.$parent.kills++;
Explanation: MainCtrl's scope is the parent scope of SimpleParentCtrl, but the grandparent of Step1Ctrl and Step2Ctrl. As some others pointed out, ng-switch creates its own scope, and then your Step1Ctrl and Step2Ctrl each created a child scope of the ng-switch.
Note: Each time the 1 or 2 button is clicked, both the ng-switch and it's currently matched child controller get a new scope.
Also: In case you happen to be looking in the Angular source and wondering how the ng-switch directive creates its own scope without a scope property, the answer is that it does so manually in its link method via scope.$new(). The directives ng-include, ng-switch, ng-repeat, and ng-view all create new scope this way, either in the link method or the compile method's returned link function.
Resources:
https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
ng-switch creates its own child scope, which is why #sh0ber's answer is one way to get it to work. In general, models should be referenced in controller scopes (hence reference objects), and not be not primitives. So using a . is a "best practice".
This is not a bug, but it is not a feature either. This is the way JavaScript prototypal inheritance works with primitives.
I would take a slightly different approach to this problem.
Rather than use $scope.$parent, I would recommend you move all of your bunny killing logic into a shared service/model.
Also, I would try to avoid referencing parent views/controllers. Referencing the parent can make it difficult to reuse your code and can be painful to debug as the project grows. It is okay for a parent to know about it's children but a child should know little to nothing about it's parent.
Here is an updated Plunk: http://plnkr.co/edit/PLDbfU8Fu7m59A42qdR6?p=preview
HTML
<body ng-controller="MainCtrl">
<p>
Dead bunnies: <strong>{{Elmer.deadWabbits}}</strong>
</p>
<div ng-controller="SimpleParentCtrl">
<button ng-click="Elmer.killTheWabbit()">Kill from simple parent gun</button>
</div>
<hr>
<div ng-switch="" on="step">
<div ng-switch-when="first" ng-controller="Step1Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 1 tab gun</button>
</div>
<div ng-switch-when="second">
<div ng-controller="Step2Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 2 tab gun</button>
</div>
</div>
</div>
<hr>
<p>
<button ng-click="changeStep('first')">1</button> <button ng-click="changeStep('second')">2</button>
</p>
</body>
JS
angular.module('plunker', []).
service("Elmer", [function() {
this.deadWabbits = 0;
this.killTheWabbit = function() {
this.deadWabbits++;
};
}]).
controller('MainCtrl', function($scope, Elmer) {
$scope.Elmer = Elmer;
$scope.step = 'first';
$scope.changeStep = function(name){
$scope.step = name;
};
}).
controller('SimpleParentCtrl', function() {}).
controller('Step1Ctrl', function() {}).
controller('Step2Ctrl', function() {});
One way to avoid this is to use the . in model name, like bunnies.kills. Is this a bug or this is a feature ?
This has been explained numberous times : https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
and in mhevery's video