I try to make my own button using directive.
The dialog (bootstrap.dialog) should be hired after clicking the button.
But, it won't.
Tried without click event, it works.
Using this:
- AngularJS v1.0.8
- Bootstrap 2.3.2
- Angular-bootstrap 0.3.0
here's my directive...
.directive("ucayButton", function($dialog) {
return {
restrict: 'EA',
template: '<button ng-transclude></button>',
replace: true,
transclude: true,
link: function(scope, element, attrs) {
element.addClass('btn btn-primary');
var t = '<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" ng-click="close()" aria-hidden="true">×</button>' +
'<h4 class="modal-title">Modal title</h4>' +
'</div>' +
'<div class="modal-body">' +
'<p>One fine body…</p>' +
'</div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ng-click="close()">Close</button>' +
'<button type="button" class="btn btn-primary" ng-click="close()">Save changes</button>' +
'</div>' +
'</div><!-- /.modal-content -->' +
'</div><!-- /.modal-dialog -->';
var modalOpts = {
backdrop: true,
keyboard: true,
backdropClick: true,
template: t,
controller: 'dialogCtrl'
};
scope.openDialog = function(){
console.log('confirmation called'); //always shown when function was called
var d = $dialog.dialog(modalOpts);
d.open().then(function(result){
if(result)
{
alert('dialog closed with result: ' + result);
}
});
};
angular.forEach(element, function(el) {
el.addEventListener('click', function() {
scope.openDialog(); // the function was hired, but the dialog didn't
});
});
scope.openDialog(); //hired
}
};
})
addEventListener is not an angular function, so when you perform code that affects $scope variables you need to get those changes back into the digest cycle.
Try this instead:
el.addEventListener('click', function() {
if(scope.$$phase) {
scope.openDialog();
} else {
scope.$apply(function() {
scope.openDialog();
});
}
});
This checks the $$phase of the scope, which is truthy if you are running code from within a digest cycle. If you are already in a digest cycle then there is no need to wrap the code with an $apply call. If you are not then wrapping the code in an $apply call lets angular know that it needs to digest the changes you are making.
Related
I have buttons like submit, reject and cancel. If i click on this button a small div with comment shows up and a ok and cancel button will be shown. On click of ok popup with 'are you sure' message popups up.
<button type="button" ng-click="showDiv = !showDiv">Submit
</button>
<button type="button" ng-click="showDiv = !showDiv">Reject
</button>
I am using a popup modal directive like this for the small div as i mentioned above:
<div ng-show="showDiv">
<yes-no msg="are you sure"></yes-no>
</div>
Now I want to change the msg on click of each button. Suppose if i click 'Submit' my msg should be 'are you sure to submit' if i click 'reject' my msg should be 'are you sure to reject'.
directive:
mainapp.directive('yesNo', function($modal){
return {
restrict: 'A',
scope: {
msg: '#msg'
},
link: linkFn
};
function linkFn(scope, element, attrs) {
var msg = attrs.msg;
var modalTemplate = '<div class="modal-content">' +
' <div class="modal-body">' +
' <h4 class="modal-title">' + msg + '</h4>' +
' </div>';
var modalInstance = $modal.open({
template: modalTemplate,
controller: 'yesNoModalCtrl'
});
});
}
});
How to achieve this? I will not be able to post the code here.
<input type="button" name="submit" ng-click="buttonclick('are you sure to submit')"/>
<input type="button" name="reject" ng-click="buttonclick('are you sure to reject')"/>
<div ng-show="showDiv">
<yes-no msg={{message}}></yes-no>
</div>
In you main controller:
app.controller("mainController", function($scope){
$scope.message="";
$scope.buttonclick = function(msg){
$scope.message= msg;
}
});
app.directive("yesNo", function(){
return{
restrict: 'E',
scope:{
msg:'#'
},
link: linkFn,
controller: 'yesNoModalCtrl'
}
});
In directives Link function
function linkFn(scope, element, attrs) {
attrs.$observe('msg', function(msg) {
// this function gets triggered each time "msg" is changed
// You can do whatever you want here
if (msg) {
var modalTemplate = '<div class="modal-content">' +
' <div class="modal-body">' +
' <h4 class="modal-title">' + msg + '</h4>' +
' </div>';
var modalInstance = $modal.open({
template: modalTemplate,
controller: 'yesNoModalCtrl'
});
}
});
}
plunker
Create a controller function:
$scope.showModal = function(msg) {
$scope.showDiv = !$scope.showDiv;
$scope.msg = msg;
}
And in your buttons:
<button type="button" ng-click="showModal('Are you sure')">Submit
</button>
<button type="button" ng-click="showModal('Some other text')">Reject
</button>
<div ng-show="showDiv">
<yes-no msg="msg"></yes-no>
</div>
I am building some kind of date-picker, which is actually 2 date pickers.
one for start date and the other for end date.Every datepicker element generate a template of 2 input tags(). I want to pass data from input's value attribute to the controller.
I have tried to define fields in the inner scope which are 2-way data binding(dateOne and dateTwo) but apparently no effect and no real data is pass between the 2 fields.
My other approach is using ng-model, but I have little exprience with this feature and I don't know the rules for that.
Here is my code
angular.module('directives', [])
.directive('datepicker', ['$timeout',function ($timeout) {
// Runs during compile
return {
scope: {
id: '#',
"class": '#',
dateOne: '=',
dateTwo: '='
},
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
template: '<div id="{{id}}" class="{{class}}">'+
'<div class="date-wrapper">'+
'<label for="datepicker-start">From:</label>'+
'<div class="fieldWrapper">'+
'<input id="datepicker-start" type="date" placeholder="Select date" value={{dateOne}} />'+
'<a class="calendar"></a>'+
'</div>'+
'</div>'+
'<div class="date-wrapper">' +
'<label for="datepicker-end">To:</label>' +
'<div class="fieldWrapper">' +
'<input id="datepicker-end" type="date" placeholder="Select date" value={{dateTwo}}/>' +
'<a class="calendar"></a>' +
'</div>' +
'</div>'+
'</div>'
,
replace: true,
link: function($scope, iElm, iAttrs, controller) {
console.log('directive link function');
console.log('directive iAttrs', iAttrs);
$(".date-wrapper").each(function (index) {
console.log('directive index', index);
$input = $(this).find('input');
$btn = $(this).find('.calendar');
console.log('input', $input[0]);
console.log('btn', $btn[0]);
$input.attr('type', 'text');
var pickerStart = new Pikaday({
field: $input[0],
trigger: $btn[0],
container: $(this)[0],
format: 'DD/MM/YYYY',
firstDay: 1
});
$btn.show();
});
}
};
}]);
------------------------Updated Code -----------------------------------
angular.module('directives', [])
.directive('datepicker', ['$timeout',function ($timeout) {
// Runs during compile
return {
scope: {
id: '#',
"class": '#',
dateOne: '=',
dateTwo: '='
},
restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
template: '<div id="{{id}}" class="{{class}}">'+
'<div class="date-wrapper">'+
'<label for="datepicker-start">From:</label>'+
'<div class="fieldWrapper">'+
'<input id="datepicker-start" type="date" placeholder="Select date" ng-model=dateOne />' +
'<a class="calendar"></a>'+
'</div>'+
'</div>'+
'<div class="date-wrapper">' +
'<label for="datepicker-end">To:</label>' +
'<div class="fieldWrapper">' +
'<input id="datepicker-end" type="date" placeholder="Select date" ng-model=dateTwo />' +
'<a class="calendar"></a>' +
'</div>' +
'</div>'+
'</div>'
,
replace: true,
link: function($scope, iElm, iAttrs, controller) {
console.log('directive iAttrs', iAttrs);
$(".date-wrapper").each(function (index) {
console.log('directive index', index);
$input = $(this).find('input');
$btn = $(this).find('.calendar');
console.log('input', $input[0]);
console.log('btn', $btn[0]);
$input.attr('type', 'text');
var pickerStart = new Pikaday({
field: $input[0],
trigger: $btn[0],
container: $(this)[0],
format: 'DD/MM/YYYY',
firstDay: 1
});
$btn.show();
});
$scope.$watch(iAttrs.dateOne, function (newValue, oldValue) {
console.log('newValue', newValue);
console.log('oldValue', oldValue);
}, true);
}
};
Actually you are almost there, I've done something very similar to what you have described and here was my approach to solve it (I used the UI-Bootstrap Date picker in my case).
The way you would send data from your directive to your controller is by using callbacks, rather than simple watches. If you would have used = you would have to set watches in your controller (and directive) to watch for value changes, it's bad practice overall and extra code.
So basically what you need to do is
In you directive definition object bind a callback method/function using the & sign like so
scope: {
onSelect: "&" // onSelect is our callback function in the ctrl
}
You then supply the callback attribute a function bound to the controller's $scope, but you pass it a function reference (not a function call as you would in something like ng-changed). like so
<my-directive on-selected="onSelected"></my-directive>
Then you define what onSelected should do, lets say I want to print the selected date
// inside controller
$scope.onSelected = function(time) {
console.log("Time selected: ", time);
}
Note that we pass the time argument from the directive to the controller like so, scope.onSelect() is actually a curried function, meaning it will return a function once called (that is, if you provided it with a function, you could test it using angular.isFunction), so you should call the curried function and provide it your argument, scope.onSelect()(time).
scope.selectDate = function(time) {
if (angular.isFunction(scope.onSelect())) {
// we use isFunction to test if the callback function actually
// points to a valid function object
scope.onSelect()(time); // we pass out new selected date time
}
}
Here is a plunk that shows what I mean.
Replace value in the template with ng-model=dateOne and the same with dateTwo.
I suggest to use a dedicated controller for this directive and not doing the logic inside the link function.
app.directive('someDirective', function () {
return {
restrict: 'A',
controller: 'SomeController',
controllerAs: 'ctrl',
template: '{{ctrl.foo}}'
};
});
Read more here http://blog.thoughtram.io/angularjs/2015/01/02/exploring-angular-1.3-bindToController.html
I'm trying to fill the body attribute, so my module will print it as it's body part, yet I have no idea why it's not working, even I tried $scope.$apply() in several places.
var app = angular.module('ClientLogger', ['ngRoute'])
app.controller('global', function($scope, $compile) {
$scope.window = window
$scope.sampletext = "sampleText";
$scope.showModal = false;
$scope.toggleModal = function(text) {
$scope.text = text;
$scope.showModal = !$scope.showModal;
};
});
app.directive('modal', function() {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body" ng-transclude> {{ body }} </div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.body = attrs.body;
scope.$watch(attrs.visible, function(value) {
if (value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
});
<body ng-app='ClientLogger' ng-controller='global'>
<a class="btn btn-default" ng-click="toggleModal(sampletext)"> sample </a>
<modal title="Message Details" body="{{text}}" visible="showModal"></modal>
</body>
As you can see, after i click the link, it will change the variable $scope.text and it will reflect to the modal. But I can't manage to do it. Since I#m very new about this Angular, I still have troubles about understanding its mechanics, so any specific details will be really good for me.
Any recommendations?
When you retrieve your attrs.body, you are retrieving undefined data.
You can use a Factory to share your data. You have to know that all angular services are singletons, so there is only one instance of a given service.
Thanks to this, you can easily share your data between your controller and your directive.
So when you will trigger an action in your Controller, you will set your data into your factory, then you can retrieve your data into your directive.
Controller
(function(){
function Controller($scope, Service) {
$scope.myText = 'sample';
$scope.showModal = false;
$scope.toggleModal = function() {
//Set the myText value by using our Service.set() method
Service.set($scope.myText);
$scope.showModal = !$scope.showModal;
};
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Service
(function(){
function Service(){
var data;
function set(value){
data = value;
}
function get(){
return data;
}
return {
get: get,
set: set
};
}
angular
.module('app')
.factory('Service', Service);
})();
Directive
(function(){
function modal(Service) {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body"> {{ body }} </div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function (scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value) {
value
//Retrieve your data by using Service.get() method, and show our modal
? ( scope.body = Service.get(), $(element).modal('show') )
: $(element).modal('hide')
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
}
angular
.module('app')
.directive('modal', modal);
})();
Then, you can call your directive into your HTML.
HTML
<body ng-app='app' ng-controller="ctrl">
<input type="text" ng-model="myText">
<a class="btn btn-default" ng-click="toggleModal()"> sample </a>
<modal title="Message Details" visible="showModal"></modal>
</body>
You can see the Working Plunker
I want to use the abilitie of ui-router to test the active state in a ng-class
If I try this it does not work :
navTest.$inject = ["$compile", "$navbar", "$state"];
function navTest($compile, $navbar, $state) {
var defaults = $navbar.defaults;
var navTest = {
restrict: 'E',
template: '<nav class="navbar navbar-default" role="navigation">' +
'<div class="container-fluid">' +
'<div class="navbar-header">' +
'<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">' +
'<span class="sr-only">Toggle navigation</span>' +
'<span class="icon-bar"></span>' +
'<span class="icon-bar"></span>' +
'<span class="icon-bar"></span>' +
'</button>' +
'<a class="navbar-brand" href="#">Brand</a>' +
'</div>' +
'<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">' +
'<ul class="nav navbar-nav">' +
'<li data-ng-class="{active: $state.includes(\'user\')}"><a data-ui-sref="user.List">Link 1</a></li>' +
'<li data-ng-class="{active: $state.includes(\'technology\')}"><a data-ui-sref="technology.List">Link 2</a></li>' +
'</ul>' +
'</div>' +
'</div>' +
'</nav>',
replace: true
};
return navTest;
}
Now if I add a function to test the active state in the "link" function
(of the directive) and I replace : data-ng-class="{active: $state.includes(\'technology\')}" by data-ng-class="{active: isActiveState(\'technology\')}"
link : function (scope, elem, attrs) {
scope.isActiveState = function (name) {
return $state.includes(name);
};
}
Everything works fine but I don't understand why because the function does exactly the same thing ?
It's a scope thing but you don't want to use $rootScope; putting the following in your link function should be enough:
scope.$state = $state;
That's because $state is not on your scope. What I recommend is that you add a reference to $state in your scope (using your link function). If you are likely to need $state in several places, I suggest you add it to the $rootScope using the run function of your module / application:
angular
.module('yourModule')
.run([
'$rootScope',
'$state',
function ($rootScope, $state) {
$rootScope.$state = $state;
}
]);
You can use ui-router filters in template.
https://github.com/angular-ui/ui-router/wiki/Quick-Reference#filters-1
In your case:
ng-class="{ active: 'user' | includedByState }"
I have an AngularJS directive, which needs to be appended after an HTML element which called it. The elements structure can have many nested buttons on different levels of DOM structure.
Right now a directive gets appended in the wrong place instead of a container element that contains the button, which called the append function.
It looks like this:
The text should be appended after a button, which was clicked.
Directive:
app.directive('recursiveFields', function ($compile, $http) {
return {
scope: {
field: '=field',
model: '=model'
},
restrict: 'E',
replace: true,
controller: "httpPostController",
template: '<div ng-repeat="nestedField in field.nestedFields"><div ng-show="{{!nestedField.isEntity && !nestedField.isEnum}}">' + '<p ng-show={{nestedField.isRequired}}>{{nestedField.name}}*: </p>' + '<p ng-show={{!nestedField.isRequired}}>{{nestedField.name}}: </p>' + '<input type="text" ng-model="model[nestedField.name]" ng-change="getCreateEntityAsText()"' + 'class="form-control" placeholder="{{parseClassName(nestedField.type)}}">' + '</div>' + '<div ng-show="{{nestedField.isEnum}}">' + '<p ng-show={{nestedField.isRequired}}>{{nestedField.name}}*: </p>' + '<p ng-show={{!nestedField.isRequired}}>{{nestedField.name}}: </p>' + '<select ng-model="model[nestedField.name]" ng-change="getCreateEntityAsText()" class="form-control">' + '<option></option>' + '<option ng-repeat="enumValue in nestedField.enumValues" label={{enumValue.name}}>{{enumValue.ordinal}}</option>' + '</select>' + '</div>' +
'<div ng-show="{{nestedField.restResourceName != null}}">' + '<accordion close-others="oneAtATime">' + '<accordion-group heading={{nestedField.name}} is-open="false">' + /*'<recursive-fields model="createEntityResource" field="field"></recursive-fields>'*/
'<button type="button" ng-click="appendDirective()">I should append a "recursiveFields" directive</button>' + '</accordion-group>' + '</accordion>' + '</div>' + '</div>',
link: function (scope, element, attrs) {
console.log("1");
if (scope.field.restResourceName != null) {
$http.get(CONSTANTS.EXPLAIN_URL + "/" + scope.field.restResourceName)
.success(function (data, status) {
scope.field.nestedFields = [];
data.content.resource.fields.forEach(function (field) {
if (field.isEnum) {
$http.get(CONSTANTS.ENUMS_URL + scope.$root.parseClassName(field.type)).success(function (data, status) {
field.enumValues = [];
for (var index in data.content.values) {
field.enumValues.push(data.content.values[index]);
}
})
}
scope.field.nestedFields.push(field);
})
})
}
scope.appendDirective = function () {
var recursiveFields = $("<p>Insert me</p>");
recursiveFields.insertAfter(element[0]);
$compile(recursiveFields)(scope);
}
}
}
})
Does anyone know how to solve this issue with Angular? Every useful answer is highly appreciated and evaluated.
Thank you.
ngClick has access to the $event that can be passed to your method like this:
<button type="button" ng-click="appendDirective($event)"
That event has a property target.
Check this: https://docs.angularjs.org/api/ng/directive/ngClick
and this: https://docs.angularjs.org/guide/expression#-event-