I have seen some polymer elements (e.g. paper-menu, paper-dialog) in their script block starts with anonymous function wrapper like this -
<script>
(function() {
Polymer({
is: 'paper-menu',
behaviors: [
Polymer.IronMenuBehavior
]
});
})();
</script>
I understand anonymous function wrapper for creating its closure. But what is the implication for polymer element. Why paper-menu needs to create its own closure while some elements do not? Anyone can explain this?
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 just come across some code where no $scope was there in the controller.
Here is the code
<div ng-app="myApp" ng-controller="ctrlCarLists as cars">
<button ng-click="cars.showCars()">
Cars
</button>
<button ng-click="alert(cars.data)">
Test
</button>
</div>
var app = angular.module('myApp', []);
app.controller("ctrlCarLists", function () {
this.data = 'hello';
this.showCars = function () {
alert("Ford, Toyata, Mercedes");
};
});
The above code is running, but this.data is not accessible...why? showCars() is accessible when the button is clicked. Why isn't this.data is not accessible?
What should be called when we declare any variable or function inside controller with the this keyword? Will it behave like a static property or function of a class?
jsfiddle link https://jsfiddle.net/tridip/z5wkzc0g/
ng-controller="ctrlCarLists as cars"
"cars" is referring to your controller, and "this" is refering to your controller as well.
ng-click="cars.showCars()"
When this function is triggered, it will refer to "showCars()" function in your controller.
As Yury Tarabanko has explained in his comment, alert function is not found, reason is:
https://docs.angularjs.org/guide/expression#context
Angular does not use JavaScript's eval() to evaluate expressions.
Instead Angular's $parse service processes these expressions.
Angular expressions do not have access to global variables like
window, document or location. This restriction is intentional. It
prevents accidental access to the global state – a common source of
subtle bugs.
Instead use services like $window and $location in functions called
from expressions. Such services provide mockable access to globals.
Source:
Why is ng-click not working?
ng-click requires an expression (see docs).
alert(cars.data)
is not an expression that angularJS can compile (it doesn't know what alert refers to) thus it should be replaced with
ng-click="cars.data()"
because cars.data() is an expression it can compile.
Fiddle
Or, alternatively
Fiddle
where we could have this:
this.alert = function (text) {
alert(text);
}
I was hoping I can append a module to the main module after bootstrap.
I found this issue: https://github.com/angular/angular.js/issues/3881, which is just what I want.
They say:
I no longer feel this is necessary, although it would be nice to see warnings if we redefine a module (but even this may be beneficial during testing)
I'm not sure what redefine a module mean, so I tried it as my guessing:
html
<div ng-controller="Ctrl">
{{hi }}
<input ng-model="hi" />
<button ng-click="say()">Say</button>
<ul>
<li phone="{{p}}" ng-repeat='p in ps'></li>
</ul>
</div>
You can see there is a phone directive.
angular code
angular.module('ctrl',[])
.controller('Ctrl', ['$scope', function($scope){
$scope.hi = 'Hi';
$scope.say = function() {
alert("Say something");
};
$scope.ps = ["1234567890123","001122334455667"];
}]);
angular.module('app', ['ctrl']);
angular.element(document).ready(function () {
angular.bootstrap(document, ['app']);
});
You can see there is no phone definition yet.
Then I try to redefine the app module and append a new phone module:
angular.module('phone', [])
.directive('phone', function() {
return {
restrict: "A",
scope: {
p : "#phone"
},
link: function(scope,el){
el.text(scope.p.substr(0,5)+"...");
el.css("cursor", "pointer");
el.on("click", function(){
el.text(scope.p);
el.css("cursor", "text");
});
}
};
});
setTimeout(function() {
console.log('redefine module');
angular.module('app', ['ctrl', 'phone']);
}, 3000);
It will run in 3 seconds.
But that's still not working. I'm not sure if my approach is correct, how to fix it?
You can see a live demo here: http://jsbin.com/xukafo/1/edit
Updated:
Let me make it clear. The module I want to append to the main module is a 3rd party module, which has already defined some modules and directives. Since it's big and slow, I want to load it asynchronously, and append it to the main(bootstrapped) module. I can't modify the source code of that module.
There is already a tricky solution: https://stackoverflow.com/a/18365367/4022015
I had tried that solution, it's working but can never pass protractor e2e testing. So I created a feature request for "appending a module" but told there is already one exist(the one in the question). And people say we don't need this feature because we can "redefine" a module, but I don't know how to "redefine" it. So there is this question.
Redefining a module means, first defining a module:
angular.module('module1', []);
and then defining it again somewhere else:
angular.module('module1', [])
.config(...);
whereas the intention is to add a .config block (notice that .module() is called with a single parameter:)
angular.module('module1')
.config(...);
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
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.