I'm looking to sure up my understanding of dependency injection and make sure my understanding is correct. I'm following a course at the moment that takes 2 modules as examples, ngMessage vs. ngResource. Within my app module injection I include ['ngResource', 'ngMessage'].
Within the controller I inject the service $resource (for ngResource).
Then in my view html I can make use of the ngMessages directive $error.
My question is why doesn't ngMessage require any insertion in to the controller? Is it the difference between a service ($resource) vs directive ($error)?
I just want to make sure I'm clear on why ngMessage didn't require any form of injection in to the controller. If my thinking is right, all modules that are injected in to your app will have all directives immediately available in the view, but any services will need to be injected in to the controller.
app.js
var myApp = angular.module('myApp', ['ngResource']);
myApp.controller('mainController', function('$resource') {
console.log($resource)
});
<form name="myForm">
<input type = "text" ng-model="field" name="myField" required minlength="5"/>
<div ng-messages="myForm.myField.$error">
<div ng-message="required">You did not enter a field</div>
<div ng-message ="minlength">The value entered is too short</div>
</div>
</form>
Directive are available at the module level and can be used in any template in the module or module that import the module that declares it.
Services are injected as objects into your controllers for accessing data from them and sharing data between controllers.
Related
I am developing single page application using partial views and angularJS.
I am having a scenario where I have to open same partial view multiple times with different data.
I tried to make controller name dynamic by adding app.directive and changing controller name both in js and html and then calls Angular-Complies method, but the issue is that it changes it for all previous tabs opened.
Then I tried to make multiple js files and changing controller name in partial view with Jquery but still does not helps. Here is my code.
HTML:
<div ng-app="myApp">
<!-- this is in view1.html -->
<div ng-controller="DashboardDesignerController">
<div ng-repeat="widget in workspace.widgets">stuff here
</div>
</div>
<!-- this is in view1.html -->
<div ng-controller="DashboardDesignerController">
<div ng-repeat="widget in workspace.widgets">stuff here
</div>
</div>
//Angular controller is in separate JS file.
app.controller("DashboardDesignerController" , function ($scope, $http,
$rootScope, $compile, $injector, $timeout) {
}
I expect to behave every tab separately according to its data but currently when I go to first opened tab, it's scope changed with the last opened tab.
Is there built in way of angularJS to doing it, Or some good approach will also appreciated.
Actually the issue was, I was calling chart drill down method in core JavaScript which was replacing $scope with last opened.
Resolved the issue by getting scope with JQuery
var scope= angular.element($('.k-state-active').find('.tempDiv')[0]).scope();
and called angular function with this scope variable.
new to angularJS, after reading the Reference about Scope
If all controllers share a $rootScope in a ng-app, can we share a ng-model which is assigned to the $rootScope, so that controllers could communicate with each other?
Test with the following snippet, but gName failed to change when model in inputController changed, presume may be gName was made $scope.gName again when input changed. if that is true, is there any way to communicate the controller with each other? ie, the input could be displayed in other controllers?
var app = angular.module("myApp",[]);
app.controller("inputController", ["$rootScope", "$scope", function($rootScope, $scope){
$rootScope.gName = "Hello";
}])
.controller("displayController1", ["$rootScope", "$scope", function($rootScope, $scope){
}])
.controller("displayController2", ["$rootScope", "$scope", function($rootScope, $scope){
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<hr/>
<div ng-controller="inputController">
<input type="text" ng-model="gName"/> <br/>
<span>{{gName}}</span>
</div>
<hr/>
<div ng-controller="displayController1">
<span>{{gName}}</span>
</div>
<hr/>
<div ng-controller="displayController2">
<span>{{gName}}</span>
</div>
</body>
In AngularJS, $scope inherit from their parent scope (and in the end it will be rootScope)
In Angular, primitive types are overwritten when a child changes them. so in the input when changing gName it is keeping the $rootScope.gName and creating a new local $scope.gName that is only showing on that controller
to solve the problem you can add an object, and changing the property in it
var app = angular.module("myApp",[]);
app.controller("inputController", ["$rootScope", "$scope", function($rootScope, $scope){
$rootScope.prop = { gName: "Hello" };
}])
.controller("displayController1", ["$rootScope", "$scope", function($rootScope, $scope){
}])
.controller("displayController2", ["$rootScope", "$scope", function($rootScope, $scope){
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<hr/>
<div ng-controller="inputController">
<input type="text" ng-model="prop.gName"/> <br/>
<span>{{prop.gName}}</span>
</div>
<hr/>
<div ng-controller="displayController1">
<span>{{prop.gName}}</span>
</div>
<hr/>
<div ng-controller="displayController2">
<span>{{prop.gName}}</span>
</div>
</body>
Note I am only answering the question, this way is not recommended for making controllers communicate with each others, you should use services for that, you can check this guide
You do not want your controllers to communicate with each other, as the only purpose of a controller is to transform data from and for the view and to handle view-local logic (like hiding things with checkboxes, previewing calculated numbers and so on).
Your actual data and business logic should be in services, which also allows for multiple controllers to access it.
Share logics/data within your application and application controllers through services. The big advantage is that the digest cycle is not influenced by changes, and that your logics/data can easily be shared accross your application on a structured / seperated manner. Usage of service reduce pollution of your readability.
I try to only use the $rootScope for event broadcasting / listening and for data attachment on the main application frame (ex. data in the application outer frame).
AngularJS services are:
Lazily instantiated – AngularJS only instantiates a service when an application component depends on it.
Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
A detailed example can be followed through a tutorial on https://thinkster.io/a-better-way-to-learn-angularjs/services
I have inherited some AngularJS code. It has this
function MainCtrl($scope)
{
// code goes here
};
angular
.module('inspinia')
.controller('MainCtrl', MainCtrl)
Now I want to add a custom controller which combines a datepicker and timepicker into one control. The GitHub project is here and there is a demo Plunk here.
The demo Punk declares its controller as
var app = angular.module('app', ['ui.bootstrap', 'ui.bootstrap.datetimepicker']);
app.controller('MyController', ['$scope', function($scope) {
How do I add that into my existing controller? What is the combined declaration? Preferably one that I can use it with ng-stricti-di on my ng-app.
[Update] here's my best guess, which I can't test until I get home in 10 hours or so. How does it look?
var myApp=angular.module('myApp', ['$scope','ui.bootstrap','ui.bootstrap.datetimepicker']);
myApp.controller('MainCtrl', function($scope)
{
// code goes here, and can use ui.bootstrap and ui.bootstrap.datetimepicker
// which were injected into the app's module
}]);
[Update 2[ When I change it to
angular
.module('inspinia' ['ui.bootstrap', 'ui.bootstrap.datetimepicker'])
.controller('MainCtrl', MainCtrl)
I get
Error: [$injector:nomod] http://errors.angularjs.org/1.5.0/$injector/nomod?p0=undefinedError: [$injector:nomod] http://errors.angularjs.org/1.5.0/$injector/nomod?p0=undefined
Despue index.html having
<script src="js/bootstrap/bootstrap.min.js"></script>
How do I get this project to use ui boostrap and its datepicker?
Please review these steps:
You don't need to inject your $scope in your app declaration just
inject external modules you want to use, for this case:
'ui.bootstrap' and 'ui.bootstrap.datetimepicker'.
angular.module('myApp', ['ui.bootstrap','ui.bootstrap.datetimepicker'])
What is the combined declaration?
Because 'ui.bootstrap.datetimepicker' depends only on 'ui.bootstrap.dateparser' and 'ui.bootstrap.position' but you need also the bootstrap templates and functionality that are included into the ui.bootstrap-tpls.js.
Make sure to include the above files required in you index.html
<link rel="stylesheet" ref="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.0/ui-bootstrap-tpls.js"></script>
<!-- make sure you download this file from the github project -->
<script src="datetime-picker.js"></script>
How do I add that into my existing controller?
When you declare your controller this inherit all module dependencies you had declared (injected) for the app, so you don't need to do this again. In your controller you should create an object literal to store the date-time selected for the user and a variable to control when the date-picker is open, like this:
angular.module('myApp').controller('MainCtrl', ['$scope', function($scope) {
$scope.myDatetime = {
dateSelected: new Date(),
isOpen: false
}
}]);
Call the date-time picker directive in your html:
<html ng-app-="myApp">
<head> .... </head>
<body ng-controller="MainCtrl">
<div class="input-group">
<input type="text" class="form-control" datetime-picker="MM/dd/yyyy HH:mm" ng-model="myDatetime.dateSelected" is-open="myDatetime.isOpen" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="myDatetime.isOpen = !myDatetime.isOpen"><i class="fa fa-calendar"></i></button>
</span>
</div>
</body>
</html>
I hope this help you.
Based on your comment under the question, your confusion is with the way the two pieces of code handle their dependency injection. So before I go further, if you haven't read the documentation on dependency injection, then stop right here and go read it. It will have all of your answers and more and it's something you need to know if handling Angular for longer than five minutes.
To answer the specific case you have, the top code you listed uses implicit injection for the controller which works but is not safe for minification nor is it recommended. The code sample you found uses array dependency inject for the controller which is better and safe for minification. The app declaration in the second sample is just standard module dependency injection and shouldn't look any different than what you already have in your application.
So to use the code you found all you have to do is add the correct module dependencies to your app something like:
angular.module('inspinia', ['ui.bootstrap', 'ui.bootstrap.datetimepicker']);
angular.module('inspinia').controller('MainCtrl',MainCtrl);
function MainCtrl($scope) { }
Your controller appears to already have the correct dependencies so it doesn't need to be changed (which is say it doesn't need anything outside of $scope). I used the code from your first example to show how your current code would be updated but ideally you would use the second version of dependency inject for your controller.
The update you have with the error is because the ui.bootstrap module is not part of bootstrap but part of the angular-bootstrap project. You need to include those js in your page.
It would be remiss of me if I didn't go ahead and mention there is a third way to do dependency injection using the $inject service. It is preferred in a number of popular style guides because it is easy to use a task runner to automate. This is arguably the best option to use for this reason.
Here is minimal app, that you will need to use this datepicker
Html:
<body ng-app="inspinia">
<div ng-controller="MainCtrl as ctrl">
<h1>Datepicker Demo</h1>
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup ng-model="ctrl.dt" is-open="ctrl.opened" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="ctrl.open()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
</div>
</body>
JavaScript:
(function() {
'use strict';
angular.module('inspinia', ['ui.bootstrap']);
angular.module('inspinia').controller('MainCtrl', MainCtrl);
function MainCtrl() {
this.open = function() {
this.opened = true;
};
this.opened = false;
}
})();
Here I created a plunker for you, so you can try:
http://plnkr.co/edit/jEwT39sKcKtr8jbQa0uc?p=preview
Can I create a module for authentication and then bind to its controllers from a view in another app / module? For example, creating a common LoginController and then have login pages in my other apps that would use this controller? If so, how? I cannot find any examples but the docs make me think this is possible.
Yeah so long as you have a script/module loaded up and set as the dependency of your app used for the ng-app or for manually bootstrapping the app then you can use any of the controllers/providers defined in those modules.
angular.module('A',['B']);
angular.module('B', []).controller('MyController',function($scope){ $scope.doSomething = function(){alert('did something')}});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app="A">
<div ng-controller="MyController">
<button ng-click="doSomething()">Do Something</button>
</div>
</div>
You could have multiple modules by that you can separate out your code.
Basically you can create a separate module for your LoginController, like
angular.module('authentication',[])
.controller('LoginController', function($scope){
//code here
});
then you main app module you should add this module
angular.module('app',['authentication']
Then you can use this app module as ng-app or you could bootsrap the same module.
Here's Plunker
I have an external template within in a controller with ng-include. It is shown and hidden based on click event of Button.It is working as required but with $parent in ng-include Template.Is there any other better way of doing this ?
Html
<body ng-controller="MainCtrl">
<div data-ng-include="'terms.html'" data-ng-show="otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{name}}!</p>
<button data-ng-click="mainPage=false; otherContent=true">Link to some Content</button>
</div>
</body>
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.mainPage=true;
});
External Template
<p>Some content here </p>
<button data-ng-click="$parent.mainPage=true; $parent.otherContent=false">Back</button>
Option1 - Set property on an object in the scope
In the main controller add an object on the scope.
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.page={mainPage:true};
});
and in the ng-click do:-
<div data-ng-include="'terms.html'" data-ng-show="page.otherContent"></div>
<div ng-show="page.mainPage">
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
<!-- -->
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
Demo - setting property on an object in the scope
Option2 - Use function
Instead of setting properties on the view (Which is anyways a good idea to abstract out too much logic from the view), Do your set operations in the controller exposed as a function that can be invoked from the view, which also gives extensibility when you need to add more logic for that particular action. And in your case you could even use the same function and call it from both the button clicks and flipped based on a boolean argument.
Demo - with function
Option3 - Use Controller Alias
Using an alias for the controller, which is nothing but instance of the controller is set as a property on the scope with the property name same as the alias provided. This will make sure you are enforce to use dot notation in your bindings and makes sure the properties you access on the child scopes with the controller alias are inherited as object reference from its parent and changes made are reflected both ways. With angular 1.3, it is also possibly to set the isolate scoped directive properties are bound to the controller instance automatically by setting bindToController property in the directive configuration.
Demo - With Controller alias
ControllerAs is the recommend way of avoiding this problem.
Using controller as makes it obvious which controller you are accessing in the template when multiple controllers apply to an element.
If you are writing your controllers as classes you have easier access to the properties and methods, which will appear on the scope, from inside the controller code.
Since there is always a . in the bindings, you don't have to worry about prototypal inheritance masking primitives.
<body ng-controller="MainCtrl as main">
<div data-ng-include="'terms.html'" data-ng-show="main.otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{main.name}}!</p>
<button data-ng-click="main.mainPage=false; main.otherContent=true">Link to some Content</button>
</div>
</body>
Here are some resources for controller as:
http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
http://toddmotto.com/digging-into-angulars-controller-as-syntax/
https://docs.angularjs.org/api/ng/directive/ngController#example