UPDATE: angular 1.3.0-rc4 removed $scope.this see commit
Each instance of a $scope has a property named this that points back to itself.
Currently (1.2.0rc1) it's not prefixed with $(public/protected) or $$(internal) so it doesn't hint that it's an angular specific property.
What is the use case for it?
This question had me grepping all through the codebase for an explanation; I finally got a hint from an old test.
Since AngularJS expressions are evaluated in the context of a scope, the scope needs to have a property called this that refers to itself so that expressions that contain this work. Take the following example:
<div ng-controller="FirstController">
`this.num` (with normal scope): {{this.num}}
</div>
<div ng-controller="SecondController">
`this.num` (with scope.this removed): {{this.num}}
</div>
app = angular.module('myApp', []);
app.controller('FirstController', function($scope) {
$scope.num = 10;
});
app.controller('SecondController', function($scope) {
delete $scope['this'];
$scope.num = 10;
});
The second example does not work; see http://jsfiddle.net/BinaryMuse/mzbpz/ for a demonstration.
Related
I would like to be able to select a button using querySelector and set an attribute of "ng-click=doSomething()"
I have tried selecting the button and then setAttribute("ng-click", "doSomething()") but its not working
my DOM:
<body>
<div ng-app="myApp" ng-controller="mainCtrl">
<button id="myBtn">click Me</button>
</div>
<script src="./js/app2.js"></script>
</body>
my javascript:
(function() {
"use strict";
angular.module("myApp", []).controller("mainCtrl", mainCtrl);
/** #ngInject */
function mainCtrl($scope) {
init();
function init() {
$scope.doSomething = () => {
console.log("doing something");
}
let btn = document.querySelector('#myBtn');
btn.setAttribute("ng-click", "doSomething()");
}
}
})();
when I click the button it should console log something.
Generally speaking, if you dynamically add "AngularJS-ified" stuff to a document after it's created - such as dynamically creating <button> elements and then adding ng-click attributes to them - those elements will neither be tracked by watchers, nor be part of the normal digest cycle. So, for example, consider the following simple example:
const myApp = angular.module('stuff', [])
.controller('stuff-cont', function($scope) {
const targ = document.querySelector('#target');
for (let i = 0; i < 10; i++) {
let newBtn = document.createElement('button');
newBtn.setAttribute('ng-click', 'sayRandNum()');
newBtn.innerText = `Button ${i}`
targ.append(newBtn);
}
$scope.sayRandNum = () =>{
alert('Your random number is '+Math.ceil(Math.random()*100));
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app='stuff' ng-controller='stuff-cont'>
<div id='target'>
</div>
The buttons above are clickable, they have an appropriately "structured" ng-click, but they <i>don't trigger</i>!
</div>
Here, we're (for some reason...) creating 10 nearly-identical buttons. However, because of when we built these ng-click'ed buttons (namely, after the initial compilation phase), and specifically when we added the ng-click attributes (also after the initial compilation phase), the buttons are effectively not "known" to the AngularJS cycle".
Looked at another way, when AngularJS is first "loaded" on a page, it first walks through the HTML on that page, and looks for any databinds ({{likeThis}}; we'll ignore these for now) or directives. ng-click, ng-repeat, and other Babbys First AngularJS stuff are just standardized directives, so they're part of that "looking for directives" procedure. When AngularJS finds said directives, it says "Okay, you've got an ng-click on this element; I'll keep an eye on that".
To actually add new AngularJS-ified elements - or add AngularJS behavior to existing elements, as I believe is more the case with you - you'll need to use the $compile function, which basically says "hey, AngularJS! I made a new thing and want you to watch it!"
This SO answer -- Working with $compile in angularjs has a pretty decent explanation of how to use the $compile function.
(function() {
"use strict";
var btn = document.querySelector('#myBtn');
btn.setAttribute("ng-click", "doSomething()");
angular.module("myApp", []).controller("mainCtrl", mainCtrl);
function mainCtrl($scope){
$scope.doSomething = function(){
alert('abc');
}
}
angular.bootstrap(document, ['myApp']);
})();
Please check the JSFiddle , the difference is you have to modified the html before angular bootstrapped so your modified html and js code can be compiled properly. Here is a AngularJS Developer Guide - Bootstrap with more infomation of angularjs bootstrap
I have a simple angular snippet
<div ng-app="myApp" ng-controller="myCtrl">
<input ng-model="name">
<h1>My name is {{name}}</h1>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.name = "John Doe";
$scope.age = "26";
$scope.height = "5.9";
$scope.gender = "M";
});
</script>
As you can see I have 4 variables declared in my myCtrl controller
$scope.name = "John Doe";
$scope.age = "26";
$scope.height = "5.9";
$scope.gender = "M";
Is there a way to programmatically list of all the declared $scope variables of a specific angular controller?
Why am I trying to do this ?
Sometimes, when you’re deep in 1000 lines of code in an angular controller, it will be very helpful to know what are the available variables to use at that specific moment. First, the benefit would be, so we don’t re-declare something that already been declared, and second, so we don't override an existing variable unintentionally. For those 2 reasons alone, that's why I am trying to list all the available $scope variables, especially the one that I declared in on that specific page, or in a specific controller of Angular.
NOTE : I am NOT looking to list all JS scope variables, but only looking for those one that I declared in Angular
I know exactly what you need and I use this lib extensively to show me my objects like Chrome does.
Angular json formatter
And the beauty is that it updates instantly with Angular magic. So you know the current state of your object at all times!
Simply drop this directive in your html and assign $scope to it.
In my mine file I have the div with ng-view and it is loaded with the some template. Inside the temp`late I have a button which will change the value.
after to click the button, I am trying to show the value in the index but I recieving a null value.
In index.html I could have something like:
<html>
...
<body ng-app="productsApp" ng-Controller="mycontroller">
<div ng-view></div>
{{value}}
</body>
</html>
In my controller I have something like
angular.module('productsApp').controller('ProductController',
['$scope', 'dataService', function ($scope, dataService) {
$scope.value;
$scope.button = function () {
$scope.value= "123";
};
}]);
The template could be something like:
<button ng-click="button">CHANGE</button>
How can I assign values to main page from templates and show them?
You need to beware which controller you set, the names have to match. Also beware of typos, even when typing fast ;). Also look into the function call. You forgot to use () at the end. And naming things a little bit better would be recommended to (I don't say my namings are the best, but calling a function button is not very readable).
HTML
<body ng-controller="MainCtrl">
<p>Value {{value}}!</p>
<button ng-click="btnPressed()">Change</button>
</body>
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.value = "456";
$scope.btnPressed = function() {
$scope.value = 123;
}
});
The controllers name in this example is MainCtrl. You need to refer the correct name as well in your HTML (you mix mycontroller and ProductController).
Working Plnkr
http://plnkr.co/edit/tpl:8rFfZljYNl3z1A4LKSL2
You have defined $scope.button in ProductController which set $scope.value.
But this $scope.value is come under scope of ProductController and not available under scope of mycontroller which parent scope.Thats why you getting null value.
Define $scope.value and $scope.button in mycontroller.
Or better way is define this functionality in some factory and access it wherever needed.
Or define that function and variable in $rootScope like
$rootScope.value;
but it make this global.
or define like this
$scope.$parent.value;
I wanted to rewrite this fiddle as it no longer worked in angular 1.2.1. From this exercise, I learned that a template is apparently always needed now in the isolated scopes.
somewhere in the directive:
template: '<p>myAttr1 = {{myAttr1}} // Passed by my-attr1<br>
myAttr2 = {{myAttr2}} // Passed by my-alias-attr2 <br>
myAttr3 = {{myAttr3}} // From controller
</p>',
I was not able,however, to successfully add this to the template:
<p ng-show="myAttr4">myAttr4= {{myAttr4}} // Hidden and missing from attrs</p>
What is a good way to hide undefined attributes that are defined on the isolated scope but not given a value from the dom?
my humble fiddle
EDIT: I use a directive called my-d1 to encapsulate the bootstrap tags. I use my-d2 to demo how to use the # in isolated scopes.
Working version merged with Sly's suggestions
I ran into the same template issue in Angular 1.2.0, see the first entry in the 1.2.0 breaking changes:
Child elements that are defined either in the application template or in some other directives template do not get the isolate scope. In theory, nobody should rely on this behavior, as it is very rare - in most cases the isolate directive has a template.
I'm not exactly sure what the issue is that you are encountering - it might be some incorrect markup or you are misnaming the scope variables listed in your isolate scope.
Using ng-show will correctly hide the element if the attribute has not been passed in.
i.e. your example here is correct: <p ng-show="myAttr4">myAttr4= {{myAttr4}}</p>
Updated version of your Fiddle: http://jsfiddle.net/Sly_cardinal/6paHM/1/
HTML:
<div ng-app='app'>
<div class="dir" my-directive my-attr1="value one" my-attr3='value three'>
</div>
<div class="dir" my-directive my-attr1="value one" my-attr3='value three' my-attr4='value four'>
</div>
</div>
JavaScript:
var app = angular.module('app', []);
app.directive('myDirective', function () {
return {
// can copy from $attrs into scope
scope: {
one: '#myAttr1',
two: '#myAttr2',
three: '#myAttr3'
},
controller: function ($scope, $element, $attrs) {
// can copy from $attrs to controller
$scope.four = $attrs.myAttr4 || 'Fourth value is missing';
},
template: '<p>myAttr1 = {{one}} // Passed by my-attr1</p> '+
'<p ng-show="two">myAttr2 = {{two}} // Passed by my-alias-attr2 </p>'+
'<p>myAttr3 = {{three}} // From controller</p>'+
'<p ng-show="four">myAttr4= {{four}} // Has a value and is shown</p>'
}
});
I'm learning angular.js, and wonder when app.controller("MyCtrl",...) should be used and when function MyCtrl($scope){...} should be used.
I see no big differences between the two approaches in this example (link to a plunker):
index.html:
<body ng-app="myApp">
<div ng-controller="FirstCtrl as app1">
<button class="btn" ng-model="app1.count"
ng-click="app1.increment()">
Click to increment</button>
{{ app1.count }}
</div>
<div ng-controller="SecondCtrl">
<button class="btn" ng-model="count"
ng-click="increment()">
Click to increment</button>
{{ count }}
</div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular.js"></script>
<script type="text/javascript" src="example.js"></script>
</body>
example.js:
var app = angular.module("myApp", []);
app.controller("FirstCtrl",function () {
this.count = 0;
this.increment = function (){
this.count = this.count + 1;
}
});
function SecondCtrl($scope){
$scope.count = 0;
$scope.increment = function (){
$scope.count = $scope.count + 1;
}
}
The major reasons for using the module based controller declaration are
Support for minification. The second approach breaks when you minify the code as Dependency Injection fails.
JavaScript good practice is to minimizing polluting the global namespace and the first syntax help there.
In both of your usages, the recommended approach is to inject $scope and use that (rather than using this, which you can do in the second approach as well).
The difference between approach one and two is in how to support minification. In the former, you can supply an array of injected arguments, whereas in the latter you modify $inject. This is of course a bit technical but it is highly recommended to support minification. See A note on minification in the documentation.
The former also does not name the function in the global scope, which is generally a good thing!
Typically, when you create an application you need to set up an initial state for an Angular scope.
Angular applies (in the sense of JavaScript's Function#apply) the controller constructor function to a new Angular scope object, which sets up an initial scope state. This means that Angular never creates instances of the controller type (by invoking the new operator on the controller constructor). Constructors are always applied to an existing scope object.
You set up the initial state of a scope by creating model properties. For example:
function GreetingCtrl($scope) {
$scope.greeting = 'Hola!';
}
The GreetingCtrl controller creates a greeting model which can be referred to in a template.
NOTE: Many of the examples in the documentation show the creation of functions in the global scope. This is only for demonstration purposes - in a real application you should use the .controller method of your Angular module for your application as follows:
var myApp = angular.module('myApp',[]);
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
Note also that we use the array notation to explicitly specify the dependency of the controller on the $scope service provided by Angular.
For more detail Read this