How to implement ng-show as toggle in multi-level ng-repeat - html

1. level1
-lvl1A
--lvl1A1
--lvl1A2
---lvl1A2X
---lvl1A2Y
-lvl2A
--lvl2A1
--lvl2A2
2. level2
this is just the sample of multilevel ng-repeat ul li element.
on click of any level only next level data will show or hide not the entire data.
kindly give me some solution to this scenario.
Thanks in Advance.

I have done this in some of my spare time.. it might not be the best way to do it.
angular.module("myApp", []).
controller("TreeController", ['$scope',
function($scope) {
$scope.delete = function(data) {
data.nodes = [];
return false;
};
$scope.add = function(data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
var showSub = false;
data.nodes.push({
name: newName,
showSub: showSub,
nodes: []
});
return false;
};
$scope.tree = [{
name: "Node",
showSub: false,
nodes: []
}];
}
]);
<script type="text/ng-template" id="tree_item_renderer.html">
{{data.name}}
<button ng-click="add(data); $event.stopPropagation();">Add node</button>
<button ng-click="delete(data); $event.stopPropagation();" ng-show="data.nodes.length > 0">Delete nodes</button>
<ul ng-show="data.showSub">
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-click="data.showSub = !data.showSub; $event.stopPropagation();"></li>
</ul>
</script>
<ul ng-app="Application" ng-controller="TreeController">
<li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'" ng-click="data.showSub = !data.showSub"></li>
</ul>
jsFiddle

Related

Knockout observable field in array to indicate order of items

I have a html view that's connected to Knockout viewmodel, and displays a list of items.
Each item in the list contains a textual name field, and a numeric order field.
A user can perform a "drag and drop" action to items in the UL list.
The "drag and drop" event changes the order of the items as follows:
<div id="wrapper">
<ul data-bind="foreach:Items">
<li draggable="true"
ondragover="event.preventDefault();"
data-bind="event:{dragstart:$root.dragItem,drop:$root.dropItem}">
<label data-bind="text:name"></label>
<label data-bind="text:orderNo"></label>
<input type="text" data-bind="value:name" />
</li>
</ul>
<script type="text/javascript">
var list = [{ name: 'Red', orderNo: 0 }
, { name: 'Green', orderNo: 1 }
, { name: 'Blue', orderNo: 2 }];
function viewmodel() {
var self = this;
self.Items = ko.mapping.fromJS(list);
self.ItemToDrag = ko.observable();
self.dragItem = function (item, event) {
self.ItemToDrag(item);
return true;
}
self.dropItem = function (item, event) {
event.preventDefault();
var up = self.ItemToDrag().orderNo() > item.orderNo();
self.ItemToDrag().orderNo(up ? item.orderNo() - 0.5 : item.orderNo() + 0.5);
//order this list
self.Items.sort(function (left, right) {
return left.orderNo() == right.orderNo() ? 0 : (left.orderNo() < right.orderNo() ? -1 : 1);
});
//set integer number
for (var i = 0; i < self.Items().length; i++) {
self.Items()[i].orderNo(i);
}
}
}
var vm;
$(document).ready(function () {
vm = new viewmodel();
ko.applyBindings(vm, $("#wrapper")[0]);
});
My question is, if it is possible with Knockout to change the contents of the order field automatically when the items of the list change their order through the UI.
Something like
<ul data-bind="foreach:Items,orderKey:orderNo"></ul>
Where orderKey indicates the order of the items, and which field to update in case of order change.
I'm not sure this is exactly what you need. This is custom binding, that sorts an array from foreach binding before:
ko.bindingHandlers.foreach["after"] = ["orderKey"];
ko.bindingHandlers.orderKey = {
update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
var key = ko.unwrap(valueAccessor());
var allBindings = allBindingsAccessor();
if("foreach" in allBindings) {
var array = ko.unwrap(allBindings.foreach);
array.sort(function(a, b) { return a[key] > b[key]; });
allBindings.foreach = array;
}
}
};
// The model
var model = { Items: ko.observableArray([{text: 3}, {text: 1}, {text: 2}]) };
// Apply
ko.applyBindings(model);
// This simulate changes in observableArray
setTimeout(function() { model.Items.push({text: 0}) }, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<ul data-bind="foreach: Items, orderKey: 'text'">
<li data-bind="text: text"></li>
</ul>
No, there is no specific binding for that use case. In knockout, however, it is simple to write a custom binding. See the documentation. In the company I'm working for, we're using a knockout-based framework (developed by us) with tons of custom bindings, some of them really complex.
I just started to create such a binding for your use case. But I realized, it won't fit the purpose unless you have dozens of such lists.
What you can do, however, is to sort put the actual sorting into a knockout computed and just do the updating of the sort index in your drop function. See example below and don't hesitate to ask if something is not clear.
var list = [{ name: 'Red', orderNo: 0 }
, { name: 'Green', orderNo: 1 }
, { name: 'Blue', orderNo: 2 }];
function viewmodel() {
var self = this;
self._items = ko.mapping.fromJS(list);
self.Items = ko.pureComputed(function () {
return self._items().sort(function (a, b) {
return a.orderNo() < b.orderNo() ? -1 : 1;
});
});
self.ItemToDrag = ko.observable();
self.dragItem = function (item, event) {
self.ItemToDrag(item);
return true;
}
self.dropItem = function (item, event) {
event.preventDefault();
var up = self.ItemToDrag().orderNo() > item.orderNo();
self.ItemToDrag().orderNo(up ? item.orderNo() - 0.5 : item.orderNo() + 0.5);
}
}
var vm;
$(document).ready(function () {
vm = new viewmodel();
ko.applyBindings(vm, $("#wrapper")[0]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div id="wrapper">
<ul data-bind="foreach:Items">
<li draggable="true"
ondragover="event.preventDefault();"
data-bind="event:{dragstart:$root.dragItem,drop:$root.dropItem}">
<label data-bind="text:name"></label>
<label data-bind="text:orderNo"></label>
<input type="text" data-bind="value:name" />
</li>
</ul>

Addition of two values in Angular js

I want to add two values using Angular js . I have fetched values from database using $http.get($scope.url) and using the values on html page. now I want to add " 1" in the value. my code is :-
app.js
mainApp.controller("displayController", ['$scope', '$http', 'shareDataService', function($scope, $http, shareDataService) {
$scope.url = 'incomeBenefit.php';
$http.get($scope.url)
.then(function (response) {$scope.names = response.data.records;
for(X in $scope.names){
var t_age = $scope.names[X]['client_age'];
var t_premium = $scope.names[X]['premium'];
}
$scope.t_age = t_age;
$scope.t_premium = t_premium;
});
}]);
and my html page :-
<ul id="form-bt-main" class="sec-row" ng-repeat="n in [] | range:31">
<li class="form-bt">{{n}}</li>
<li class="form-bt">{{t_age = t_age +1}}</li>
</ul>
I want to add '1' in t_age. t_age = '24' and want values like this 24 25 26 27 28 29 30 in li on output screen.
angular.module('app',[]).controller('ctrl',function($scope){
$scope.age = parseInt('24');
$scope.names = ["singh","jorawar","kiran"];
});
<body ng-controller="ctrl">
<div ng-repeat="name in names">
{{name}}<br>
{{age + $index - 1 + 1}}
</div>
</body>
<li class="form-bt">{{t_age = t_age +1}}</li> is not valid.
just write <li class="form-bt">{{t_age +1}}</li>
or if you want that value in controller as ng-model, try this
<li class="form-bt" ng-model="ages[$index]">{{t_age +1}}</li>
$scope.t_age = t_age; might be a string, just use parseInt(t_age); A simple fiddle to help..
<div ng-app="">
<div ng-controller="MyCtrl">
{{age + 1}}
</div>
</div>
JS:
function MyCtrl($scope) {
$scope.age = parseInt('24');
}
http://fiddle.jshell.net/0wh13nrz/1/
In your controller, you have to parse your age which is a string into a int
parseInt('string', 10); // parseInt with radix
CONTROLLER
mainApp.controller("displayController", ['$scope', '$http', 'shareDataService', function($scope, $http, shareDataService) {
$scope.url = 'incomeBenefit.php';
$http.get($scope.url)
.then(function (response) {$scope.names = response.data.records;
for(X in $scope.names){
var t_age = parseInt($scope.names[X]['client_age'], 10);
var t_premium = $scope.names[X]['premium'];
}
$scope.t_age = t_age;
$scope.t_premium = t_premium;
});
}]);
Now you variable t_age is an integer
What kind of array is names. If it has name object then seeing your controller your html code must look something like this-
<ul id="form-bt-main" class="sec-row" ng-repeat="n in names | range:31">
<li class="form-bt">{{n.name}}</li>
<li class="form-bt">{{n.client_age = n.client_age + 1}}</li>
<li class="form-bt">{{n.premium}}</li>
<li class="form-bt">0</li>
<li class="form-bt">27000</li>
<li class="form-bt">0</li>
</ul>
and your controllers should not have loop to manipulate the names array, ng-repeat does that looping. Your controller should also change to -
mainApp.controller("displayController", ['$scope', '$http', 'shareDataService', function($scope, $http, shareDataService) {
$scope.url = 'incomeBenefit.php';
$http.get($scope.url)
.then(function (response) {
$scope.names = response.data.records;
});
}]);
If you need to output values like that, try to ng-repeat number. Example:
<ul>
<li ng-repeat="i in getNumber(7)">{{$index+number}}</li>
</ul>
And in your controller:
$scope.number = parseInt('24');
$scope.getNumber = function(num) {
return new Array(num);
}
live demo (jsfiddle).
full post about that trick is here

What does this tag do?

I have been learning how to read other people's code and when ever I see something like this <meganav-item item="item" ng-repeat="item in website.nav.primary"></meganav-item> I get stuck.
I have a basic understand of angular, but the problem is the <meganav> tag. I do not know what this is..I have done a Google search, but nothing useful is showing.
Update
I have managed to locate the file of the <meganav> element. After following the instructions from the links that you guys have provided, it led me to a file named "MegaNavItem.js". Here is the code:
window.tcoStore.directive('meganavItem', ['$timeout','transport', function($timeout,transport) {
var lockTimeout = false;
var meganavLocks = transport.getModel('meganavLocks', {lock : false});
var clear = function (){
if(meganavLocks.timeout){
$timeout.cancel(meganavLocks.timeout);
}
}
var action = function(callback, time) {
if(meganavLocks.lock){
return;
}
clear();
meganavLocks.timeout = $timeout(callback, time);
}
var dropLock = function(callback, time) {
meganavLocks.lock = false;
}
return {
restrict : 'E',
replace: true,
templateUrl : '/page/header/meganav/item.html',
scope : {
item : '=',
clickOnly : '#',
delayIn : '#',
delayOut : '#'
},
link : function($scope, elem, attrs){
if(!$scope.clickOnly){
$scope.delayInValue = parseInt($scope.delayIn || 300,10);
$scope.delayOutValue = parseInt($scope.delayOut || 500,10);
elem.on('mouseenter', $scope.showDelayed);
if($scope.delayOutValue > 0){
elem.on('mouseleave', $scope.hideDelayed);
}
}
},
controller: ['$scope', '$timeout', 'transport', '$location' ,
function($scope, $timeout, transport,$location) {
// When $location changes ...
$scope.$on('$locationChangeSuccess', function() {
$scope.hide(true);
$scope.isActive = !_.isUndefined($scope.item.link) && ($scope.item.link.replace(/\/+$/,'') == $location.path().replace(/\/+$/,''));
});
$scope.loadSubmenu =0;
// tranposrt model accessable by other items
var meganavVisibleModel = transport.getModel('meganavActive');
var meganavVisibleModelId = $scope.item.$$hashKey;
meganavVisibleModel[meganavVisibleModelId] = false;
// hide and show funs
$scope.hide = function(forceFullClose){
clear();
meganavVisibleModel[meganavVisibleModelId] = false;
if(forceFullClose) {
meganavLocks.lock = true;
$timeout.cancel(lockTimeout);
lockTimeout = $timeout(dropLock, 1000);
}
};
$scope.hideDelayed = function (delay) {
action($scope.hide, _.isNumber(delay) ? delay : $scope.delayOutValue);
};
$scope.show = function(){
if(meganavLocks.lock){
return;
}
clear();
$scope.loadSubmenu = 1;
for(var i in meganavVisibleModel){
meganavVisibleModel[i] = (meganavVisibleModelId == i);
}
};
$scope.showDelayed = function (delay) {
action($scope.show, _.isNumber(delay) ? delay : $scope.delayInValue);
};
$scope.$watch(function(){
$scope.visible = meganavVisibleModel[meganavVisibleModelId];
});
// first touch click, second touch go to link
$scope.touch = function($event, path){
if(!$scope.visible) {
//$event.preventDefault();
$scope.show();
}else if(tco.empty(path)) {
$scope.hide();
} else {
if(path.match(/^https?:/)){
window.location.href = path;
}else{
$location.path(path);
}
}
}
}]
}
}]);
And this file led me to another file named item.html. The code :
<li class="header--menu__item my-repeat-animation" ng-class="{ 'is-active': isActive, open : visible && item.groups.length}" off-click="hide()" >
<a ng-if=":: item.groups.length"
ng-class="{active: item.active}"
class="header--menu__item--link has-children"
ng-click="show()"
title="{{::item.name}}">
{{::item.name}}
</a>
<a ng-if=":: !item.groups.length"
class="header--menu__item--link"
href="{{::item.link}}"
title="{{::item.name}}">
{{::item.name}}
</a>
<div class="header-menu-dropdown ng-hide" ng-show="visible" ng-if=":: item.groups.length">
<ul class="header-menu-dropdown__meganavGroup">
<li ng-repeat="meganavGroup in item.groups" class="header--menu-group">
<span class="meganav--group--name">{{::meganavGroup.name}}</span>
<ul class="meganav--group--items">
<li ng-repeat="groupItem in meganavGroup.items">
{{::groupItem.name}}
<span class="icon"></span>
</li>
</ul>
</li>
<li class="header-menu-offers" ng-repeat="offer in item.offers">
<a href="{{::offer.offer_link}}" class="placeholder">
<img tco-image="offer.offer_image" crop="3" alt="{{::offer.offer_name}}" />
</a>
<span class="offer-name">{{::offer.offer_name}}</span>
</li>
</ul>
<div class="header-menu-message" ng-bind-html="item.message"></div>
</div>
</li>
My issue is now that I cannot make out what where to find {{::item.name}}, which is the thing that I want to change. What technique can I use to find {{::item.name}}?
Sorry for all the noob questions! Your help is much appreciated!
In Angular it is possible to build your own HTML element. You won't find any information about this element because it doesn't exist. The developer has created that on its own and handles the content inside a module. Have a look at http://www.aleaiactaest.ch/2012/07/29/build-your-own-html-element-with-angular/ for more information.
Hope this helps, Cheers.
As i've noticed it's Angular App, so probably there are defined an directive which is called meganavItem. See Angular Directive for more information, you have to find definition of that directive and discover what is html layout and logic lives under <meganav-item>. However if there are no directive with defined name.
Also it may be separate registered element, see "Custom Elements
"article of how it's done and it would be more easy for you to find out how it works ( if it registered in that way...)

clone object with unique name in Angular

I'm new to Angular and looking for a way to make a clone button inside a list.
When I click this button, it will clone the object and add a number to the object name:
"new test" - will be changed to "new test (2)" and so on...
It requires to check the last 3 letters every time and check all the objects every time.
Is there any library doing this?
I think you don't need any lib for this. Basically, it you need one loop that looks for duplicates. See an example:
angular.module('myApp', [])
.controller('MyCtrl', function($scope){
$scope.items = ['Sample item'];
$scope.suggestedNewName = 'Sample item';
var isNameOccupied = function(name) {
return $scope.items.indexOf(name) >= 0;
};
$scope.addNewItem = function(){
var suggestedName = $scope.suggestedNewName;
var newName = suggestedName;
for (var i = 2; isNameOccupied(newName); i++) {
newName = suggestedName + " (" + i + ")";
}
$scope.items.push(newName);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<form ng-controller="MyCtrl" ng-submit="addNewItem()">
<ul>
<li ng-repeat="item in items">{{ item }}</li>
</ul>
<label>Suggested name for new item:</label>
<input type="text" ng-model="suggestedNewName">
<button type="submit">Add new item!</button>
</form>
</div>

How can i make pagination through AngularJS?

Recently I create a AngularJs App . Thats code are below
HTML :
<div ng-app="" ng-controller="Hello">
<ul>
<li ng-repeat="x in greeting">
{{ x.user_name }}
</li>
</ul>
</div>
And my JS code is:
function Hello($scope, $http) {
$http.get('http://localhost/google/cibl/dashboard/get_all_user').
success(function(data) {
$scope.greeting = data;
});
}
Its working fine. This http service give me 2000 row now i want to paginate this by AngularJs. How can I do that ?
In your controller
app.controller('Hello', function($scope){
$scope.pageSize = 10;
$scope.currentPage = 0;
$scope.changePage = function(page){
$scope.currentPage = page;
}
})
In your mark up, you should have
<div ng-app="" ng-controller="Hello">
<ul>
<li ng-repeat="x in greeting | startFrom: currentPage * pageSize | limitTo: pageSize">
{{ x.user_name }}
</li>
</ul>
</div>
We're missing the startFrom filter so lets create that
app.filter('startFrom', function() {
return function(input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
Now all thats left is the paginating panel, I'll leave it up to you to pretty it with css
<ul class="pagination" >
<li ng-repeat="page in pagination" ng-class="{'active':currentPage == page}"><a ng-click="changePage(page)">{{page + 1}}</a></li>
</ul>
Notes:
The reason why we use changePage() instead of currentPage = page is due to ng-repeat which could break some of the variables
In your anchor () tag, instead of ng-click, you can use a href to mark the page and in your controller, watch the page ref and change based on the queries. The benefits to this is that when you decide to do SEO for your website, it will be ready for that!
href="#!/partialname?page={{page}}"
You can do this way:
Pagination Example: http://jsfiddle.net/2ZzZB/56/
Found it in this question:
Pagination on a list using ng-repeat
At least I got a solution and its work properly :
HTML :
<div ng-controller="userController" class="jumbotron">
<h2>User List</h2>
<table class="table table-striped">
<thead>
<tr>
<th>User </th>
<th>Group</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="u in users | itemsPerPage : 5">
<td>{{u.user_name}}</td>
<td>{{u.user_type}}</td>
</tr>
</tbody>
</table>
<dir-pagination-controls on-page-change="pageChanged(current)" template-url="<?php echo base_url(); ?>js/dirPagination.tpl.html"></dir-pagination-controls>
</div>
and JS :
Here i use AngularJs pagination directive
function userController($scope, $http) {
$scope.users = [];
$scope.total = 0;
$scope.perPage = 25; // this should match however many results your API puts on one page
getUsers(1);
$scope.pagination = {
current: 1
};
$scope.pageChanged = function (newPage) {
getUsers(newPage);
};
function getUsers(pageNumber) {
// this is just an example, in reality this stuff should be in a service
$http.get(app.baseUrl + 'dashboard/get_all_user/' + pageNumber)
.success(function (data) {
console.log(data);
$scope.users = data.users;
$scope.total = data.total;
})
.error(function (data) {
console.log(data);
alert("There was a problem. Please try again later.");
});
}
};
var myApp = angular.module('myApp', ['angularUtils.directives.dirPagination']);
var app = app || {};
app.baseUrl = '<?= base_url() ?>';
myApp.controller('userController', userController);