I know that AngularJS appends "Provider" to registered providers, so it is not necesary to name them with that "provider" and you should call them with the full name as "SomethingProvider".
I'm doing that but the console throws me an
Unknown provider: ReportProviderProvider <- ReportProvider <- reportDirective
I have a service, provider and directive called Report, everyone in its own file. ReportProvider.js, ReportService.js, ReportDirective.js
When I try to use the directive I got the error.
Why do angular appends "Provider" to my required dependency?
angular.module('thdmaterialApp')
.provider('Report', function () {});
angular.module('thdmaterialApp')
.service('Report', function (ReportProvider) {
});
angular.module('thdmaterialApp')
.directive('report', function (ReportProvider) {} );
you don't need to write it like this ReportProvider ,, you just need to inject Report ,,
you will have to add Provider when you want to use it in config ,, but in DI it works as other injectable services ,,
and I think of course you will need to change the name of Report service ,,
You are trying to inject a the provider into your directive? I think they are only meant to be used inside config blocks to pre-configure a service. From the docs:
You should use the Provider recipe only when you want to expose an API
for application-wide configuration that must be made before the
application starts. This is usually interesting only for reusable
services whose behavior might need to vary slightly between
applications.
Also, I think your provider will need to define this.$get as a function that returns an object for it to work.
You only need to use provider if you want to be able to change some properties on a service in some situations. I've tried to show this in a DEMO you might find helpful.
app.js
var app = angular.module('thdmaterialApp', []);
app.controller('MainCtrl', function($scope, ReportService, Report) {
$scope.serviceValue = ReportService.value;
$scope.value = Report.getValue();
});
app.provider('Report', function () {
// if you remove the config block you should see this value
this.value = 'default';
this.configureValue = function(newValue){
this.value = newValue;
}
this.$get = function() {
var value = this.value;
return {
getValue: function() {
return value
}
}
};
});
// if you comment this out then the default value will be returned
app.config(function(ReportProvider){
ReportProvider.configureValue('new configured value');
});
app.service('ReportService', function () {
return {
value: 'I don\'t need to be configured.'
};
});
index.html
<!DOCTYPE html>
<html ng-app="thdmaterialApp">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.3/angular.js" data-semver="1.4.3"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>ReportService.value: {{serviceValue}}</p>
<p>Report.getValue(): {{value}}</p>
</body>
</html>
Related
Basically I am trying to access controller scope property from directive's controller function. I am doing it through $parent property. It works fine for static directive but not for dynamically created directive.
please have a look on my plunker
Dynamic Directive
In a plunker, when I click on folder with Id = 1. all goes good and folder path shows as "1 path". Same goes for folder with Id = 2.
But it does not work for dynamically appended folder with Id = n
I am somewhat new to angular. Any help would be much appreciated.
Updated Answer
In light of the latest requirement:
I am trying to call the directive function (i.e updateMap) from
controller.
You can use a Service to share variables between Controllers and Isolated Directives. In the example below, the Service holds the function that will be executed. Each directive when clicked will set the Service's function to it's own updateMap() function. Then the Controller in onFolderPathClicked() calls the Services executeFunction() function, which runs the previously set function.
script.js:
var module = angular.module('testApp', []);
module.service('updateMapService', function(){
var updateMapFunction = null;
this.executeFunction = function(){
updateMapFunction();
};
this.setFunction = function(fn){
updateMapFunction = fn;
};
});
module.controller('crtl', function($scope, updateMapService) {
$scope.onFolderPathClicked = function(){
updateMapService.executeFunction();
};
});
module.directive('folder', function($compile) {
return {
restrict: 'E',
scope: {
id: '#',
folderPath: "="
},
template: '<p ng-click="onFolderClicked()">{{id}}</p>',
controller: function($scope, $element, updateMapService) {
$scope.onFolderClicked = function(){
updateMapService.setFunction(updateMap);
addFolder();
};
var addFolder = function() {
$scope.folderPath = $scope.id + ":click here for calling update map";
var el = $compile('<folder id="n" folder-path="folderPath"></folder>')($scope);
$element.parent().append(el);
};
var updateMap = function() {
alert('inside updateMap()..' + $scope.id);
}
}
}
});
index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-app="testApp" ng-controller="crtl">
<div>FolderPath : <a ng-click="onFolderPathClicked()">{{ folderPath }}</a> </div>
<folder id="1" folder-path="folderPath"></folder>
<folder id="2" folder-path="folderPath"></folder>
</div>
</html>
You could also move folder-path into a Service to save from passing it in as an attribute. The code smell being that passing it in as an attribute means doing so twice, whereas in a Service it means setting it and getting it once (code reuse).
I'm trying to build a directive based on Angular custom filter with promise inside. I tried defining a filter, with the same result.
The issue is that I always get {} as the value, whenever I use a promise.
The following simple example shows the issue very clearly.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive('convert', ['$q', function ($q) {
return {
template: '{{convertFunc()}}',
link: function (scope, elem, attrs) {
scope.convertFunc = function () {
var def = $q.defer();
def.resolve('test');
return def.promise.then(function (s) {
return s; // debugger doesn't stop here
}, function (e) {
return e; // debugger doesn't stop here
});
};
}
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="https://code.angularjs.org/1.2.28/angular.js" data-semver="1.2.28"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div data-convert></div>
</body>
</html>
Can you help finding out what am I doing wrong?
Edit
This is the plnkr I used to create the example.
Edit 2
I'm trying to show the result of the promise on a page. In my real code I'm calling a service and then building a string using values from the service.
VS debugger shows that my string is built correctly. But I cannot display it on the page. I'm under the impression, that JS/Angular fails to recognize completion of the promise. I cannot stop on the breakpoints marked above.
I have a plnkr of a simple code that tries to display the value of typeof("a") in an html page. What I always get is nothing displayed, while I'm expecting for "string". The same goes for other data types. Here is my plnkr:
https://plnkr.co/edit/mUASA8s9TgplwysMhXvG?p=preview
Am I missing something?
I'll note that my final goal is to use ng-if with a condition that is based on the type of a variable.
Angular version: 1.4.8
you not access directly typeof in TEmplate. other approach use this:
In Controller
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.typeOfFun = function(name){
return typeof name;
}
});
In Templte
<div>{{typeOfFun("a")}}</div>
Your code has several problems:
typeof's syntax is typeof a and not typeof(a)
typeof is not a legitimate expression for angular's expression-binding, thus isn't printed to the template.
Considering the two points above, you should move the check to an external function and use the correct syntax - and everything works :)
See the corrected code:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.getTypeOfA = function() {
return typeof "a"
}
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" type="text/javascript"></script>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div>{{"aaa"}}</div>
<div>{{getTypeOfA()}}</div>
</body>
</html>
Obviously, you would want to pass a variable to the "checkTypeOf" function and return the result and not do it as I did. This is only for demonstration purposes!!!!
I am still in study mode of angularjs and just 2 day old. I was trying to make module and so i created seperate js file for it and created module like below.
Also added controller.
var app = angular.module("githubViewer", []);
app.controller("MainCtrl", MainCtrl);
But when i run i get error 'MainCtrl' is not a function, got undefined
here is Plunker
Can someone help me?
After looking in plunker,I think you want to create a separate module in separate file for your controllers and add it to your main module.
For that create module for controllers in separate file,
angular.module("githubViewer", [])
.controller('MainCtrl', function($scope,$http) {
//your logic
});
then add it to your main as dependency in main module
angular.module('plunker', ['githubViewer']);
here is working demo : http://plnkr.co/edit/T9p7Uo2DxUVjqS1wuuiA?p=preview
Ok, you're new to angular, so here's a couple of rules which you must follow until you can prove you need to do otherwise.
You can place definition of module in a separate file. In short plunkers it is often an overkill, but that's what you should be doing in realworld-sized apps. Note that I'm talking about only the module here. Not talking about controllers, factories and other stuff.
Separating body of controller from its inclusion into angular does not bring any benefit. Don't do that.
That said, your files should look like this:
# my_app.module.js
angular.module('myApp', []);
# main.controller.js
var app = angular.module('myApp')
app.controller('MainCtrl', MainCtrl);
function MainCtrl() {
// logic here
}
I check your Plunker.
here is Working Plunker as you want logic of controller in seperate js file and module in seperate file
app.js
function MainCtrl($scope,$http) {
var person = {
firstName: "Kiran",
lastName: "Nandedkar"
};
$scope.name = 'World';
var onUserComplete = function(response){
$scope.user = response.data;
}
var onError = function(reason){
$scope.error = "dfdfdf" ;
}
$http.get("https://api.github.com/users/odetocode")
.then(onUserComplete,onError);
$scope.person = person;
};
module.js
var app = angular.module("githubViewer", []);
app.controller("MainCtrl", MainCtrl);
index.html
<!DOCTYPE html>
<html ng-app="githubViewer">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.3/angular.js" data-semver="1.4.3"></script>
<script src="app.js"></script>
<script src="module.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{person.firstName}}!</p>
<div>Login : {{user.login}}</div>
</body>
(simple plunkr demo here)
SUMMARY:
There is a leak using ng-repeat after the 2nd wave iterating over an 'array' of custom objects like this :
<div ng-repeat="d_sampleObject in mySampleObjects">
{{d_sampleObject.description}}
</div>
Memory profile reveals an extra 'd_sampleObject' left over and not de-referenced. More details (via a controller and an injected service) below. A simple demonstration also in the provided plunkr link. Any thoughts and help greatly appreciated in advance!
NOTE: 'mySampleObjects' is an array of the following instances:
ml.MySampleObject = function (id) {
this.id = id;
this.description = 'this is object #:' + ' '+id;
}
DETAILS:
I have a custom object model that reflects the business domain objects that we utilize in our AngularJS app. I have found that when I pass an instance of one of my custom objects to ng-repeat, a reference is kept to it (I think by Angular) and memory is not freed. This happens on the second 'wave' (click on 'refresh') of the ng-repeat as it iterates, again, over its array of objects. This leak is exposed in my Profile tests (in Chrome) . Here is a short example in plunkr. Click on 'refresh' button once (or more) to see the extra 'd_sampleObject' object instance that is leaked (in Chrome Profile Inspection). Note, the 'd_sampleObject' name is only used when passed to ng-repeat. I have included screenshots of the extra object instance ('d_sampleObject') that is being leaked further below. Why is there a leak and how can it be avoided?
(Note, I have found if I don't iterate over my object collection (JS array) thru an object but rather thru a primitive index ('integer'), there is no leak. The leak seems to only happen when I use an object reference as a result of ng-repeat iterations)
SIMPLE HTML:
<!DOCTYPE html>
<html ng-app="memoryleak">
<head>
<meta charset="utf-8" />
<title>Memory Leak Test</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.13/angular.min.js" data-semver="1.3.13"></script>
<script src="app.js"></script>
<script src="dataservice.js"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="d_sampleObject in mySampleObjects">
{{d_sampleObject.description}}
</div>
<br>
<button ng-click="redo()">Number of refreshes: {{numRedos}}!</button>
</body>
</html>
SIMPLE APP.JS
(function(ml) {
var app = angular.module('memoryleak',['servicemodule']);
app.controller('MainCtrl', ['$scope', 'dataservice', function($scope, dataservice) {
$scope.redo = function () {
$scope.numRedos++;
$scope.mySampleObjects = dataservice.myObjectCollection;
dataservice.redo();
}
$scope.redo();
}]);
}(window.MEMLEAK = window.MEMLEAK || {}));
SIMPLE dataservice.js
(function(ml) {
'use strict';
var serviceModule = angular.module('servicemodule',[]);
serviceModule.factory('dataservice', ['$rootScope', '$http',
function ($rootScope, $http) {
this.myObjectCollection = [];
this.redo = function () {
this.numRedos++;
// that.myObjectCollection = [];
this.myObjectCollection.length = 0;
for (var i = 0; i < 10; i++) {
var sampleObject = new ml.MySampleObject(i);
that.myObjectCollection.push(sampleObject);
}
sampleObject=null;
}
ml.MySampleObject = function (id) {
this.id = id;
this.description = 'this is object #:' + ' '+id;
}
return this; //return the entire service to make methods accessible to dependents
}]);
}(window.MEMLEAK = window.MEMLEAK || {}));
SCREENSHOT 1: (FIRST PAGE LOAD--there are 10 'mySampleObjects' created)
SCREENSHOT 2: (CLICKED ON REFRESH--there is an 11th mySampleObject created/leaked with a reference to the instance name of 'd_sampleObject' passed to ng-repeat.)
There is acknowledgement by the AngularJS folks that this is indeed a bug in the framework. A fix and pull request has been posted.
I have also asked what the timeframe is for a formal fix.