I've created two custom directives, one parent and one child directive that communicate with each other through parent directive controller.
Parent Directive
app.directive('attrsCtrl', function ($compile) {
return {
restrict: 'E',
scope: {
attributes: '=array',
options: '='
},
controller: function ($scope, $element) {
$scope.attributes = [];
this.returnOptions = function(){
return $scope.options;
}
this.saySomething = function (obj) {
$scope.attributes.push(obj);
alert(obj.name + "/" + obj.type.name);
var newDirective = angular.element('<attributes> </attributes>');
$element.append(newDirective);
$compile(newDirective)($scope);
}
}
}})
Child Directive
app.directive('attributes', function ($compile) {
return {
restrict: 'E',
require: '^attrsCtrl',
template: '<div ng-class="classInput">' +
' <div class="col-md-6" style="padding-left: 0;">' +
' <label>Nome do Atributo</label>' +
' <input type="text" class="form-control input-sm" placeholder="Nome do Atributo" ng-model="attrname" ng-change="validation()" ng-disabled="afterSend">' +
' </div>' +
' <div class="col-md-4 " style="padding-left: 0;"> ' +
' <label>Tipo do Atributo</label> ' +
' <select class="form-control input-sm" ng-options="option.name for option in options" ng-model="attrtype" ng-disabled="afterSend"></select>' +
' </div> ' +
' <div class="col-md-2" style="padding-right: 0;"> ' +
' <label> </label>' +
' <button type="button" class=" btn btn-default pull-right" ng-click="changeButton()" style="margin-top: 1em;" ng-disabled="disabled"> Adicionar </button>' +
' </div> ' +
'</div>' +
'<div class="clearfix></div>',
link: function (scope, element, attrs, attrsCtrl) {
scope.classInput = 'form-group';
scope.disabled = true;
scope.afterSend = false;
scope.options = attrsCtrl.returnOptions();
scope.changeButton = function () {
scope.attr = {
name: scope.attrname,
type: scope.attrtype
};
attrsCtrl.saySomething(scope.attr);
scope.disabled = true;
scope.afterSend = true;
}
scope.validation = function () {
if (scope.attrname.length < 6) {
scope.classInput = 'form-group has-error';
} else {
scope.classInput = 'form-group has-success';
scope.disabled = false;
}
}
}
};})
HTML
<attrs-ctrl array="myAttributes" options="options" >
<attributes/>
</attrs-ctrl>
My issue is that after i click twice to create a directive, it automatically creates another directive but i can't type anything in it! This behavior was supposed to happen only after i click on "Adicionar" button.
There are two problems here
In above example, link function contains scope.disabled = true;, it should be changed to scope.disabled = false; I guess it is your typo while pasting the example here.
Secondly, I guess you have added the parent directive dependency explicitly as you want to create independent child directive. As per your code, the scope between parent and child directive is getting shared. So, scope of parent directive is being shared across all child directives and all fields are appearing disabled.
Add scope:{}, to your directive function as given below...
This will create separate child directive scope.
There is a nice article explaining scopes in details: https://github.com/angular/angular.js/wiki/Understanding-Scopes
app.directive('attributes', function ($compile) {
return {
restrict: 'E',
require: '^attrsCtrl',
scope: {},
template: '<div ng-class="classInput">' +
' <div class="col-md-6" style="padding-left: 0;">' +
' <label>Nome do Atributo</label>' +
' <input type="text" class="form-control input-sm" placeholder="Nome do Atributo" ng-model="attrname" ng-change="validation()" ng-disabled="afterSend">' +
' </div>' +
' <div class="col-md-4 " style="padding-left: 0;"> ' +
' <label>Tipo do Atributo</label> ' +
' <select class="form-control input-sm" ng-options="option.name for option in options" ng-model="attrtype" ng-disabled="afterSend"></select>' +
' </div> ' +
' <div class="col-md-2" style="padding-right: 0;"> ' +
' <label> </label>' +
' <button type="button" class=" btn btn-default pull-right" ng-click="changeButton()" style="margin-top: 1em;" ng-disabled="disabled"> Adicionar </button>' +
' </div> ' +
'</div>' +
'<div class="clearfix></div>',
link: function (scope, element, attrs, attrsCtrl) {
scope.classInput = 'form-group';
scope.disabled = false;
scope.afterSend = false;
scope.options = attrsCtrl.returnOptions();
scope.changeButton = function () {
scope.attr = {
name: scope.attrname,
type: scope.attrtype
};
attrsCtrl.saySomething(scope.attr);
scope.disabled = true;
scope.afterSend = true;
}
scope.validation = function () {
console.log("validate");
if (scope.attrname.length < 6) {
scope.classInput = 'form-group has-error';
} else {
scope.classInput = 'form-group has-success';
scope.disabled = false;
}
}
}
};})
Related
I recently change the code of a page that I had in a html page to an aspx one because I needed to implement a master page. The code and the function worked perfectly fine in the htmlpage but since I had changed it to the aspx page the bootbox dialog stop working properly, as soon as I click on it, it closes immediately and refreshes the page. These are the scripts that Im using.
<script src="Scripts/jquery-3.1.1.js"></script>
<script src="Scripts/popper.js"></script>
<script src="Scripts/bootstrap.js"></script>
<script src="Scripts/bootbox.js"></script>
Here are some of the functions of the page:
function disableTool(Herramientaid) {
bootbox.confirm({
message: "¿Estas seguro que deseas eliminar este herramienta?",
buttons: {
confirm: {
label: 'Si',
className: 'btn-success'
},
cancel: {
label: 'No',
className: 'btn-danger'
}
},
callback: function (result) {
if (result) {
sendRequest('DisableTool', { "Herramientaid": Herramientaid },
function (response) {
if (response[0] == 'OK') {
gettool();
bootbox.alert('La herramienta se ha eliminado');
}
else {
bootbox.alert('Error! ' + response[0]);
}
});
}
}
});
}
function UpdateTool(ToolControl) {
var Herramientaid = $(ToolControl).parent().parent().find('td').eq(0).html();
var NombreH = $(ToolControl).parent().parent().find('td').eq(1).html();
var Adminid = $(ToolControl).parent().parent().find('td').eq(2).html();
var HDesc = $(ToolControl).parent().parent().find('td').eq(3).html();
bootbox.dialog({
title: "Actualizar herramientas",
message: '<div class="row"> ' +
'<div class="col-md-12"> ' +
'<div class="form-group"> ' +
'<label class="col-md-3 control-label" for="txNombreH">Nombre:</label> ' +
'<div class="col-md-9"> ' +
'<input type="text" id="txNombreH" class="form-control" placeholder="Nombre" value=""' + NombreH + '/>' +
'</div>' +
'</div>' +
'<div class="form-group"> ' +
'<label class="col-md-3 control-label" for="txAdminid">ID del Administrador:</label> ' +
'<div class="col-md-9"> ' +
'<input type="text" id="txAdminid" class="form-control" placeholder="Administrador ID" value=""' + Adminid + '/>' +
'</div>' +
'</div>' +
'<div class="form-group"> ' +
'<label class="col-md-3 control-label" for="txHDesc">Descripcion de la herramienta:</label> ' +
'<div class="col-md-9"> ' +
'<input type="text" id="txHDesc" class="form-control" placeholder="Descripcion" value=""' + HDesc + '/>' +
'</div>' +
'</div>' +
'</div>' +
'</div>',
buttons: {
No: {
label: "No",
className: "btn-danger",
callback: function () {
}
},
Yes: {
label: "Si",
className: "btn-success",
callback: function () {
var newNombreH = $('#txNombreH').val();
var newAdminid = $('#txAdminid').val();
var newHDesc = $('#txHDesc').val();
sendRequest('UpdateTool', {
"Herramientaid": Herramientaid, "NombreH": newNombreH, "Adminid": newAdminid, "HDesc": newHDesc
},
function (response) {
if (response[0] == 'OK') {
gettool();
bootbox.alert('Se ha configurado la herramienta');
}
else {
UpdateTool(ToolControl);
bootbox.alert('Error! ' + response[0]);
}
}
)
}
}
}
});
}
I had this same issue today, updating to latest and greatest bootbox didn't help. I eventually tracked down that the button click for "Close", "Ok", even "Esc" was triggering the submission of the form event.
I've had to add a propagation catcher after calling my dialog:
$(".dialog-class-name").on('hidden.bs.modal', function (e) {
e.preventDefault();
e.stopPropagation();
});
It feels hacky, but it doesn't involve editing the actual library, and it does work....
My directive:
'<tr ng-repeat="item in listGroup">' +
'<th scope="row">{{$index + 1}}</th>' +
'<td>{{item.name}}</td>' +
'<td ng-if="!listAll">{{item.users.length}}</td>' +
'<td ng-if="!listAll">_</td>' +
'<td>{{item.createdAt}}</td>' +
'<td><button type="submit" class="btn btn-default" ng-click="remove(item)">remove</button></td>' +
scope: {
listName: "#",
listGroup: "=",
listAll: "=",
submit: "&",
remove: "&"
HTML:
<table-list
list-name="users"
list-group="usersGroup"
list-all="usersAll"
submit="submit()"
remove="remove()">
</table-list
Controller:
$scope.remove = function(username) {
console.log(username);
/*
AV.Cloud.run('updateUserGroup', {
groupName: $scope.group.name,
users: _.pluck(selectedUsers, 'name'),
op: "add"
}, {
*/
I'm not very sure what to add in remove="remove()"> in the HTML it's supposed to receive an argument, but the argument is inside the directive.
How can I make remove() get the argument item.name?
Can pass the function name as reference to isolated scope using =
JS
scope: {
listName: "#",
listGroup: "=",
listAll: "=",
submit: "&",
remove: "="
HTML
<table-list
list-name="users"
list-group="usersGroup"
list-all="usersAll"
submit="submit()"
remove="remove"><!-- Note "()" deleted -->
</table-list>
Now you can pass arguments to the referenced function inside directive or in html
In Directive:
use: ng-click="remove({itemToRemove:item})"
In HTML:
<table-list
list-name="users"
list-group="mainAppCtrlAs.usersGroup"
list-all="usersAll"
submit="submit()"
remove="mainAppCtrlAs.remove(itemToRemove)">
In Controller:
self.remove = function(itemToRemoveIs)
{
alert("Remove This\nId: = " + itemToRemoveIs.id + "\nName = " + itemToRemoveIs.name);
}
Full Code:
<!doctype html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"> </script>
<script>
var appModule = angular.module('mainApp', []);
</script>
<html ng-app="mainApp">
<head>
<script src="mainAppCtrl.js"></script>
</head>
<body>
<h2>Todo</h2>
<div ng-controller="mainAppCtrl as mainAppCtrlAs">
<table-list
list-name="users"
list-group="mainAppCtrlAs.usersGroup"
list-all="usersAll"
submit="submit()"
remove="mainAppCtrlAs.remove(itemToRemove)">
</table-list>
</div>
</body>
</html>
Controller: mainAppCtrl.js
appModule.controller('mainAppCtrl', function() {
self = this;
self.userGrp1 = {};
self.userGrp1.id = '01';
self.userGrp1.name = 'Manager';
self.userGrp1.users = ['Ashish','Ravi'];
self.userGrp2 = {};
self.userGrp2.id = '02';
self.userGrp2.name = 'Developer';
self.userGrp2.users = ['krishan','Geeta','Ritu'];
self.usersGroup = [self.userGrp1,self.userGrp2];
self.remove = function(itemToRemoveIs)
{
alert("Remove This\nId: = " + itemToRemoveIs.id + "\nName = " + itemToRemoveIs.name);
}
});
appModule.directive('tableList', [function() {
return {
template: '<div ng-repeat="item in listGroup">' +
'<th scope="row">{{$index + 1}}</th>' +
'<td>{{item.name}}</td>' +
'<td ng-if="!listAll">{{item.users.length}}</td>' +
'<td ng-if="!listAll">_</td>' +
'<td>{{item.createdAt}}</td>' +
'<td><button type="submit" class="btn btn-default" ng- click="remove({itemToRemove:item})">remove</button></td>' +
'</div><br>',
scope: {
listName : "#",
listGroup: "=",
listAll: "=",
submit: "&",
remove: "&"
} //scope
}; //return
}]);
I made a popup drop dow using AngularUI bootstrap $uibModal service to replace the original select form element.
http://jsfiddle.net/ehcj8wn7/17/
angular.module('myApp', [
'ui.bootstrap'])
.controller('myController', function ($scope) {
$scope.cities = ['Shanghai', 'Beijing'];
$scope.basicInfo = {
city: 'Shanghai'
};
$scope.updateCities1 = function () {
$scope.cities.pop();
$scope.cities.push('New York');
}
$scope.updateCities2 = function () {
$scope.cities = ['Taipei', 'Hong Kong'];
}
})
.directive('popupDropDown', ['$uibModal', function ($uibModal) {
var dropDownTemplate = '<div class="modal-header">' +
' <h3 class="modal-title">Please Select</h3>' +
'</div>' +
'<div class="modal-body">' +
' <button class="btn btn-block" ng-class="{\'btn-primary\': item===selected}" ng-repeat="item in items" ng-click="select(item)" ng-if="!cols">{{item}}</button >' +
' <div class="row no-padding" ng-if="cols===12">' +
' <div class="col-xs-1 text-center no-padding" ng-repeat="item in items">' +
' <button class="btn btn-lg no-padding" ng-class="{\'btn-primary\': item===selected}" ng-click="select(item)" >{{item}}</button >' +
' </div>' +
' </div>' +
' <div class="row no-padding" ng-if="cols===6">' +
' <div class="col-xs-2 text-center no-padding" ng-repeat="item in items">' +
' <button class="btn btn-lg no-padding" ng-class="{\'btn-primary\': item===selected}" ng-click="select(item)" >{{item}}</button >' +
' </div>' +
' </div>' +
' <div class="row no-padding" ng-if="cols===4">' +
' <div class="col-xs-3 text-center no-padding" ng-repeat="item in items">' +
' <button class="btn btn-lg no-padding" ng-class="{\'btn-primary\': item===selected}" ng-click="select(item)" >{{item}}</button >' +
' </div>' +
' </div>' +
' <div class="row no-padding" ng-if="cols===3">' +
' <div class="col-xs-4 text-center no-padding" ng-repeat="item in items">' +
' <button class="btn btn-lg no-padding" ng-class="{\'btn-primary\': item===selected}" ng-click="select(item)" >{{item}}</button >' +
' </div>' +
' </div>' +
' <div class="row no-padding" ng-if="cols===2">' +
' <div class="col-xs-6 text-center no-padding" ng-repeat="item in items">' +
' <button class="btn btn-lg no-padding" ng-class="{\'btn-primary\': item===selected}" ng-click="select(item)" >{{item}}</button >' +
' </div>' +
' </div>' +
'</div>' +
'<div class="modal-footer">' +
' <button class="btn btn-warning" type="button" ng-click="cancel();">Cancel</button>' +
'</div>';
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, controller) {
var popupDropDownAttribute = scope.$eval(attrs.popupDropDown);
var showPopupDropDown = function (evt) {
var dropDown = $uibModal.open({
animation: true,
template: dropDownTemplate,
resolve: {
items: function () {
return popupDropDownAttribute.items;
},
cols: function () {
return popupDropDownAttribute.cols;
},
selected: function () {
return element.val();
}
},
controller: function ($scope, $modalInstance, items, cols, selected) {
$scope.items = items;
$scope.cols = cols;
$scope.selected = selected;
$scope.select = function (v) {
$modalInstance.close(v);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
});
dropDown.result.then(function (v) {
controller.$setDirty();
controller.$setViewValue(v);
controller.$render();
});
};
element.attr('readonly', '');
element.addClass('dropdown');
element.on('click', showPopupDropDown);
}
};
}]);
This works fine for static items, and the items can be updated by poping or pushing (in function updateCities1), but when I change the content of items by direct assignment (in function updateCities2), the items in the popup drop down doesn't update.
Is there any way to watch the items in AngularJs directive so that the second way would work? Or when I want to change the items, do I have to use the first way to update them?
Is there any shortcut to clear and push multiple items into the original array?
Finally I figured it out.
There was a bug in the directive.
The popupDropDownAttrabute shoud be fetched when showing the modal, thus, the link should be:
link: function (scope, element, attrs, controller) {
var showPopupDropDown = function (evt) {
var popupDropDownAttribute = scope.$eval(attrs.popupDropDown);
var dropDown = $uibModal.open({
animation: true,
template: dropDownTemplate,
resolve: {
items: function () {
return popupDropDownAttribute.items;
},
cols: function () {
return popupDropDownAttribute.cols;
},
selected: function () {
return element.val();
}
},
controller: function ($scope, $modalInstance, items, cols, selected) {
$scope.items = items;
$scope.cols = cols;
$scope.selected = selected;
$scope.select = function (v) {
$modalInstance.close(v);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
});
dropDown.result.then(function (v) {
controller.$setDirty();
controller.$setViewValue(v);
controller.$render();
});
};
element.attr('readonly', '');
element.addClass('dropdown');
element.on('click', showPopupDropDown);
}
http://jsfiddle.net/Nidhin/xd3Ab/
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.roles = [
{roleId: 1, roleName: "Administrator"},
{roleId: 2, roleName: "Super User"}
];
$scope.user = {
userId: 1,
username: "JimBob",
roles: [$scope.roles[0]]
};});myApp.directive('multiSelect', function($q) {return {
restrict: 'E',
require: 'ngModel',
scope: {
selectedLabel: "#",
availableLabel: "#",
displayAttr: "#",
available: "=",
model: "=ngModel"
},
template: '<div class="multiSelect">' +
'<div class="leftms fL ">' +
'<label class="control-label fL" for="multiSelectSelected">{{ availableLabel }} ' +
'({{ available.length }})</label>'+'<div class="clear"></div>'+
'<select id="multiSelectAvailable" ng-model="selected.available" multiple ' +
'class="pull-left mselect " ng-options="e as e[displayAttr] for e in available"></select>' + '<div class="clear"></div>'+
'</div>' +
'<div class=" width10p fL" >' +
'<button class="btn mover left" ng-click="add()" title="Add selected" ' +
'ng-disabled="selected.available.length == 0">' +
'<i class="icon-arrow-right clrblk">></i>' +
'</button>' +
'<button class="btn mover right" ng-click="remove()" title="Remove selected" ' +
'ng-disabled="selected.current.length == 0">' +
'<i class="icon-arrow-left clrblk"><</i>' +
'</button>' +
'</div>' +
'<div class="leftms fL">' +
'<label class="control-label fL" for="multiSelectSelected">{{ selectedLabel }} ' +
'({{ model.length }})</label>' +'<div class="clear"></div>'+
'<select id="currentRoles" ng-model="selected.current" multiple ' +
'class="pull-left mselect fL" ng-options="e as e[displayAttr] for e in model">' +
'</select>' + '<div class="clear"></div>'+
'</div>' +
'<div class=" width10p fL" >' +
'<button class="btn mover left" ng-click="up()" title="Add selected" ' +
'ng-disabled="selected.current.length == 0">' +
'<i class="fa fa-angle-up clrblk"></i>' +
'</button>' +
'<button class="btn mover right" ng-click="down()" title="Remove selected" ' +
'ng-disabled="selected.current.length == 0">' +
'<i class="fa fa-angle-down clrblk"></i>' +
'</button>' +
'</div>' +
'</div>', link: function(scope, elm, attrs) {
scope.selected = {
available: [],
current: []
};
/* Handles cases where scope data hasn't been initialized yet */
var dataLoading = function(scopeAttr) {
var loading = $q.defer();
if(scope[scopeAttr]) {
loading.resolve(scope[scopeAttr]);
} else {
scope.$watch(scopeAttr, function(newValue, oldValue) {
if(newValue !== undefined)
loading.resolve(newValue);
});
}
return loading.promise;
};
/* Filters out items in original that are also in toFilter. Compares by reference. */
var filterOut = function(original, toFilter) {
var filtered = [];
angular.forEach(original, function(entity) {
var match = false;
for(var i = 0; i < toFilter.length; i++) {
if(toFilter[i][attrs.displayAttr] == entity[attrs.displayAttr]) {
match = true;
break;
}
}
if(!match) {
filtered.push(entity);
}
});
return filtered;
};
scope.refreshAvailable = function() {
scope.available = filterOut(scope.available, scope.model);
scope.selected.available = [];
scope.selected.current = [];
};
scope.add = function() {
scope.model = scope.model.concat(scope.selected.available);
scope.refreshAvailable();
};
scope.remove = function() {
scope.available = scope.available.concat(scope.selected.current);
scope.model = filterOut(scope.model, scope.selected.current);
scope.refreshAvailable();
};
scope.update = function() {
scope.model = scope.model.concat(scope.selected.current);
//scope.model = filterOut(scope.model, scope.selected.current);
scope.refreshAvailable();
};
scope.up = function() {
var $op = $('#currentRoles option:selected');
if($op.length){
$op.first().prev().before($op);
}
$('#currentRoles').find('option').attr('selected','selected');
//scope.update();
scope.refreshAvailable();
};
scope.down = function() {
var $op = $('#currentRoles option:selected');
if($op.length){
$op.last().next().after($op);
}
//scope.add();
scope.refreshAvailable();
scope.apply();
};
$q.all([dataLoading("model"), dataLoading("available")]).then(function(results) {
scope.refreshAvailable();
});
}};})// JavaScript Document
on this link you will find two select box Available Role & Current role, I have to move item from Available Role to Current role Then Move the item Up and down in Current role Select box
--- Moving Item from available role to Current role I have used angular js
--- For Moving item UP and down in Current role I have used Jquery But when I am posting the value order of item is not passing in same format which is in Current role selectbox.
Pls use the fiddle.
in my opinion, you should just modify the arrays within $scope to get the ordering right.
https://gist.github.com/jfornoff/db2bb5f0c35bc0364529
This is a gist of a bit of code that i used to modify array orders in a project that i worked on.
Basically what you would do is just grab your variable that points to the currently selected element,
and modify the corresponding array to suit what you are trying to do.
$scope.up = function(){
ArrayService.moveUp(correspondingArray, selected.current);
};
Hope that helps, cheers!
You can use Angular itself to move elements up and down too. If you reorder the elements in the array available and current the ui would automatically reorder the elements.
Use the array splice method to move element within the array. See this answer on how to move elements in an array.
Move an array element from one array position to another
I'm not able to catch the change event of a model in Angular. Everything works fine, I can view the updated model value in the template, but not available in the controller.
This is my code:
controller:
//watches the date selector
$scope.$watch('startDate', function (value1, value2) {
console.log (value1, value2);
});
template:
{{startDate}}{{endDate}}
<daterangepicker readonly start="startDate" end="endDate" format="currentUser.settings.dateFormat" options="daterangeoptions"></daterangepicker>
directive:
//daterange picker
myAppDirectives
.directive('daterangepicker', function ($parse, $timeout) {
return {
restrict: 'E',
replace: true,
scope: {
options: '=options',
start: '=start',
end: '=end',
format: '=format'
},
compile: function (element, attrs){
//debugger;
//var initialVal = attrs.startDate + attrs.endDate;
var htmlText = '<div class="input-append pull-right">';
htmlText += '<input type="text" readonly="" id="range" value="" />';
htmlText += '<input type="hidden" name="startDate" ng-model="startDate" value="" />';
htmlText += '<input type="hidden" name="endDate" ng-model="endDate" value="" />';
htmlText += '<span class="add-on"><i class="icon-calendar"></i></span></div>';
element.replaceWith(htmlText);
//return function, does all the logic
return function (scope, element, attrs) {
var daterangecallback = function (start, end) {
//let angular know change happened to $scope
scope.start = start;
scope.end = end;
scope.$apply();
}
$timeout(function(){
var range = element.find('#range');
range.daterangepicker (scope.options, daterangecallback);
range.val(moment(scope.options.startDate).format(scope.options.format) + ' - ' + moment(scope.options.endDate).format(scope.options.format));
});
}
}
}
})