Using this instead of $scope inside controller - javascript

I am trying to follow style guide for angular and there wrote we should use this insted scope...
Styleguide
Could someone explain me when I am able to use this?
Here is my try..... What I am doing wrong?
I am trying to toggle form....
here is my html code:
REPLY
<a href="#" ng-click="formEdit(x)" ng-if="x.formEditShow" >CLOSE</a>
With classic $scope I would do like this inside my conroller :
$scope.formEdit = function(data){
data.formEditShow = !data.formEditShow;
}
But with this it should look something like this(but don't work):
var vm = this;
vm.formEdit = formEdit;
function formEdit(data){
data.formEditShow = !data.formEditShow;
}
Anyone can help me to understand this?

When you are using this(context) in controller instead of $scope, you must use controllerAs while defining html on page to access controller variables. Whenever you wanted to use variable bounded to this on view you could use alias of your controller. Below you can see vm is alias of controller.
ng-controller="myController as vm"
Then while accessing controller method an variable inside ng-controller div you need to use alias of your controller like ng-click="vm.formEdit(x)"
HTML
REPLY
<a href="#" ng-click="vm.formEdit(x)" ng-if="x.formEditShow" >CLOSE</a>

Assuming your controller is named FormController.
First step
The first step is to declare the route (or the ng-controller value if you are not using a router) as such:
FormController as form // name it semantically instead of a generic name
Due to the above configuration, angular will alias as form the instances of FormController.
HTML template
Then adapt your html template according to the alias you gave (form). I modified your html to keep only the essential part about the question. We are calling the functions form.reply and form.close.
REPLY
CLOSE
Controller declaration
According to what we wrote above, our controller should look like that:
myApp.controller('FormController', function () {
var vm = this;
vm.reply = function () {
// ...
}
vm.close = function () {
// ...
}
}
Notice the var vm = this; line? Theoretically we could get rid of this line, and store the functions reply and close in the this object. But depending of the context, this does not refer to the same object. In a callback function this would not refer to the controller but to the callback function. That's why we are caching the this that refers to the controller. We usually name this reference vm for viewmodel, as a controller controls a view.

Related

Why I can't see a $scope variable that was defined in a $scope function?

I have the following piece of code in angular
$scope.prepare = function(){
$scope.elems = [1,2,3];
};
$scope.action = function(){
var elem = $scope.elems[0]; //undefined
}
then, in my view, I use the directive ng-init="prepare()" and attach to a button the action function on click event
<button ng-click="action()">action</button>
Inthe action function the scope hasn't the elems array defined?
Can anybody tell me why this happen?
Thanks!
Since you are not showing the controller or the scope of the HTML where you are calling init() and action(), I can't even guess why you are having problems since the code you have posted works. This is a pluker proving that much: http://plnkr.co/edit/qMzPtJtp9t9CoNKkmWIc?p=preview
<div ng-init="prepare()"></div>
<input type="button" value="Call function" data-ng-click="action()" />
<p>Init Defined: {{elems}}</p>
<p>Function call: {{redefined}}</p>
$scope.prepare = function(){
$scope.elems = [1,2,3];
};
$scope.action = function(){
$scope.redefined = $scope.elems[0]; //undefined
}
With that said, you are not using ng-init() correctly. From the angluar documentation:
"This directive can be abused to add unnecessary amounts of logic into your templates. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat ... and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope."
Link to ng-init documentation: https://docs.angularjs.org/api/ng/directive/ngInit
You will be much better off initializing your array in the controller.

Insert Angular directive into DOM from inside a Service

My goal is to create an Angular module that displays popup dialog messages. This module contains a directive (HTML, CSS and JavaScript) containing the internal logic (and markup and styles). Plus there's a service (factory) which acts as an API that can be used by other services.
Now this service of course has an openDialog() function which should insert the dialog directive into the DOM and present it to the user.
All solutions to this problem I have found so far make use of the $compile function. But it needs scope as a parameter. In a service where there's no scope though. They only exist in controller or link functions.
The reason I chose this implementation is for separation of concerns (directive's link and controller for internal usage, factory for external usage because it can be dependency injected). I know I could pass the scope when calling the function like this:
popupDialogService.openDialog({ /* options */ }, $scope);
But I don't see the point. It doesn't feel right. What if I call that function from inside another service which doesn't use scope either?
Is there a way to easily put the directive into the DOM from inside the service function or is there a better way to solve this problem?
Another solution I'm thinking about is calling a function of the directive's controller from inside the directive's factory. Is that possible?
Code
popupDialog.directive.js
angular.module('popupDialog').directive('popupDialog', directive);
function directive() {
return { ... };
}
popupDialog.service.js
angular.module('popupDialog').factory('popupDialogService', factory);
function factory() {
return { openDialog, closeDialog }; // *ES2015
function openDialog(options) {
// this function should put the `popupDialog` directive into the DOM
}
function closeDialog() {
// and this one should remove it
}
}
some.random.service.js
angular.module('myApp').factory('someRandomService', factory);
factory.$inject = ['popupDialogService'];
function factory(popupDialogService) {
return { clickedButton };
function clickedButton() {
popupDialogService.openDialog({ /* options */ });
// Sample implementation.
// It shouldn't matter where this function is beeing called in the end.
}
}
I know I could pass the scope when calling the function ... And it doesn't feel right.
Well you anyway need scope for dialog HTML content, Angular needs to compile and render it in some scope, right? So you have to provide scope object for your template somehow.
I suggest you to take a look at popular modal implementations like they do it, for example Angular UI Bootstrap's $modal or this simple one I was creating for my needs. The common pattern is passing scope parameter with modal initialization or use new child scope of the $rootScope for dialog. This is the most flexible way that should work for your both cases.
After all, it's not necessarily has to be real scope instance. You can even make your service accept plain javascript object and use it to extend new $rootScope.$new() object with.

Angularjs directive scope accessing in a factory

I'm running Angular 1.4.3.
I'm trying to create a 'factory' in angular that helps me create a common menu system in my app. The 'create' function of the 'gui' factory creates a ul and the li elements are clickable with ng-click.
This ul is attached to the document body.
The ng-click should execute the 'callMe' function in my factory, but I'm not sure what scope to use....
Code:
var App = angular.module('App', ['ngRoute']);
App.factory('gui', function() {
var menu = function {
"create" : function(){
var menu_container = $('<div id="menu"></div>');
var menu_ul = $('<ul></ul>');
menu_ul.append('<li class="menu-item-purple" ng-click="gui.menu.callMe()"><a>About <span style="float: right;">></span></a></li>');
menu_container.append( menu_ul );
menu_container.prependTo(document.body);
},
"callMe" : function(){
console.log("I HAVE BEEN HIT");
}
}
return {
"menu" : menu
};
})
.controller('ExampleController', function($scope, gui){
$scope.gui = gui;
gui.menu.create();
})
So in the above code, when I click the li menu button - I do not get any response.
I have tried the following in the li element:
ng-click="this.callMe()"
I thought the original should work because if I hard code the html into the view with that ng-click directive, it works. I assume it could be something to do with load order as the gui.menu object should be present in the view as it's passed in the controller's scope?
Since your factory actually involves with some DOM operation, I would suggest you defining a directive.
You can define your ul, li elements in the directive template and also handles the ng-click events. A directive is the best choice to share some DOM structure as well as the logic across different places in your application.
To access the method contained within your factory in the template, you need to change the 'this' variable to gui, that's because your methods are contained in a variable named gui inside your scope.
Although, it may be easier to create a function inside your scope for the thing you want to do.
$scope.createMenu = function () { gui.menu.create(); };
And then, you can call the function directly in the template with
ng-click="createMenu()"
But, as #Joy said it would be better with a directive since your DOM is modified

controller and link functions in directive do not have same scope

I have an issue with my angular.js directive.
It should be a kind of autocomplete, in directive's controller property I'm loading an array of values and inside link function compiling template to show the results.
But when I update scope inside link it doesn't reflect on controller and template, please take look at the example here - http://plnkr.co/edit/Lz3QGwklghPo3as2QTqU
Should I apply scope changes or smth similar?
Your code has two problems
Attach click event to document instead of body
Use $apply() inside bind
Below code will resolve your problem
$document.bind('click', function (e) {
scope.results = [];
scope.$apply();
});
I update your $body.bind('click',...) method to
$body.bind('change', function (e) {
scope.results = [];
});
and it seemed to work (I mean that after 0.5 sec I typed a letter, the list of name is re-displayed).

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.)

Categories

Resources