Why to use $onInit in AngularJS? [duplicate] - javascript

This question already has an answer here:
When to use the AngularJS `$onInit` Life-Cycle Hook
(1 answer)
Closed 3 years ago.
In AngularJS, what is the purpose of the $onInit function if I can do the same initialization without that function?
For example this:
module.component("myComponent", {
...
controller: function() {
const $ctrl = this;
$ctrl.$onInit = () => {
$ctrl.var1 = "hello";
$ctrl.var2 = { test: 3 };
}
}
});
Can also be done like this:
module.component("myComponent", {
...
controller: function() {
const $ctrl = this;
$ctrl.var1 = "hello";
$ctrl.var2 = { test: 3 };
}
});
Is there some case where $onInit is necessary?

Per the AngularJS docs
Called on each controller after all the controllers on an element have been constructed and had their bindings initialized (and before the pre & post linking functions for the directives on this element).
This gives you two guarantees:
All controllers on this element have been constructed
The pre & post linking functions for this element have not been executed
In contrast to your first method, where neither of these facts are guaranteed (though the second one is highly probable).
In addition, as a matter of style and readability, it makes it very clear to a potential reader/reviewer that this is code that you as the developer intended to run upon initialization of this controller.

Related

In Angular 1.x When "watching" in a directive; why does watching a function that returns a variable act different than just watching the variable?

The easiest way to explain this question is with some sample code so here is a very simple directive written in ES6 syntax:
export default class IsFoo {
constructor() {
// Set the directive properties
this.restrict = 'A';
this.require = 'ngModel';
}
link(scope, element, attributes, controller) {
let foo = scope.$eval(attributes.foo);
controller.$validators.isFooBar = (modelValue) => {
// make sure we have the most recent value foo
foo = attributes.foo;
return foo === 'bar';
};
scope.$watch(() => {return attributes.foo;}, () => controller.$validate());
}
static directiveFactory() {
IsFoo.instance = new IsFoo();
return IsFoo.instance;
}
}
IsFoo.directiveName = 'isFooBar';
That is a rough version of my directive with all the actual important validation removed.. it's pretty simple.
If I change the watch line to be:
scope.$watch(attributes.foo), ()=>controller.$validate());
It doesn't work. Why? Why does the function returning the attributes.foo work?
What is the difference that causes the end result to be different?
Also, disclaimer, I'm intentionally not using scope isolation because the directive is being used on an element that has another directive that uses scope isolation.. so they collide and you get an error Multiple directives asking for new/isolated scope on: xxx.
My rough guess is that it is related to how closures behave in javascript but I can't wrap my head around how the two behaving differently.
Thanks for any insight you can provide.
The interface for scope.$watch is according to the documentation the following:
$watch(watchExpression, listener, [objectEquality]);
With watchExpression being either a string or a function. If it is a string, it is interpreted as a path within your scope object. Assuming attributes.foo is "test.something", it will watch scope.test.something - if it exists.
If you want to watch for changes of the value of attributes.foo, you have to use the function, or attach attributes.foo to your scope and passing "attributes.foo" as watchExpression.

What's the difference between $scope.myScope = function () and function () in AngularJS [duplicate]

This question already has answers here:
var functionName = function() {} vs function functionName() {}
(41 answers)
Closed 7 years ago.
I've recently gone into developing with AngularJS. It's confusing to me the different between these two:
$scope.myScope = function () {
var x = 'do something with variable here';
$scope.anotherScope = x;
};
and
function myFunction () {
var x = 'do something with variable here';
$scope.anotherScope = x;
}
They both seem to be able to do the same thing (I use them a lot inside controllers). Is there a best practice for when and where to use these two?
$scope.myScope = function () {};
This means your function is a property of the scope object. So you can use it in your controller , html page even in your app. it can be referenced in different modules in the same app. so you just call it in your html page using the function name directly either onclick or onchange , anyhow depending on your need.
the other definition can only be used in your controller and is not the scope of your app. however if you define your function using "this.myScope = function(){};" then you can call the function in your html by using your controller. like ng-click = "controllerName.myScope();"
the main difference is i nwhich scope the function belongs to and where all you can reference the function.
hope it helps !!!!
As mourycy already mentioned, you should use the form
$scope.myScopeFunction = function () {
...
};
only for functions which you want to call via the scope object. This is needed for function calls within your views.
For example:
<button ng-click="myScopeFunction()" />
which calls the function myScopeFunction of the current $scope object.
If you don't need to be able to call a controller method from "outside" you should use the following form:
function myFunction() {
...
};
$scope.myScop = function(){
...
};
is a function you can execute from the HTML controller.
function foo(){
...
}
is a function you can only execute on the controller's JS file.

How can i call a function that is in another controler (Angular js) [duplicate]

This question already has answers here:
Can one AngularJS controller call another?
(14 answers)
Closed 7 years ago.
in ControllerOne
$scope.displayReport = function( status ){
statusCkeck( status ) ;
}
statusCkeck() ; is in another controller -controllerTwo
How can i call this function from one controller to another
i tried angular.element(document.getElementById('controllerTwo')).scope().statusCheck();
But it throwing error.
Please suggest
You should use a service instead to avoid muddying up the controllers with code that calls functions in other controllers. If you're sharing functions, just define them in a service and inject that service where needed.
angular.module('myApp', [])
.factory('myService', function() {
return {
sharedFunction: function (foo, bar) {
return foo + bar;
}
}
};
You can get scope:
angular.element(document.getElementById('yourControllerElementID')).scope().get()
But yourControllerElementID should be ElementID not ControlerName!
For example:
<div id="elemID" ng-controller="YourController"></div>
So elemId is id of element with YourController.

AngularJS controller and methods

I'm a beginner in angularjs with a few questions about controllers.
Here's my example controller:
function exampleController($scope)
{
$scope.sampleArray = new Array();
$scope.firstMethod = function()
{
//initialize the sampleArray
};
$scope.secondMethod = function()
{
this.firstMethod();
};
};
Here are my questions:
How I can call firstMethod from secondMethod? Is the way I did it correct, or is better way?
How I can create a constructor for the controller? I need to call the secondMethod that call the firstMethod that initialize the sampleArray?
How I can call a specific method from html code? I found ng-initialize but I can't figure out how to use it.
You call a method the same way you declared it:
$scope.secondMethod = function() {
$scope.firstMethod();
};
Which you can also call from HTML like so:
<span>{{secondMethod()}}</span>
But controllers don't really have "constructors" - they're typically used just like functions. But you can place initialization in your controller function and it will be executed initially, like a constructor:
function exampleController($scope) {
$scope.firstMethod = function() {
//initialize the sampleArray
};
$scope.secondMethod = function() {
$scope.firstMethod();
};
$scope.firstMethod();
}
you call the first method by using $scope.
So
$scope.secondMethod = function()
{
$scope.firstMethod();
};
Not really sure what you mean in your second question.
For your third quesiton, you can either have the method run automatically "onload" on controller, OR run it via an front-end angular binding.
e.g.
Run Automatically
function exampleController($scope)
{
$scope.sampleArray = new Array();
$scope.firstMethod = function()
{
//initialize the sampleArray
};
$scope.secondMethod = function()
{
$scope.firstMethod();
};
$scope.secondMethod(); // runs automatically.
};
Run on binding
<div ng-controller="ExampleController"> <!-- example controller set up in namespace -->
<button class="btn" ng-click="secondMethod()">Run Second Method</button>
</div>
#Josh and #Christopher already covered your questions, so I won't repeat that.
I found ng-initialize but I can't know how to use that :-(
The directive is actually ng-init. Sometimes (e.g., if you are starting to use Angular in parts of an application and you still need to dynamically generate a view/HTML page server-side), ng-init can sometimes a useful way to initialize something. E.g.,
<div ng-controller="ExampleCtrl">
<form name="myForm">
<input type="text" ng-model="folder" ng-init="folder='Bob'">
Here's an example where someone needed to use ng-init: rails + angularjs loading values into textfields on edit
I'd also like to mention that controllers are not singletons. If you use ng-view, each time you go to a different route, a new controller is created. The controller associated with the view you are leaving is destroyed, and the controller associated with the view you are going to is executed. So that "initialization code" in a controller could get executed multiple times while an app is running. E.g, if you visit a page, go elsewhere, then come back, the same controller function (and its "initialization code") would be executed twice.
If you want something to truly run once, put it in a service or in a module's config() or run() methods. (Services are singletons, and hence each service is instantiated only once, so initialization code in a service is only run once.)

Unit testing jQuery document.ready function [duplicate]

This question already has answers here:
How to invoke $(document).ready(function() {}) in unit testing
(5 answers)
Calling $(document).ready(function() {...}); from another file
(1 answer)
Closed 5 months ago.
I have a question in regards to unit testing jQuery's document.ready function().
Currently I have 2 scenarios in my code:
function myFunction()
{
$(document).ready(function() { ... });
}
And:
$(document).ready(function()
{
// some really long setup code here
});
I tried to write a unit test for the first scenario, but I just couldn't get it to run into the document.ready function. As for the second scenario, I haven't come up with a way to test it yet (I'm having trouble coming up with both a way to test it and the syntax).
So assuming I cannot change the source code, are there any ways to test those functions? (assuming it is a good idea to test them)
Thanks.
You do not need to test $(document).ready as it is part of the framework and is already unit tested. When writing unit tests you need to test two things:
Your interaction with the framework. This includes things like making sure that you call the right functions with the right parameters.
Your own code - that your code does the right thing.
So what you really need to do is to make sure that whatever code that gets called from $(document).ready is correct.
function myInit(){
//...
}
function myFunction()
{
$(document).ready(myInit);
}
All you need to do now is to unit test myInit function.
What you can also do is mock out $.ready function to make sure that you are calling it:
var readyCalled = false;
$.ready = function(func){
readyCalled = (myInit == func);
}
//Your code containing `myInit` will get executed somewhere here
//....
//Then test:
test("Should have called ready", function() {
ok(readyCalled, "ready should have been called with myInit as a parameter.")
});
The function that registers the on ready handler should register another function, not an anonymous codeblock. Then you can test the code that calls $.ready() separate from the code that runs on ready. So you have:
One test to verify the right function is set as the the ready handler
Another test to verify the ready handler does the right stuff
To test scenario 1, you'll need to inject a test double for jQuery. This is difficult as if you redefine $ or jQuery, odds are you'll screw up other code that relies on it for other processing (like the test runner). At the same time your code may still want to call jQuery directly when its using utility methods like array concatenation. Any inversion-of-control pattern should address this though (http://martinfowler.com/articles/injection.html).
Anyhow, here's some code using constructor injection (using JSMock for the mocking library, and QUnit (of jQuery) for the test runner):
// the code
var createComponent = function(_$) {
var that = {};
that.OnStart = function() {
_$.ready(this.OnReady);
};
that.OnReady = function() {
};
return that;
};
// the test
test("OnStart associates the ready handler", function() {
var sut;
var mock$ = mc.createMock($);
mock$.expects().ready(isA.TypeOf(Function)).andStub(function(callback) {
equals(callback, sut.OnReady);
});
sut = createComponent(mock$);
sut.OnStart();
mc.verify();
});
test("OnReady does the right stuff", function() {
//etc
});
I use this general pattern for all event handlers in JS... You might prefer to use prototype type classes. When you pass functions as parameters to jQuery, you need to be aware that the "this" value will not be set by jQuery when those callbacks are called. In the test, this breaks because equals(callback, sut.OnReady) no longer passes. To address this, you need to make the event handlers direct members of each instance. You can imagine when there are a number of then its nice to have a util that takes a list of them, but this demonstrates making 'OnReady' a member who does not rely on 'this'.
var Component = function(_$) {
this._$ = _$;
// repeat for each event handler thats tested
this.OnReady = function() {
Component.prototype.OnReady.apply(this);
}
}
Component.prototype.Start = function() {
this._$.ready(this.OnReady);
}
Component.prototype.OnReady = function() {
}

Categories

Resources