AngularJS Editable Table - angularjs

I want to create a editable table in AngularJs. Same date I am using for another list and details view in same page.
My query is when I click the edit link in the list details view (Selected View Section in the page) part and modify the values in the textbox it is not changing in the table view section until I click the save button, but when I click the edit in the table section (Editable Table section in the page) part and modify the value in the textbox is it changing in the Selected View section also.
But, I want the change the all the values after I click the save link. Please fine the sample code and advise me.
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Example - example-example53-production</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<h1>Editable Table</h1>
<table id="searchObjResults">
<tr><th ng-click="sort('name')">Name</th><th ng-click="sort('phone')">Phone</th></tr>
<tr><td><input ng-model="search.name"></td><td><input ng-model="search.phone"></td></tr>
<tr ng-repeat="friendObj in users | orderBy:orderProp:direction| filter:search:strict" ng-class-odd="'odd'">
<td click-to-edit="friendObj" type="tbl"></td>
</tr>
</table>
<h1>Selected View</h1>
<ul>
<li ng-repeat="user in users" ng-class="{active: checkActive(user)}" ng:click="select(user)">{{user.name}}</li>
</ul>
<p>selected: {{selectedUser.phone}}</p>
<p click-to-edit="selectedUser.name"></p>
<p click-to-edit="selectedUser.phone"></p>
<script>
var myApp = angular.module('myApp', []);
//myApp.by.id('setbtn')element('h1').addClass('active');
myApp.controller('MainCtrl', ['$scope','$filter', function ($scope,$filter) {
$scope.users = [{name:'John', phone:'555-1276'},
{name:'John', phone:'555-1278'},
{name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'},
{name:'Adam', phone:'555-5678'},
{name:'Julie', phone:'555-8765'},
{name:'Juliette', phone:'555-5678'}];
//setting for order
$scope.users = $filter('orderBy')($scope.users, 'name');
//to set the defalult search
//$scope.search = {
//phone : "555-1278"
//};
//sorting
$scope.direction = false;
$scope.orderProp = "name";
$scope.sort = function(column) {
if ($scope.orderProp === column) {
$scope.direction = !$scope.direction;
} else {
$scope.orderProp = column;
$scope.direction = false;
}
};
//selected when list click
$scope.select = function(user) {
$scope.selectedUser = user;
};
//applying the selected Class
$scope.checkActive = function(user) {
return $scope.selectedUser == user;
};
//set the first item as selected
//$scope.select($scope.users[0]);
$scope.selectedUser = $scope.users[0];
}]);
myApp.directive("clickToEdit", function() {
var editorTemplate = '<td class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value.name}} ' +
'{{value.phone}} ' +
'Edit' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue.name">' +
'<input ng-model="view.editableValue.phone">' +
'Save' +
' or ' +
'cancel.' +
'</div>' +
'</td>';
var editorTemplate1 = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value}} ' +
'Edit' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'cancel.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
//template: editorTemplate,
template: function(element, attrs) {
if(attrs.type=='tbl'){
return editorTemplate;
}
else{
return editorTemplate1;
}
},
scope: {
value: "=clickToEdit",
},
link: function(scope, element, attrs) {
//alert(element);
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
</script>
<style>
.active{color:green}
</style>
</body>
</html>

When you click edit, you could do a clone of your model and edit that. Then when you click save, update (or replace) the original model. Discard the changes if they do not click save.
There is a cloneDeep function in the underscore library that you could use.

Related

close multiselect dropdown directive on clicking outside dropdown

I have tried codes from this links
AngularJS dropdown directive hide when clicking outside,
http://plnkr.co/edit/ybYmHtFavHnN1oD8vsuw?p=preview,
closing dropdown single or multiselect when clicking outside.
But did not help.
<html>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css">
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
'use strict';
var app = angular.module('myApp', ['app.directives']);
app.controller('AppCtrl', function($scope){
$scope.roles = [
{"id": 1, "name": "Manager", "assignable": true},
{"id": 2, "name": "Developer", "assignable": true},
{"id": 3, "name": "Reporter", "assignable": true}
];
$scope.member = {roles: []};
$scope.selected_items = [];
});
var app_directives = angular.module('app.directives', []);
app_directives.directive('dropdownMultiselect', function($document){
return {
restrict: 'AE',
scope:{
model: '=',
options: '=',
pre_selected: '=preSelected'
},
template: "<div class='btn-group' data-ng-class='{open: open}'>"+
"<button class='btn btn-small'>Select</button>"+
"<button class='btn btn-small dropdown-toggle' data-ng-click='open=!open;openDropdown()'><span class='caret'></span></button>"+
"<ul class='dropdown-menu' aria-labelledby='dropdownMenu'>" +
"<li><a data-ng-click='selectAll()'><i class='icon-ok-sign'></i> Check All</a></li>" +
"<li><a data-ng-click='deselectAll();'><i class='icon-remove-sign'></i> Uncheck All</a></li>" +
"<li class='divider'></li>" +
"<li data-ng-repeat='option in options'> <a data-ng-click='setSelectedItem()'>{{option.name}}<span data-ng-class='isChecked(option.id)'></span></a></li>" +
"</ul>" +
"</div>" ,
link: function postLink(scope, element, attrs)
{
console.log("in on click");
var onClick = function (event) {
var isChild = element[0].contains(event.target);
var isSelf = element[0] == event.target;
var isInside = isChild || isSelf;
if (!isInside) {
scope.$apply(attrs.dropdownMultiselect)
}
}
scope.$watch(attrs.isActive, function(newValue, oldValue) {
if (newValue !== oldValue && newValue == true) {
$document.bind('click', onClick);
}
else if (newValue !== oldValue && newValue == false) {
$document.unbind('click', onClick);
}
});
},
controller: function($scope){
$scope.openDropdown = function(){
$scope.selected_items = [];
for(var i=0; i<$scope.pre_selected.length; i++){ $scope.selected_items.push($scope.pre_selected[i].id);
}
};
$scope.selectAll = function () {
$scope.model = _.pluck($scope.options, 'id');
console.log($scope.model);
};
$scope.deselectAll = function() {
$scope.model=[];
console.log($scope.model);
};
$scope.setSelectedItem = function(){
var id = this.option.id;
if (_.contains($scope.model, id)) {
$scope.model = _.without($scope.model, id);
} else {
$scope.model.push(id);
}
console.log($scope.model);
return false;
};
$scope.isChecked = function (id) {
if (_.contains($scope.model, id)) {
return 'icon-ok pull-right';
}
return false;
};
}
}
});
</script>
<body>
<div ng-app="myApp" ng-controller="AppCtrl">
<dropdown-multiselect pre-selected="member.roles" model="selected_items" options="roles" is-active="isDropdownOpen()"></dropdown-multiselect>
<pre>selected roles = {{selected_items | json}}</pre>
</div>
</body>
</html>
Please suggest changes for this.
I solved it finally using below code.
<html>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css">
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
'use strict';
var app = angular.module('myApp', ['app.directives']);
app.controller('AppCtrl', function($scope){
$scope.roles = [
{"id": 1, "name": "Manager", "assignable": true},
{"id": 2, "name": "Developer", "assignable": true},
{"id": 3, "name": "Reporter", "assignable": true}
];
$scope.member = {roles: []};
$scope.selected_items = [];
});
var app_directives = angular.module('app.directives', []);
app_directives.directive('dropdownMultiselect', function($document){
return {
restrict: 'AE',
scope:{
model: '=',
options: '=',
pre_selected: '=preSelected'
},
template: "<div class='btn-group' data-ng-class='{open: open}'>"+
"<button class='btn btn-small'>Select</button>"+
"<button class='btn btn-small dropdown-toggle' data-ng-click='open=!open;openDropdown()' ><span class='caret'></span></button>"+
"<ul class='dropdown-menu' aria-labelledby='dropdownMenu' ng-show='open'>" +
"<li><a data-ng-click='selectAll()'><i class='icon-ok-sign'></i> Check All</a></li>" +
"<li><a data-ng-click='deselectAll();'><i class='icon-remove-sign'></i> Uncheck All</a></li>" +
"<li class='divider'></li>" +
"<li data-ng-repeat='option in options'> <a data-ng-click='setSelectedItem()'>{{option.name}}<span data-ng-class='isChecked(option.id)'></span></a></li>" +
"</ul>" +
"</div>" ,
link: function(scope, elem, attr, ctrl)
{
//console.log("in click");
elem.bind('click', function(e) {
console.log("in click");
// this part keeps it from firing the click on the document.
e.stopPropagation();
});
$document.bind('click', function() {
// magic here.
console.log("click in document");
scope.$apply(attr.dropdownMulti);
/*var myElement= document.getElementsByClassName('btn btn-small dropdown-toggle');
angular.element(myElement).triggerHandler('click');*/
})
},
controller: function($scope){
$scope.openDropdown = function(){
$scope.selected_items = [];
for(var i=0; i<$scope.pre_selected.length; i++){
$scope.selected_items.push($scope.pre_selected[i].id);
}
};
$scope.selectAll = function () {
$scope.model = _.pluck($scope.options, 'id');
console.log($scope.model);
};
$scope.deselectAll = function() {
$scope.model=[];
console.log($scope.model);
};
$scope.setSelectedItem = function(){
var id = this.option.id;
if (_.contains($scope.model, id)) {
$scope.model = _.without($scope.model, id);
} else {
$scope.model.push(id);
}
console.log($scope.model);
return false;
};
$scope.isChecked = function (id) {
if (_.contains($scope.model, id)) {
return 'icon-ok pull-right';
}
return false;
};
}
}
});
</script>
<body>
<div ng-app="myApp" ng-controller="AppCtrl">
<dropdown-multiselect pre-selected="member.roles" model="selected_items" options="roles" is-active="isDropdownOpen()" dropdown-multi="open=false"></dropdown-multiselect>
<pre>selected roles = {{selected_items | json}}</pre>
</div>
</body>
</html>

AngularJS template in directive not display text

I try to create directive in angularjs:
JavaScript and HTML code:
'use strict';
var app = angular.module('myApp', ['app.directives']);
app.controller('AppCtrl', function($scope){
$scope.roles = [
{"id": 1, "name": "Michael"},
{"id": 2, "name": "Max"},
{"id": 3, "name": "John"}
];
$scope.dummyData = "Dummy!!!";
$scope.member = {roles: []};
$scope.selected_items = [];
});
var app_directives = angular.module('app.directives', []);
app_directives.directive('dropdownMultiselect', function(){
return {
restrict: 'E',
scope:{
testElem: '=',
model: '=',
options: '=',
pre_selected: '=preSelected'
},
template: "<div class='btn-group' data-ng-class='{open: open}'>"+
"<button class='btn btn-small'>{{testElem}}</button>"+
"<button class='btn btn-small dropdown-toggle' data-ng-click='open=!open;openDropdown()'><span class='caret'></span></button>"+
"<ul class='dropdown-menu' aria-labelledby='dropdownMenu'>" +
"<li><a data-ng-click='selectAll()'><i class='icon-ok-sign'></i> Check All</a></li>" +
"<li><a data-ng-click='deselectAll();'><i class='icon-remove-sign'></i> Uncheck All</a></li>" +
"<li class='divider'></li>" +
"<li data-ng-repeat='option in options'> <a data-ng-click='setSelectedItem()'>{{option.name}}<span data-ng-class='isChecked(option.id)'></span></a></li>" +
"</ul>" +
"</div>" ,
controller: function($scope){
$scope.openDropdown = function(){
$scope.selected_items = [];
for(var i=0; i<$scope.pre_selected.length; i++){ $scope.selected_items.push($scope.pre_selected[i].id);
}
};
$scope.selectAll = function () {
$scope.model = _.pluck($scope.options, 'id');
console.log($scope.model);
};
$scope.deselectAll = function() {
$scope.model=[];
console.log($scope.model);
};
$scope.setSelectedItem = function(){
var id = this.option.id;
if (_.contains($scope.model, id)) {
$scope.model = _.without($scope.model, id);
} else {
$scope.model.push(id);
}
console.log($scope.model);
return false;
};
$scope.isChecked = function (id) {
if (_.contains($scope.model, id)) {
return 'icon-ok pull-right';
}
return false;
};
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<div ng-app="myApp" ng-controller="AppCtrl">
<dropdown-multiselect pre-selected="member.roles" model="selected_items" testElem = "dummyData" options="roles"></dropdown-multiselect>
In template in the directive definition I have this row:
<button class='btn btn-small'>{{$scope.testElem}}</button>
As you can see with help of this row I try to display text inside $scope.testElem variable.
But the text does not appears.What I am missing? Why I don't see text on the declared button?

Using JSPlumb in an angular.js directive

I'm trying to use jsPlumb along with AngularJS and have run into some problems. I started with the jsplumb-ng example from github and attempted to modify the jsPlumb parameters to mimic the statemachine demo from the jsPlumb site. The jsplumb-ng approach creates a jsPlumbCanvas directive and it contains jsPlumbNodes. As a starting point I tried to place some static nodes from the statemachine demo and then add in two nodes controlled by the angular controller. The demo nodes are placed on the canvas correctly and connections are created. The angular nodes are created and jsPlumb has trouble in the makeTarget method and prints messages to the console.
TypeError: Cannot set property '_jsPlumbTarget' of undefined {stack: (...), message: "Cannot set property '_jsPlumbTarget' of undefined"}
I would greatly appreciate any advice to get moving forward again!
Apologize up front because I'm dancing around the fact that I do not have enough reputation to post images of the problem.
i.imgur.com/Ao4qzm9.png
i.imgur.com/OMS0FGE.png
Plunk
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="https://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css" rel="stylesheet">
<link data-require="bootstrap#3.3.1" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="https://jsplumbtoolkit.com/css/jsplumb.css" />
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script data-require="jquery#*" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>
<script data-require="bootstrap#3.3.1" data-semver="3.3.1" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script data-require="jsplumb#*" data-semver="1.7.3" src="https://jsplumbtoolkit.com/js/jquery.jsPlumb-1.7.3-min.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.0.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="DiagramEditorCtrl">
<div id="diagramView" style="overflow: hidden;" ng-cloak>
<js-plumb-canvas
on-connection="onConnection1"
x="posx"
y="posy"
zoom="zoomlevel"
ng-drop="true"
ng-drop-success="onDropComplete1($data,$event)"
>
<div ng-repeat="state in stateObjects track by state.uuid">
<js-plumb-node state-object="state" style="positon: relative;"></js-plumb-node>
</div>
<js-plumb-connection ng-click="setActiveConnection($index)" ng-repeat="connection in stateConnections" ng-model="connection">
</js-plumb-connection>
</js-plumb-canvas>
</div>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.controller('DiagramEditorCtrl', ['$scope',
function($scope) {
$scope.zoomlevel = 85;
$scope.posx = 14;
$scope.posy = 14;
$scope.stateObjects = [{
'name': 'jsPlumb',
'uuid': '123',
'style': '',
'x': 300,
'y': 150
}, {
'name': 'Brewer',
'uuid': '124',
'style': '',
'x': 45,
'y': 175
}
];
// Defaults the connections between objects. Will come from a service.
$scope.stateConnections = [
// {
// targetUUID: 'ds1',
// sourceUUID: 'sn1'
// }
];
var instance = jsPlumb.instance;
$scope.onConnection = function(instance, connection, targetUUID,
sourceUUID) {
$scope.stateConnections.push({
'targetUUID': targetUUID,
'sourceUUID': sourceUUID,
'conn': connection
});
$scope.$apply();
};
}
])
.directive('jsPlumbCanvas', function() {
var jsPlumbZoomCanvas = function(instance, zoom, el, transformOrigin) {
transformOrigin = transformOrigin || [0, 0];
var p = ['webkit', 'moz', 'ms', 'o'],
s = 'scale(' + zoom + ')',
oString = (transformOrigin[0] * 100) + '% ' + (transformOrigin[1] *
100) +
'%';
for (var i = 0; i < p.length; i++) {
el.style[p[i] + 'Transform'] = s;
el.style[p[i] + 'TransformOrigin'] = oString;
}
el.style.transform = s;
el.style.transformOrigin = oString;
instance.setZoom(zoom);
};
var def = {
restrict: 'E',
scope: {
onConnection: '=onConnection',
zoom: '=',
x: '=',
y: '='
},
controller: function($scope) {
this.scope = $scope;
},
transclude: true,
templateUrl: 'workspace.html',
link: function(scope, element, attr) {
angular.element(document).ready(function () {
var instance = jsPlumb.getInstance({
Endpoint : ['Dot', {radius:2}],
HoverPaintStyle : {strokeStyle:'#1e8151', lineWidth:2 },
ConnectionOverlays : [
[ 'Arrow', {
location:1,
id:'arrow',
length:14,
foldback:0.8
} ],
[ 'Label', { label:'Connecting...', id:'label', cssClass:'aLabel' }]
],
Container:'workspace-container'
});
scope.jsPlumbInstance = instance;
/*
* Standard JSPlumb Statemachine example that works fine.
*/
var windows = jsPlumb.getSelector('.workspace-container .node');
instance.draggable(windows);
instance.bind('click', function(c) {
instance.detach(c);
});
instance.bind('connection', function(info) {
info.connection.getOverlay('label').setLabel(info.connection.id);
});
instance.doWhileSuspended(function() {
var isFilterSupported = instance.isDragFilterSupported();
if (isFilterSupported) {
instance.makeSource(windows, {
filter:'.ep',
anchor:'Continuous',
connector:[ 'StateMachine', { curviness:20 } ],
connectorStyle:{ strokeStyle:'#5c96bc', lineWidth:2, outlineColor:'transparent', outlineWidth:4 },
maxConnections:5,
onMaxConnections:function(info, e) {
alert('Maximum connections (' + info.maxConnections + ') reached');
}
});
}
else {
var eps = jsPlumb.getSelector('.ep');
for (var i = 0; i < eps.length; i++) {
var e = eps[i], p = e.parentNode;
instance.makeSource(e, {
parent:p,
anchor:'Continuous',
connector:[ 'StateMachine', { curviness:20 } ],
connectorStyle:{ strokeStyle:'#5c96bc',lineWidth:2, outlineColor:'transparent', outlineWidth:4 },
maxConnections:5,
onMaxConnections:function(info, e) {
alert('Maximum connections (' + info.maxConnections + ') reached');
}
});
}
}
});
instance.makeTarget(windows, {
dropOptions:{ hoverClass:'dragHover' },
anchor:'Continuous',
allowLoopback:true
});
instance.connect({ source:'opened', target:'phone1' });
instance.connect({ source:'phone1', target:'phone1' });
jsPlumb.fire('jsPlumbDemoLoaded', instance);
/*
* End standard JSPlumb statemachine example
*/
});
}
};
return def;
})
.directive('jsPlumbNode', function() {
var def = {
restrict: 'E',
require: '^jsPlumbCanvas',
scope: {
node: '=stateObject'
},
// At one point, I tried to have a real uuid assigned as the div#id, but
// JSPlumb seemed to see {{node.uuid}}
// id="{{node.uuid}}"
template: '<div class="node" ' +
' ng-style="{ \'left\':node.x, \'top\':node.y }" >' +
' <div id="ds1-nh" class="node-header" ></div>' +
' <div id="ds1-nb" class="node-body">' +
'{{node.name}}' +
' </div>' +
' <div id="ds1-nt" class="node-tools">' +
' <div id="ds1-nt-o" class="node-tool-options">' +
' <span ng-click="configure($index)" class="glyphicon glyphicon-cog"></span>' +
' </div>' +
' <div id="ds1-nt-d" class="node-tool-delete">' +
' <span ng-click="trash($index)" class="glyphicon glyphicon-trash"></span>' +
' </div>' +
' </div>' +
'</div>',
link: function($scope, element, attrs, jsPlumbCanvas) {
// removed dependence on uuid4 for this plunker.
$scope.node.uuid = '1234-123-1233';
angular.element(document).ready(function () {
var instance = jsPlumbCanvas.scope.jsPlumbInstance;
function displayMaxConnectionError(info, e) {
alert('Maximum connections (' + info.maxConnections +
') reached');
}
var nodeEl = element.find('.node' );
instance.draggable( nodeEl, {
grid: [20, 20],
drag: function(event, ui) {
var posX, posY;
if( typeof event.pos !== 'undefined') {
posX = event.pos[0];
posY = event.pos[1];
}
if (typeof ui !== 'undefined') {
posX = ui.position.left;
posY = ui.position.top;
}
$scope.node.x = posX;
$scope.node.y = posY;
$scope.$apply();
}
});
// suspend drawing and initialise.
instance.doWhileSuspended(function() {
var eps = element.find('.node-header');
//console.log(eps);
for (var i = 0; i < eps.length; i++) {
var e = eps[i],
p = e.parentNode;
console.log( 'e: %o', e );
console.log( 'e.parent: %o', p );
instance.makeSource(e, {
parent: p,
anchor: 'Continuous',
connector: ['StateMachine', {
curviness: 20
}],
connectorStyle: {
strokeStyle: '#5c96bc',
lineWidth: 2,
outlineColor: 'transparent',
outlineWidth: 4
},
maxConnections: 5,
onMaxConnections: displayMaxConnectionError
});
}
instance.makeTarget(nodeEl, {
dropOptions: {
hoverClass: 'dragHover'
},
anchor: 'Continuous',
allowLoopback: true
});
});
});
}
};
return def;
})
.directive('jsPlumbConnection', function($timeout) {
var def = {
restrict: 'E',
require: '^jsPlumbCanvas',
scope: {
ngClick: '&ngClick',
ngModel: '=ngModel'
},
link: function($scope, element, attrs, jsPlumbCanvas) {
var instance = jsPlumbCanvas.scope.jsPlumbInstance;
$timeout(function() {
if (typeof $scope.ngModel.conn === 'undefined') {
$scope.ngModel.conn = instance.connect({
uuids: [
$scope.ngModel.targetUUID,
$scope.ngModel.sourceUUID
],
overlays: [
['Label', {
label: '',
id: 'label'
}]
],
editable: true
});
}
var connection = $scope.ngModel.conn;
connection.bind('mouseenter', function(conn, originalEvent) {
$scope.ngModel.mouseover = true;
$scope.$apply();
});
connection.bind('mouseleave', function(conn, originalEvent) {
$scope.ngModel.mouseover = false;
$scope.$apply();
});
}, 1300);
$scope.$on('$destroy', function() {
instance.detach($scope.ngModel.conn);
});
}
};
return def;
})
;
I have also looked at the mrquincle/jsplumb-example from github. It works fine until I tried to load a more recent copy of jsPlumb, jquery, jquery-ui and angular. The issue with the updated libraries is that the connections are not attached between the two nodes.
i.imgur.com/VLtjWFy.png
Plunk
I am not tied to either the jsplumb-ng or the jsplumb-example approach. I'm just trying to get one to work.
Thanks ahead of time!

How to use $compile inside an Angular directive?

I have a collection of 500 items (persons) that I am rendering in an Angular ng-repeat directive. Each item has three fields (FirstName, LastName, Company). I want the user to be able to see details / edit the items below each rendered row. I have a button (Font Awesome square-plus) that when clicked needs to show the item details / edit. I do not want to include this markup/logic within the controller because having it rendered but hidden is very slow...multiple seconds in Chrome. I assume this is due to all the watches.
To resolve the issue, I created a directive that injects the details / edit items under the current record at run-time. I attempt to $compile the markup to link it to the current scope of the ng-repeat row.
Problems.. I think I am having issues with the scope. The directive is references within the ng-repeat block (p in Persons), so I would think I would be passed the record scope in the directive link function. But I can only get the record object by getting the parent scope (scope.$parent.p instead of scope.p). I don't understand.
Also, once the $compile function is executed, I do see the person info displayed in the new details blocks. But changes are not reflected in the current record data, nor an I dismiss the details block.
Any suggestions?
Markup:
<div class="row" ng-repeat="p in Persons">
<div class="col-lg-1">
<i class="fa fa-plus-square" ng-show="!p.showDetail" manage-details></i>
</div>
<div class="col-lg-2">{{::p.FirstName}}</div>
<div class="col-lg-2">{{::p.LastName}}</div>
<div class="col-lg-2">{{::p.Company}}</div>
<div id="marker{{$index}}"></div>
<hr ng-if="!$last" />
</div>
JS:
(function () {
'use strict';
angular
.module('ngRepeatMystery', [])
.controller('TestController', TestController)
.directive('manageDetails', manageDetails);
TestController.$inject = ['$scope'];
function TestController($scope) {
$scope.Persons = [
{ 'FirstName': 'Joe', 'LastName': 'Delbridge', 'Company': 'Dow', 'showDetail': false },
{ 'FirstName': 'Tony', 'LastName': 'Ingram', 'Company': 'Samtec', 'showDetail': false },
{ 'FirstName': 'Mike', 'LastName': 'Smolinski', 'Company': 'HCHB', 'showDetail': false },
{ 'FirstName': 'Lee', 'LastName': 'Shipman', 'Company': 'Cummins', 'showDetail': false },
{ 'FirstName': 'Eric', 'LastName': 'ONeal', 'Company': 'MSD', 'showDetail': false },
];
$scope.DismissDetails = function (index) {
$scope.Persons[index].showDetail = false;
var wrappedMonkey = angular.element($document[0].getElementById('details' + index));
angular.element(wrappedMonkey).hide();
}
};
manageDetails.$inject = ['$compile', '$document', '$timeout'];
function manageDetails($compile, $document, $timeout) {
return {
restrict: 'A',
scope: {},
link: function (scope, element, attrs) {
element.bind('click', function () {
// scope.$parent !!? WAT!
scope.$parent.p.showDetail = !scope.$parent.p.showDetail;
if (scope.$parent.p.showDetail) {
var index = scope.$parent.$index;
var wrappedMarker = angular.element($document[0].getElementById('marker' + index));
var details = getDetailsTemplate(index);
wrappedMarker.replaceWith(details);
var wrappedDetails = angular.element($document[0].getElementById('details' + index));
$compile(wrappedDetails.contents())(scope.$parent);
};
});
}
};
function getDetailsTemplate(index) {
var detailsTemplate =
"<div id=\"details" + index + "\" style=\"padding: 20px;\">" +
"<div class=\"row\">" +
"<div class=\"col-lg-2\"></div>" +
"<div class=\"col-lg-8\">" +
"<label>Last Name</label>" +
"<input ng-model=\"p.LastName\" placeholder=\"Last Name\"><br/>" +
"<label>First Name</label>" +
"<input ng-model=\"p.FirstName\" placeholder=\"First Name\"><br/>" +
"<label>Company</label>" +
"<input ng-model=\"p.Company\" placeholder=\"Company\"><br/>" +
"<button class=\"btn btn-primary\" ng-click=\"p.DismissDetails($index);\">Done</button><br/>" +
"</div>" +
"<div class=\"col-lg-2\"></div>" +
"</div>" +
"</div>";
return detailsTemplate;
}
};
})()
Plunker: http://plnkr.co/edit/64TcuhaNi2JcC1hzon15
I am also open to other alternatives...
Ok, I think there are a lot of issues with your code.
I would advise against having the directive modify something outside of itself.
As I commented earlier, don't use $parent. Just pass data as attributes. And create a new scope when you call $compile to avoid polluting the existing scope.
I modified your code so it works, but it's still not pretty:
http://plnkr.co/edit/eLNxewwFzobqTkQ4867n
HTML:
<div class="row" ng-repeat="p in Persons">
<div class="col-lg-1">
<i class="fa fa-plus-square" ng-show="!showDetail" manage-details monkey="p" index="$index" show-detail="showDetail"></i>
</div>
<div class="col-lg-2">{{p.FirstName}}</div>
<div class="col-lg-2">{{p.LastName}}</div>
<div class="col-lg-2">{{p.Company}}</div>
<div id="marker{{$index}}"></div>
<hr ng-if="!$last" />
</div>
JS:
return {
restrict: 'A',
scope: {
monkey: '=',
index: '=',
showDetail: '='
},
link: function (scope, element, attrs) {
var childScope;
element.bind('click', function () {
scope.showDetail = !scope.showDetail;
if (scope.showDetail) {
childScope && childScope.$destroy();
childScope = scope.$new();
var index = scope.index;
var wrappedMarker = angular.element($document[0].getElementById('marker' + index));
wrappedMarker.html(getDetailsTemplate(index));
childScope.p = angular.copy(scope.monkey);
childScope.dismissDetails = function () {
scope.showDetail = false;
scope.monkey = angular.copy(childScope.p);
wrappedMarker.html('');
};
$compile(wrappedMarker.contents())(childScope);
};
});
}
};
function getDetailsTemplate(index) {
var detailsTemplate =
"<div id=\"details" + index + "\" style=\"padding: 20px;\">" +
"<div class=\"row\">" +
"<div class=\"col-lg-2\"></div>" +
"<div class=\"col-lg-8\">" +
"<label>Last Name</label>" +
"<input ng-model=\"p.LastName\" placeholder=\"Last Name\"><br/>" +
"<label>First Name</label>" +
"<input ng-model=\"p.FirstName\" placeholder=\"First Name\"><br/>" +
"<label>Company</label>" +
"<input ng-model=\"p.Company\" placeholder=\"Company\"><br/>" +
"<button class=\"btn btn-primary\" ng-click=\"dismissDetails();\">Done</button><br/>" +
"</div>" +
"<div class=\"col-lg-2\"></div>" +
"</div>" +
"</div>";
return detailsTemplate;
}

ng-repeat filter not updating on scope change

Just learning angular, so please excuse the noob nature of this question . . .
I have a list of posts that I want to filter, either alphabetically or based on age (oldest first or youngest first). My code was working before I moved my html into a directive template. Now it's not. Pretty clear that I'm missing something w/r/t the digest cycle.
Check out this plunker to see the problem in action. As you can see, both $scope.predicate and $scope.reverse are being updated, but the filter isn't re-triggering.
HTML:
<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.16/angular.js" data-semver="1.2.16"></script>
<script src="app.js"></script>
</head>
<body>
<div posts></div>
</body>
JS:
var app = angular.module('myApp', []);
app.controller('postsCtrl', ["$scope", function($scope) {
function getPosts() {
return [{
"title": "bbbBloter",
"createdOn": new Date()
}, {
"title": "cccBloter",
"createdOn": new Date(1360000000000)
}, {
"title": "aaaBloter",
"createdOn": new Date(1290000000000)
}, {
"title": "dddBloter",
"createdOn": new Date(1390000000000)
}, {
"title": "abcBloter",
"createdOn": new Date(1280000000000)
}, ];
}
//load each blotter, then get each blotters' posts, and then set-up the blotter init values
function init() {
$scope.posts = getPosts();
$scope.predicate = 'story.last_posting';
$scope.reverse = true;
$scope.orderProp = "last_posting";
}
//user has requested a different sorting
$scope.changeSort = function(sortingOn) {
switch (sortingOn) {
case "title":
$scope.predicate = 'story.title';
$scope.reverse = false;
break;
case "last_posting":
$scope.predicate = 'story.last_posting';
$scope.reverse = true;
break;
case "first_posting":
$scope.predicate = 'story.last_posting';
$scope.reverse = false;
break;
}
};
init();
}]);
app.directive('posts', function($filter) {
return {
replace: true,
template:
'<div ng-controller="postsCtrl">' +
'<div>' +
'Search: <input ng-model="query">' +
'Sort by:' +
'<select ng-model="orderProp" ng-change="changeSort(orderProp)" >' +
'<option value="title">Alphabetical</option>' +
'<option ng-selected=true value="last_posting">Newest</option>' +
'<option value="first_posting">Oldest</option>' +
'</select>' +
'<br>predicate:{{predicate}}; reverse:{{reverse}}<br>' +
'<ul>' +
'<li debug ng-repeat="post in posts | filter:query | orderBy:predicate:reverse">' +
'{{post.title}}' +
'<p>{{post.createdOn}}</p>' +
'</li>' +
'</ul>' +
'</div>' +
'</div>',
link: function(scope){
scope.$watch("reverse", function(){
console.log(scope);
});
}
};
}
);
This isn't about digest cycles. What you assign to predicate has nothing to do with your data.
Here's a working plunker.
I changed this part:
$scope.changeSort = function(sortingOn) {
switch (sortingOn) {
case "title":
$scope.predicate = 'title';
$scope.reverse = false;
break;
case "last_posting":
$scope.predicate = 'createdOn';
$scope.reverse = true;
break;
case "first_posting":
$scope.predicate = 'createdOn';
$scope.reverse = false;
break;
}
};
Your "predicates" are story.title and story.last_posting. The structure of the items is
{
"title": "bbbBloter",
"createdOn": new Date()
}
Given that there is no "story" property in the items, it's thus impossible to sort by story.title.
Change your predicates to title and createdOn, and everything will go fine.
You need to remove the story. from the predicate expressions. I think that this was something you left behind when you move to the directive.
I've forked your plunker here

Resources