Here is my angular js app with html code
<html>
<head>
<title></title>
<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<script src="Scripts/angular.min.js" type="text/javascript"></script>
<script type="text/javascript" language="javascript">
angular.module("demo", []).controller('DemoController', function ($scope) {
$scope.user = {
dateOfBirth: new Date(1970, 0, 1)
}
});
</script>
</head>
<body>
<div ng-app="demo" ng-controller="DemoController">
Date Of Birth:
<my-datepicker type="text" ng-model="user.dateOfBirth" />
<br />
Current user's date of birth: <span id="dateOfBirthDisplay">{{user.dateOfBirth}}</span>
</div>
</body>
</html>
It works fine.But the moment i add a directive to it, it shows error
Error: Argument 'DemoController' is not a function, got undefined
Here is the full code with directive
<html>
<head>
<title></title>
<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<script src="Scripts/angular.min.js" type="text/javascript"></script>
<script type="text/javascript" language="javascript">
angular.module("demo", []).controller('DemoController', function ($scope) {
$scope.user = {
dateOfBirth: new Date(1970, 0, 1)
}
});
angular.module("demo", []).directive('myDatepicker', function ($parse) {
return {
restrict: "E",
replace: true,
transclude: false,
compile: function (element, attrs) {
var modelAccessor = $parse(attrs.ngModel);
var html = "<input type='text' id='" + attrs.id + "' >" +
"</input>";
var newElem = $(html);
element.replaceWith(newElem);
return function (scope, element, attrs, controller) {
var processChange = function () {
var date = new Date(element.datepicker("getDate"));
scope.$apply(function (scope) {
// Change bound variable
modelAccessor.assign(scope, date);
});
};
element.datepicker({
inline: true,
onClose: processChange,
onSelect: processChange
});
scope.$watch(modelAccessor, function (val) {
var date = new Date(val);
element.datepicker("setDate", date);
});
};
}
};
});
</script>
</head>
<body>
<div ng-app="demo" ng-controller="DemoController">
Date Of Birth:
<my-datepicker type="text" ng-model="user.dateOfBirth" />
<br />
Current user's date of birth: <span id="dateOfBirthDisplay">{{user.dateOfBirth}}</span>
</div>
</body>
</html>
i'm following the tutorial from this link http://henriquat.re/directives/advanced-directives-combining-angular-with-existing-components-and-jquery/angularAndJquery.html
You are defining the demo module twice. By passing a second argument to the angular.module method, you create a new module. Don't pass the second argument to return a reference to an existing module:
// Create a module
angular.module("demo", []).controller('DemoController', function ($scope) {
// ...
});
// Get reference to module
angular.module("demo").directive('myDatepicker', function ($parse) {
// ^ Only one argument
});
Note that the controller method returns the module to allow chaining, and the module method also obviously returns the module, so you also have two other options:
Chaining:
angular.module("demo", []).controller('DemoController', function ($scope) {
// ...
}).directive('myDatepicker', function ($parse) {
// ...
});
Storing reference to module:
var demo = angular.module("demo", []);
demo.controller('DemoController', function ($scope) {
// ...
});
demo.directive('myDatepicker', function ($parse) {
// ...
});
Related
First of all, the way i am doing may not be correct. But i will explain the problem:
1) I am creating directive called as < firstDirective >
2) when the clicks on a button in the first directive, then I am trying to insert the second directive dynamically at runtime
As follows:
<!DOCTYPE html>
<html>
<script src="lib/angular/angular.js"></script>
<body ng-app="myApp">
<first-directive></first-directive>
<script>
var app = angular.module("myApp", []);
app.directive("firstDirective", function() {
return {
template : '<h1>This is first directive!</h1> <br / ><br / ><button type="button" ng-click="firstCtrl()">Click Me to second directive!</button> <div id="insertSecond"></div> ',
controller: function ($scope) {
$scope.firstCtrl = function($scope) {
angular.element(document.querySelector('#insertSecond')).append('<second-directive></second-directive>');
}
}
}
});
app.directive("secondDirective", function() {
return {
template : '<h1>This is second directive!</h1> <br / ><br / >',
controller: function ($scope) {
}
}
});
</body>
</html>
But it is not working, i mean, it is inserting the "< second-directive > < / second-directive >" text but not the content as per the directive above.
I am new to angular js, I think we can do this in a different way or my approach itself is not correct. But all i want to insert the second directive dynamically.
EDIT:: I got the solution for this, thanks to the George Lee:
Solution is we have to compile as follows, but didn’t pass scope object to the function:
<!DOCTYPE html>
<html>
<script src="lib/angular/angular.js"></script>
<body ng-app="myApp">
<first-directive></first-directive>
<script>
var app = angular.module("myApp", []);
app.directive("firstDirective", function($compile) {
return {
templateUrl : '<h1>This is first directive!</h1> <br / ><br / ><button type="button" ng-click="firstCtrl()">Click Me to second directive!</button> <div id="insertSecond"></div> ',
controller: function ($scope) {
$scope.firstCtrl = function() {
var ele = $compile('<second-directive></second-directive>')($scope);
angular.element(document.querySelector('#insertSecond')).append(ele);
}
}
}
});
app.directive("firstDirective", function() {
return {
templateUrl : '<h1>This is second directive!</h1> <br / ><br / >',
controller: function ($scope) {
}
}
});
Also, this link , gives very good explanation of how to dynamically compile and inject the templates.
You can use the $compile service in Angular, make sure you include it in the dependency injection.
app.directive("firstDirective", ['$compile', function($compile) {
...
controller: function ($scope) {
$scope.firstCtrl = function() {
var ele = $compile('<second-directive></second-directive>')($scope);
angular.element(document.querySelector('#insertSecond')).append(ele);
}
}
There is one more problem instead of templateUrl, template should be there
<!DOCTYPE html>
<html>
<script data-require="angular.js#1.0.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js" data-semver="1.0.8"></script>
<first-directive></first-directive>
<script>
var app = angular.module("myApp", []);
app.directive("firstDirective", function($compile) {
return {
template : '<h1>This is first directive!</h1> <br / ><br / ><button type="button" ng-click="firstCtrl()">Click Me to second directive!</button> <div id="insertSecond"></div> ',
controller: function ($scope) {
$scope.firstCtrl = function() {
var ele = $compile('<second-directive></second-directive>')($scope);
angular.element(document.querySelector('#insertSecond')).append(ele);
}
},
restrict: "EAC"
}
});
app.directive("secondDirective", function() {
return {
template : '<h1>This is second directive!</h1> <br / ><br / >',
controller: function ($scope) {
},
restrict: "EAC"
}
});
</script>
</body>
</html>
I'm trying to create an angular component, a timepicker, using plain javascript models, I want the controller of the component expose an api and also working with ngModel.
I'm pretty newbie with angular and don't know how to work with ngModel. I have two inputs inside the template with hours and minutes. My problem is that I don't know how to pass the ngmodel parameters to the controller.
I've prepared a plunker:
http://plnkr.co/edit/aal3VP?p=preview
(function() {
var app = angular.module('plunker', []);
function DemoController() {
this.tpVal = {
hours: 10,
minutes: 0
};
}
app.controller('DemoController', DemoController);
function TimePickerModel(config) {
this.show = config.show || true;
this.hours = null;
this.minutes = null;
}
function TimePickerController() {
// API for state
this.model = new TimePickerModel({});
}
TimePickerController.prototype.show = function showTimePicker() {
this.model.show = true;
};
TimePickerController.prototype.hide = function hideTimePicker() {
this.model.show = false;
};
TimePickerController.prototype.setHours = function setHoursTimePicker(hours) {
this.model.hours = hours;
};
TimePickerController.prototype.setMinutes = function setMinutesTimePicker(minutes) {
this.model.minutes = minutes;
};
TimePickerController.prototype.setValue = function setValueTimePicker(value) {
this.model.hours = value;
this.model.minutes = value;
};
app.directive('timepicker', function($compile) {
return {
restrict: 'AE',
controller: 'TimePickerController',
scope: {},
require: 'ngModel',
templateUrl: 'timepicker.html',
link: function(scope, element, attrs, ngModel) {
//console.log('Model val: ' + ngModel.$modelValue);
//console.log('View val: ' + ngModel.$viewValue);
ngModel.$render = function() {
//Do something with your model
console.log(scope.model);
var actualValue = ngModel.$modelValue;
console.log('Model val: ' + ngModel.$modelValue.hours);
console.log('View val: ' + ngModel.$viewValue.hours);
//console.log(element.find('input')[0]);
//element.find('input')[0].val(actualValue.hours);
}
}
};
});
app.controller('TimePickerController', TimePickerController);
})();
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<script data-require="angular.js#1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<script data-require="angular.js#1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-route.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="DemoController as ctrl">
{{ctrl.tpVal}}
<timepicker ng-model="ctrl.tpVal"></timepicker>
</div>
</body>
</html>
ng-model is a standard angular directive to bound inputs values to scope property, you don't need to inject it or call the directive property with the same name. If you want to inject the values from the controller into the directive, you can use scope property for that.
in directive:
scope: {
model : '=time'
},
in index.html
<timepicker time="ctrl.tpVal"></timepicker>
Check that modification: http://plnkr.co/edit/cJ0mjI?p=preview.
You also can see how changing model value inside the directive can propargate outside by adding dummy increaseHours function in directive;
I have a AngularJS directive which takes an ID and makes a lookup on this ID to get col-width, hide-state and order for a given flexbox element.
What I d like to do is to add a ng-if=false attribute to the element if its hide-state is true. Is there any way, how I can add ng-if attribute from within a directive to a element?
My current code:
.directive("responsiveBehaviour", ['ResponsiveService', function(ResponsiveService){
var linkFunction = function(scope, element, attributes){
var id = attributes["responsiveBehaviour"];
var updateResponsiveProperties = function(){
element.attr("column-width", ResponsiveService.getWidth(id));
if(ResponsiveService.getOrder(id)){
element.attr("order", ResponsiveService.getOrder(id));
}
if(ResponsiveService.getHidden(id) == true){
element.attr("hidden", "");
} else {
element.removeAttr("hidden");
}
};
if(id) {
scope.$watch('device', function () {
updateResponsiveProperties();
});
}
};
If I add
element.attr("ng-if", false);
instead of
element.attr("hidden", "");
it just renders out the ng-if attribute to the element but there is no action happening, so the element is still rendered and visible although ng-if is false.
Do you have any idea on how to achieve what I am looking for?
Thanks in advance.
Greets
something like below:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive( 'test', function ( $compile ) {
return {
restrict: 'E',
scope: { text: '#' },
template: '<p ng-click="add()">Jenish</p>',
controller: function ( $scope, $element ) {
$scope.add = function () {
var el = $compile( "<test text='n'></test>" )( $scope );
$element.parent().append( el );
};
}
};
});
working plunk is here
Update
Here is simple example as you requested.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.add = function () {
alert('Jenish');
$scope.cond = false;
}
$scope.cond = true;
});
app.directive( 'test', function ( $compile ) {
return {
restrict: 'E',
template: '<p ng-click="add()">Click me to hide</p>',
link: function ( $scope, $element, attr ) {
var child = $element.children().attr('ng-if', 'cond')
console.log($element)
$compile(child)($scope);
}
};
});
<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.3.x" src="https://code.angularjs.org/1.3.13/angular.js" data-semver="1.3.13"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<test></test>
</body>
</html>
I hope this would help you.
In angular js, I want to create a validator that will cause the ng-model value to become invalid when another value is specified. Now I have something that works fine for angular js 1.1.4 (which I was using because I was using an old plunkr), but when I switch to 1.1.5, it stops working.
I am sure I am doing something wrong with the scope, but I am not sure what.
Here is my code (plunkr here: http://plnkr.co/edit/Ug9oM1LNqPpTsONhRTnG?p=preview)
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.doSomething = function () {
alert('Submitted!');
}
$scope.data = {};
$scope.data.value = new String('blah');
$scope.data.value.$$error = 'My Error';
$scope.data.toggleError = function() {
if ($scope.data.value.$$error) {
$scope.data.value.$$error = null;
}
else {
$scope.data.value.$$error = "SOME ERROR";
}
};
console.log($scope.data.value instanceof String);
});
app.directive('serverError', function (){
return {
require: 'ngModel',
scope:true,
link: function(scope, elem, attr, ngModel) {
scope.$watch('attr.errorValue', function() {
console.log("The error value is " + scope.errorValue);
ngModel.$setValidity('serverError', scope.errorValue == null);
});
}
};
});
Here is my HTML:-
<!DOCTYPE html>
<html ng-app="angularjs-starter">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<form name="myForm" ng-submit="doSomething()">
<input type="text" name="fruitName" ng-model="data.value" serverError errorValue="data.value.$$error" />
<div>{{ data.value.$$error }}</div>
<span class="invalid" ng-if="myForm.fruitName.$error.serverError">
{{data.value.$$error}}
</span>
<br/>
<button type="submit" ng-disabled="myForm.$invalid">Submit</button>
<input type="button" ng-click="data.toggleError()" value="Toggle Error"/>
</form>
</body>
</html>
As soon as I change from 1.1.3 to 1.2.0, my directive stops working.
I worked it out.
http://plnkr.co/edit/Ug9oM1LNqPpTsONhRTnG?p=preview
The directive is simple enough:-
app.directive('serverError', function($parse) {
return {
// restrict to an attribute type.
restrict: 'A',
// element must have ng-model attribute.
require: 'ngModel',
// scope = the parent scope
// elem = the element the directive is on
// attr = a dictionary of attributes on the element
// ctrl = the controller for ngModel.
link: function(scope, elem, attr, ctrl) {
scope.$watch(attr.serverError, function(newValue, oldValue) {
if (newValue != null) {
ctrl.$setValidity('serverError', false);
}
else {
ctrl.$setValidity('serverError', true);
}
});
}
};
});
I want to broadcast angular event from javascript function i.e angular.injector(['ng', 'myModule']).get("mySharedService").prepForBroadcast('hello');
By using above line I can invoke prepForBroadcast() but I can't catch event in $scope.$on()
Note: I want to call prepForBroadcast() method from javascript function.
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<!-- SPELLS -->
<!-- load angular via CDN -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="script.js"></script>
<style>
.question{
border:1px solid darkgray;
padding:10px;
margin-bottom:10px;
}
</style>
</head>
<body>
<div ng-app="myModule">
<div id="appID" ng-controller="ControllerZero">
<input ng-model="message" >
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
<my-component ng-model="message"></my-component>
</div>
</body>
<script>
angular.injector(['ng','myModule']).get("mySharedService").prepForBroadcast('hello');
</script>
</html>
script.js file
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
console.log('prepForBroadcast');
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
console.log('broadcastItem');
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
myModule.directive('myComponent', function(mySharedService) {
return {
restrict: 'E',
controller: function($scope, $attrs, mySharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'Directive: ' + mySharedService.message;
});
},
replace: true,
template: '<input>'
};
});
function ControllerZero($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
console.log('handle event');
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];
angular.injector() creates a new injector, and with it a new $rootScope. The event will be broadcasted on this new $rootScope instead of on the one your controllers are listening on.
You need to retrieve the injector already associated with your application:
angular.element(domElement).injector();
You also need to manually trigger the digest loop for the data bindings to update, for example by using $apply.
Example:
angular.element(document).ready(function() {
var element = angular.element(document.querySelector('.ng-scope'));
var injector = element.injector();
var scope = element.scope();
scope.$apply(function() {
injector.get('mySharedService').prepForBroadcast('hello');
});
});
Demo: http://plnkr.co/edit/NDKBdzSmvN1xY7alafir?p=preview
Another way of publishing events from one controller and listening them in other controllers would be to use angular-PubSub module.
The PubSub makes only subscribers to listen to the published events unlike the $rootScope.$broadcast in which it sends event to all the Scopes in Scope hierarchy making it inefficient as compared to the PubSub approach.