Angular JS - Inplace Edit inside tree - angularjs

I could not get inplace edit working within a tree.
I forked a fiddle where inplace edit worked within a simple array of input data
Here is the fiddle
http://jsfiddle.net/cguy/wcMzw/8/
thanks for any help.
<script type="text/ng-template" id="tree_item_renderer.html">
<button ng-click="expand_collapse(data)" ng-show="data.show && data.nodes.length > 0">-</button>
<button ng-click="expand_collapse(data)" ng-show="!data.show && data.nodes.length > 0">+</button>
<edit-in-place value="data.name"></edit-in-place>
<ol ng-show="data.show">
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'"></li>
</ol>
</script>
<div id="tree">
<ol ng-controller="TreeCtrl" >
<li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'"></li>
</ol>
</div>
var app = angular.module( 'myApp', [] );
app.directive( 'editInPlace', function() {
return {
restrict: 'E',
scope: { value: '=' },
template: '<span ng-click="edit()" ng-bind="value"></span><input ng-model="value"></input>',
link: function ( $scope, element, attrs ) {
// Let's get a reference to the input element, as we'll want to reference it.
var inputElement = angular.element( element.children()[1] );
// This directive should have a set class so we can style it.
element.addClass( 'edit-in-place' );
// Initially, we're not editing.
$scope.editing = false;
// ng-click handler to activate edit-in-place
$scope.edit = function () {
$scope.editing = true;
// We control display through a class on the directive itself. See the CSS.
element.addClass( 'active' );
// And we must focus the element.
// `angular.element()` provides a chainable array, like jQuery so to access a native DOM function,
// we have to reference the first element in the array.
inputElement[0].focus();
};
// When we leave the input, we're done editing.
inputElement.prop( 'onblur', function() {
$scope.editing = false;
element.removeClass( 'active' );
});
}
};
});
app.controller("TreeCtrl",['$scope', function($scope) {
$scope.expand_collapse = function(data) {
data.show = !data.show;
}
// below is an array of size 1 - it does not have to be that way
$scope.tree = [ {
name : "Root",
show : true,
nodes : []
} ];
var nodeChild1 = {
name : "Child 1",
show : false,
nodes : []
};
var nodeChild2 = {
name : "Child 2",
show : false,
nodes : []
};
// Add the children
$scope.tree[0].nodes.push(nodeChild1);
$scope.tree[0].nodes.push(nodeChild2);
var nodeGrandChild1 = {
name : "Grand Child 1",
show : false,
nodes : []
};
var nodeGrandChild11 = {
name : "Grand Child 11",
show : false,
nodes : []
};
nodeChild1.nodes.push(nodeGrandChild1);
nodeChild1.nodes.push(nodeGrandChild11);
var nodeGrandChild2 = {
name : "Grand Child 2",
show : false,
nodes : []
};
var nodeGrandChild21 = {
name : "Grand Child 21",
show : false,
nodes : []
};
nodeChild2.nodes.push(nodeGrandChild2);
nodeChild2.nodes.push(nodeGrandChild21);
} ]);

There were some extra Controller tags in my original fiddle.
Now it works - here is the updated fiddle.
http://jsfiddle.net/cguy/wcMzw/9/
<br />
<p>Here we repeat the contacts to ensure bindings work
</p>
<script type="text/ng-template" id="tree_item_renderer2.html">
<button ng-click="expand_collapse(data)" ng-show="data.show && data.nodes.length > 0">-</button>
<button ng-click="expand_collapse(data)" ng-show="!data.show && data.nodes.length > 0">+</button>
{{data.name}}
<ol ng-show="data.show">
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer2.html'"></li>
</ol>
</script>
<div id="tree2">
<ol>
<li ng-repeat="data in tree" ng-include="'tree_item_renderer2.html'"></li>
</ol>
</div>

For the 2nd tree in your demo, you missed the directive
<edit-in-place value="data.name"></edit-in-place>

Related

angularjs $compile html template in forEach not update variables

I want to dynamically generate html. I have generateHtml function contains loop for items, currently it is not displaying proper variables added in template. It is always display the last items data on all the iteration on compiled html.
Following is the controller & template code
This is my controller code
angular.module('app').controller('PageController', ['$scope', '$sce', '$compile','$templateRequest',
function ($scope, $sce, $compile,$templateRequest) {
$scope.itemsHtml = '';
// Array contains dynamic data
vm.items = [{
id: 1,
name: 'abc',
}, {
id: 2,
name: 'pqr',
}, {
id: 3,
name: 'stu',
}, {
id: 4,
name: 'xyz',
}];
vm.currentItem = [];
let templateUrl = $sce.getTrustedResourceUrl('/views/item.html');
$templateRequest(templateUrl).then(function(template) {
vm.itemTemplate = template;
}, function() {
});
vm.generateHtml = function() {
items.forEach(function (item, key) {
vm.currentItem = item;
let compiledTemplate = $compile(vm.itemTemplate)($scope).html();
/* Append compiled dynamic html */
$scope.itemsHtml += compiledTemplate;
});
}
function init() {
vm.generateHtml();
}
init();
}
]);
This is template view
<script type="text/ng-template" id="item.html">
<div className="item-wrapper">
<div className="item-inner">
{{ pageCtrl.currentItem.name }}
</div>
<div className="action-inner">
<div className="btn-action"
role="button"
ng-click="pageCtrl.edit(
pageCtrl.currentItem.id
)">
<i className="fa fa-plus"></i>
</div>
</div>
</div>
</script>
I got the solution for this issue.
Actually when we use compile after that we have to interpolate the compiled template
compiledTemplate = $interpolate(compiledTemplate)($scope);
let compiledTemplate = $compile(vm.itemTemplate)($scope).html();
/* Here interpolated compiled template */
compiledTemplate = $interpolate(compiledTemplate)($scope);
/* Append compiled dynamic html */
$scope.itemsHtml += compiledTemplate;

AngularJS - Watch filtered list for changes

Within angular I have a filtered list of people that takes the filter criteria from a predicate function. I want to watch a variable of the filtered list (called filteredPeople) every time the filtered list changes. But I am unable to see when that variable changes.
My code is below:
HTML:
<ul>
<li ng-repeat="person in ($ctrl.filteredPeople = ($ctrl.people | filter: $ctrl.filter))">
...
</li>
</ul>
JS:
controller: ['$scope',
function ($scope) {
var $ctrl = this;
$ctrl.people = {...}
$ctrl.filteredPeople = [];
$scope.$watch($ctrl.filteredPeople, function () {
console.log("called"); //not being called
});
$ctrl.filter = function (p) {
//custom filter function for each item in the array of people
}
}]
I can answer any questions of provide more code if needed
angular.module('app', []).controller('ctrl', function($scope) {
var vm = this;
vm.items = [
{ name: 'Sam' },
{ name: 'Max' },
{ name: 'Tom' },
{ name: 'Henry' },
{ name: 'Jack' },
{ name: 'Kate' }
]
var counter = 1;
$scope.$watchCollection('vm.filtered', function(){
console.log('Changed' + counter++);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl as vm'>
<input type='text' ng-model='vm.filter' />
<ul>
<li ng-repeat='item in vm.filtered = (vm.items | filter : vm.filter)'>{{item}}</li>
</ul>
</div>

How to access child records of a firebase array dynamically using Angularfire

I am trying to implement a accordion using angularfire . I am able to retrieve the top level list ("A1","D1","R1")for display but I am unable to figure out how to retrieve the child for each accordion tab that is selected. For Eg if I select "D1", it should open up and display "C1", "H1".
Here is my data on firebase
{
"A1" : {
"B1" : 50
},
"D1" : {
"C1 " : 98,
"H1" : 12
},
"R1" : {
"RR1" : 67,
"RD1" : 54
}
}
My code
var app=angular.module("sampleApp",["firebase"]);
app.controller("MyCtrl", ["$scope", "$firebaseArray", "$firebaseObject",
function($scope, $firebaseArray,$firebaseObject) {
var ref = firebase.database().ref("Products/");
var list = $firebaseArray(ref);
$scope.list = list;
$scope.activeTabs = [];
// check if the tab is active
$scope.isOpenTab = function (tab) {
// check if this tab is already in the activeTabs array
if ($scope.activeTabs.indexOf(tab) > -1) {
// if so, return true
return true;
} else {
// if not, return false
return false;
}
}
// function to 'open' a tab
$scope.openTab = function (tab) {
// check if tab is already open
if ($scope.isOpenTab(tab)) {
//if it is, remove it from the activeTabs array
$scope.activeTabs.splice($scope.activeTabs.indexOf(tab), 1);
} else {
// if it's not, add it!
$scope.activeTabs.push(tab);
}
}
}
]);
HTML Code
<div class="container accordion__container" ng-controller="MyCtrl">
<div class="accordion__tab" ng-repeat="products in list">
<div class="accordion__tab-title" ng-click="openTab(products.$id)">{{products.$id}} </div>
<div class="accordion__tab-content" ng-show="isOpenTab(products.$id)">
<div class="accordion__tab-contentdet" ng-repeat="productDet in <sub Product details>">
</div>
</div>
</div>
</div>
I made some changes in your code.
In HTML i used nav tabs.
<ul class="nav nav-tabs">
<li ng-repeat="products in list">
<a data-toggle="tab" href="#{{products.id}}">{{products.id}}</a>
</li>
</ul>
<div class="tab-content">
<div id="{{products.id}}" class="tab-pane fade" ng-repeat="products in list">
<h3>{{products.id}}</h3>
<p>Content : {{products.data}}.</p>
</div>
</div>
Controller
app.controller("MyCtrl", ["$scope", "$firebaseObject",
function($scope, $firebaseObject) {
var ref = firebase.database().ref("Products");
var list = $firebaseObject(ref);
list.$loaded().then(function() {
$scope.list = [];
angular.forEach(list, function(value,key){
$scope.list.push({ id: key, data: value})
})
});
}
]);
Another Method
Instead of using list.$loaded() you can use the below code:
ref.once('value', function(snapshot) {
$scope.list = [];
angular.forEach(snapshot.val(), function(value,key){
$scope.list.push({ id: key, data: value})
})
})
I just created a plunker for you. Please check it
https://plnkr.co/edit/5dOr7xAWIFlmdykAC1yh
if you have any doubt please let me know.

Why does my service reinitialize?

On my web application, I have a menu on the left that should be the same on all pages. On top of that, whenever a page changes, the item that was selected should be selected again when the new page loads. For this I made a directive:
menu.html
<div id="sidebar-wrapper" ng-style="style()" fill-height="20">
<ul class="sidebar-nav">
<li class="component-menu" ng-repeat="component in menu.components">
<span>
<img src="img/arrow_down_ok.png" />
<span ng-class="{selected: menu.isActive(component.name)}" ng-click="menu.select(component.name,'all')" >{{ component.name }}</span>
</span>
<ul ng-if="component.devices">
<li class="device-menu" ng-repeat="device in component.devices">
<span ng-class="{selected: menu.isActive(component.name, device.name)}" ng-click="menu.select(component.name,device.name)">{{ device.name }}</span>
</li>
</ul>
</li>
</ul>
</div>
menu directive
var app = angular.module("myApp.directives", ["services"]);
app.directive("componentMenu", ["menuService", function() {
return {
restrict: "E",
templateUrl: "js/templates/menu.html",
controller: function($scope, $location, menuService) {
var that = this;
this.components = [ {
name : "pms",
devices : [ {name : "robot"}, {name : "controller"} ]
}, {
name : "bms",
devices : [ {name : "ScanningController"}, {name : "nozzle"} ]
}, ];
console.log("menu components:", this.components);
menuService.selectedComponent = "";
menuService.selectedDevice = "";
this.select = function(component, device) {
device = device || "all";
$location.search("component", component);
$location.search("device", device);
menuService.selectedComponent = component;
menuService.selectedDevice = device;
console.log("Menu selected:", menuService.selectedComponent + "/" + menuService.selectedDevice);
}
this.isActive = function(component, device) {
device = device || "all";
return component == menuService.selectedComponent && device == menuService.selectedDevice;
}
$scope.$watch(function($scope, that) {
return $location.url();
}, function(url) {
if (url) {
var component = menuService.selectedComponent;
var device = menuService.selectedDevice;
if (!(menuService.selectedComponent == component && menuService.selectedDevice == device)) {
that.select(component, device);
}
}
}
);
},
controllerAs: "menu",
};
}]);
menu service
var app = angular.module("myApp.services", []);
app.factory("menuService", [function() {
this._selectedComponent;
this._selectedDevice;
var menuSelection = {
selectedComponent: this._selectedComponent,
selectedDevice: this._selectedDevice
}
return menuSelection;
}]);
Whenever an item in the menu is selected, the console prints out the selected item correctly and next prints out the menu components (which seems correct since the URL gets changed). But when the page of the new URL is loaded, the variables inside the menu service are empty again.
Could anybody explain me why this is, as I understood that services were singletons and should keep their values.
When using service, it is instanciated with the new keyword which is why you bind the properties to the this and return a constructor. However you are using a factory. In that case you create an object that you return. Therefore you need your components to be instanciated with var =.

AngularJs decoupling page segments' nav links but maintaining one navigation system

I have a side menu directive that populates itself from a Sidebar Controller but the routing for all click events on the entire website comes from the page's parent Route Controller.
The Container main page
<body data-ng-controller ="RouteCtrl as vm">
<div data-header=""></div>
<div data-ng-controller="SidebarCtrl">
<div data-side-bar=""></div>
</div>
<div data-ng-view=""></div>
......
</body>
The SidebarCtrl
(function () {
'use strict';
var controllerId = 'SidebarCtrl';
angular.module('app').controller(controllerId,
['$scope', SidebarCtrl]);
function SidebarCtrl($scope) {
var vm = this;
vm.title = 'SidebarCtrl';
vm.toggleMenu = function () {
vm.minifyMe = !vm.minifyMe;
};
vm.expandNav = function (item) {
if (item !== vm.expandedItem) {
vm.expandedItem = item;
} else {
//vm.expandedItem = null;
}
};
vm.menuItems = [
{
name: "Parent", subMenu:
[
{ name: "Sub 1", module: "Mod1", url: "rootPage" },
{ name: "Sub 2", module: "Mod2", url: "mod2Sub1/somePage" },
{ name: "Sub 3", module: "Mod3", url: "folder2/mod3Sub2/somePage" },
{ name: "Sub 4", module: "Mod4", url: "folder3/subFoler/somePage" }
]
}
];
}
angular.module('app')
.directive('sideBar', function () {
return {
restrict: "A",
replace: true,
templateUrl: '/app/html/common/sidebar.html'
};
});
})();
The sidebar html
<aside id="left-panel" data-ng-controller="SidebarCtrl as vm">
....
<li data-ng-repeat="menuItem in vm.menuItems" ....>
<span class="menu-item-parent">{{menuItem.name}}</span>
<ul ....>
<li data-ng-repeat="subItem in menuItem.subMenu">
<a style="cursor: pointer;" data-ng-click="vm.changeView('{{subItem.module}}','{{subItem.url}}')">{{subItem.name}}</a>
</li>
</ul>
</li>
....
</aside>
The Route Controller
// Route navigation for entire app
vm.changeView = function (moduleView, dashboardView) {
$rootScope.event = { viewUrl: dashboardView, moduleUrl: moduleView };
console.log(dashboardView);
$location.path(dashboardView);
};
var newRoute = $routeParams.primaryNav;
if ($routeParams.secondaryNav != "" && $routeParams.secondaryNav != undefined) {
newRoute = newRoute + '/' + $routeParams.secondaryNav;
}
if ($routeParams.tertiaryNav != "" && $routeParams.tertiaryNav != undefined) {
newRoute = newRoute + '/' + $routeParams.tertiaryNav;
}
$scope.templateUrl = '/app/html/' + newRoute + '.html';
As show above, the side bar populates but the routing does not work
If I made the sidebar nav static [which I do not want], and the sidebar page's controller the RouteCtrl the side bar navigation responds properly.
How can I maintain the entire navigation of the website in the route controller, yet decouple the various segments of the website, eg. the sidebar nav, the headernav, the footernav, etc ?
To solve this problem, I did several things:
I removed vm as a pattern, I cannot see any value in using it so I reverted back to the prior pattern of $scope.
Although the parent controller's function changeView is now accessible to the child [again, once ALL 'vm' is replaced with $scope in the project, I opted for $emit] :
1. Index.html
<body data-ng-controller ="RouteCtrl">
<div data-header=""></div>
<div data-ng-controller="SidebarCtrl">
<div data-side-bar=""></div>
</div>
<div data-ng-view=""></div>
......
</body>
2. Sidebar.html
< a style="cursor: pointer;" data-ng-click="handleNav(subItem.module,subItem.url)">{{subItem.name}}</a>
3. Side Crtl:
$scope.handleNav = function (args1, args2) {
$scope.$emit('handleNewView', (args1, args2));
};
4. Parent Crtl:
$scope.$on('handleNewView', function (args1, args2) {
$scope.changeView(args1, args2);
});
$scope.changeView = function (moduleView, dashboardView) {
$rootScope.event = { viewUrl: dashboardView, moduleUrl: moduleView };
$location.path(dashboardView);
};
In conclusion, I cannot see any value in adopting "Controller as vm" as a pattern.

Resources