Dynamically inserting elements in Angular JS - angularjs

I have three tabs(Article(s), Visitor(s), Subscription(s)) and a common place of pagination; where each tab data will be provided with pagination. On click of the respective tab; respective ng-view are coming to picture and controllers are responding properly. For this custom made pagination; i want to update the number if <li> accordingly on the basis of the server response(number of pages available for next pagination).
<div ng-app="myLibrary">
<ul>
<li>Article(s)</li>
<li>Visitor(s)</li>
<li>Subscription(s)</li>
<li>
<ul> //will behave as pagination toolbar and each <li> represents a page; after a minimum of 5 pages; i will add a combo(as in plan) to cater more page(s)
<li ng-repeat="tPageObj in recordPageNumbers">
<span ng-click="fetchPage(tPageObj.pageIndex)">{{ tPageObj.pageIndex }}</span>
</li>
</ul>
</li>
</ul>
</div>
<div ng-view></div>
When the view is rendered(after getting the data from server; i have a array with the $scope ($scope.recordPageNumbers) and calculating the page(s) accordingly. Even in the console; it shows appropriate number of page(s); but i am unable to figure-out why the ng-repeat is not behaving(as i learned so-far; being two way binding modification in the model will trigar the view update) as it should.
var myLibrary = angular.module('myLibrary', ['ngRoute', 'ngTable']);
myLibrary.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/',
{ templateUrl : 'angular-view/article.html', controller : 'articleList' } );
$routeProvider.when('/articleManager',
{ templateUrl : 'angular-view/article.html', controller : 'articleList' } );
$routeProvider.when('/visitorManager',
{ templateUrl : 'angular-view/visitor.html', controller : 'visitorList' } );
$routeProvider.when('/subscriptionsManager',
{ templateUrl : 'angular-view/subscriptions.html', controller : 'subscriptions' } );
}]);
myLibrary.controller('articleList', function($scope, $http){
$scope.articleListArray = [];
$scope.recordPageNumbers = [];
$http.get('ngMyLibrary.do?action=ALLARTICLE')
.success(function(response) {
$scope.articleListArray = response.data; //sending data to `ng-view`
var totalPageCount = response.totalPageCount;//calculating pages and according creating the `recordPageNumbers` array.
if(totalPageCount){
for(var counter = 1; counter <= totalPageCount; counter++)
$scope.recordPageNumbers.push({pageIndex : counter, disableButton : false});
} else {
$scope.recordPageNumbers.push({pageIndex : 1, disableButton : true});
}
console.log($scope.recordPageNumbers); //console show as expected
}
);
});
Console:
[Object { pageIndex=1}, Object { pageIndex=2}, Object { pageIndex=3}, Object { pageIndex=4}, Object { pageIndex=5}, Object { pageIndex=6}]
I tried with {{ $index }} as the loop index of the ng-repeat but it din't work as well. Please help. I am newbie to ng; hence could not figure out the way to check within the ng-repat tag through debug.

Without seeing the full project structure it is a bit difficult to know. But have you checked to see if the controllers scope covers the HTML you are trying to use?
For example update the first line to be
<div ng-app="myLibrary" ng-controller="articleList">
It could be that the way you have it set up with the routing, the controller is only being responsible for the HTML being injected into the ng-view part of the page.

Related

ng-repeat is not refreshed when ng-click method called from directive

I am working on creating reusable directive which will be showing composite hierarchical data .
On first page load, Categories like "Server" / "Software"/ "Motherboard" (items array bound to ng-repeat) would be displayed . If user clicks on "Server" then it would show available servers like "Ser1"/"Ser2"/"Ser3".
html :
<div ng-app="myApp" ng-controller="myCtrl" ng-init="init()">
<ul>
<li ng-repeat="item in items">
<div my-dir paramitem="item"></div>
</li>
</ul>
</div>
Now first time Items are loading, but clicking on any item is not refreshing ng-repeat. I have checked ng-click, "subItemClick" in below controller, method and it is being fired. However the items collection is not getting refreshed.
http://plnkr.co/edit/rZk9cbEJU90oupVgcSQt
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.init = function() {
$scope.items = [{iname: 'server',subItems: ['ser1', 'ser2','ser3']}
];
};
$scope.subItemClick = function(sb) {
if (sb.subItems.length > 0) {
var zdupitems = [];
for (var i = 0; i < sb.subItems.length; i++) {
zdupitems.push({
iname: sb.subItems[i],
subItems: []
});
}
$scope.items = zdupitems;
}
};
}])
.directive('myDir', function() {
return {
controller: 'myCtrl',
template: "<div><a href=# ng-click='subItemClick(paramitem)'>{{paramitem.iname}}</a></div>",
scope: {
paramitem: '='
}
}
});
I am expecting items like ser1/ser2 to be bound to ng-repeat on clicking "Server" but it is not happening .
Any help?
I think that onClick is screwing up the method's definition of $scope. In that context, the $scope that renders the ngRepeat is actually $scope.$parent (do not use $scope.$parent), and you're creating a new items array on the wrong $scope.
I realize the jsfiddle is probably a dumbed down example of what you're dealing with, but it's the wrong approach either way. If you need to use a global value, you should be getting it from an injected Service so that if one component resets a value that new value is reflected everywhere. Or you could just not put that onClick element in a separate Directive. What's the value in that?

Progressive scrolling with angularjs

I have a database witch contains 1000 products. I have a list which displays 10 products at one time. This grid is scrollable. I want to load products 10 by 10.
What is the best way to do this with angularjs ? I am looking for a method which need shortest code.
Thanks
The concept is called pagination and if you want to achieve this in less amount of time then you must go for any already available module.
I have used Infinite Scroll It is very easy to use and would help you to implement things in no time.
You are looking for lib called "infinite scroll". You can find this app here https://github.com/ifwe/infinite-scroll
Where in the controller you put this code:
var app = angular.module('MyApp', ['tagged.directives.infiniteScroll']);
app.controller('MainController', ['$scope', '$http', function($scope, $http) {
$scope.page = 1;
$scope.items = [];
$scope.fetching = false;
// Fetch more items
$scope.getMore = function() {
$scope.page++;
$scope.fetching = true;
$http.get('/my/endpoint', { page : $scope.page }).then(function(items) {
$scope.fetching = false;
// Append the items to the list
$scope.items = $scope.items.concat(items);
});
};
}]);
And this is your view:
<div ng-app="MyApp">
<div ng-controller="MainController">
<ul tagged-infinite-scroll="getMore()">
<li ng-repeat="item in items">
{{ item.title }}
</li>
</ul>
</div>
</div>
Once it detect you scroll is at the end of the ng-repeat items it calls getMore function to load new items. Remember to add to the endpoint limit, and offset to load rest of the data, not same data every time.

Ng-repeat using $scope from a different state?

I have two views, the first one (Search) has a button which when clicked will add an item to $scope.results1 and then take the user to the other view (Results) where the ng-repeat is.
When I click the button and the results page comes up, only "1" is displayed. However, if I call the test function straight away in the controller, I get taken to the Results page and both "1" and "2" are displayed. In both cases, the console log shows that the array results1 contains 2 items.
From what I've read, the solution would be to implement either a factory or a service but I'm fairly new to Ionic/angular so not quite sure how to begin such an implementation, any pointers would be appreciated!
Button in Search view :
<button class="button-full" id="find" ng-click="test();">Find</button>
SearchController:
$scope.results1=[];
$scope.results1.push(1);
$scope.test = function(){
$scope.results1.push(2);
console.log("pushed 2");
console.log($scope.results1);
$state.go("tab.results");
};
Results view:
<ion-content ng-controller="SearchController">
<body>
<div id="results">
<div class="list" id="search-items">
<div ng-repeat="item in results1">
{{item}}
</div>
</div>
</div>
</body>
</ion-content>
You could implememnt a service for holding Results like this
var mainApplicationModule = angular.module("yourAppName");
mainApplicationModule.service('ResultService', function(){
var results = [];
this.add = function(data){ // to add data to results
results.push(data);
}
this.getResults = function(){ // to get all results
return(results);
}
})
Inject ResultService into your SearchController like this,
mainApplicationModule.controller('SearchController',['$scope','ResultService','$location', function($scope,ResultService,$location) {
ResultService.add(1) // Adds 1 to 'results' array in ResultService
$scope.test = function() {
ResultService.add(2); // Adds 2 to Results array in ResultService
$location.path("/results") // replace with path to your results view
}
$scope.results1 = ResultService.getResults(); // will have [1,2]
}
you can pass data while changing state,
config the state like this:
.state('tab.results', {
url: '/yoururl',
templateUrl: 'yourtemplate',
controller: 'yourcontroller',
params: {
"results": ""
}
});
then using :
$state.go("tab.results",{"results": $scope.results1});
in the second controller inject $stateParams and get value:
$scope.results = $stateParams.results;

What is the "right" way in Angularjs of doing "master-detail" inter directive communication

I have a directive that displays a list of "master" items and when the user clicks on one of these items I want any "details" directives on the page (there could be more than one) to be updated with the details of the currently selected "master" item.
Currently I'm using id and href attributes as a way for a "details" directive to find its corresponding master directive. But my impression is that this is not the angular way, so if it's not, what would be a better solution?
I appreciate that typically when the issue of inter-communication between directives is raised then the obvious solutions are either to use require: "^master-directive" or to use a service, but in this case the directives are not in the same hierarchy and I don't think using a service is appropriate, as it would make the solution more complicated.
This is some illustrative code showing what I'm doing currently.
<div>
<master-list id="master1"></master-list>
</div>
<div>
<details-item href="#master1" ></details-item>
</div>
In the master-list directive when an item is selected I set an attribute to indicate the currently selected master item:
attrs.$set('masterListItemId',item.id);
In the details-item directive's link function I do:
if (attrs.href) {
var id = attrs.href.split('#')[1];
var masterList = angular.element(document.getElementById(id));
if (masterList) {
var ctrl = masterList.controller('masterList');
ctrl.attrs().$observe('masterListItemId',function(value) {
attrs.$set('detailItemId',value);
});
}
}
attrs.$observe('detailItemId',function(id) {
// detail id changed so refresh
});
One aspect that put me off from using a service for inter-directive communication was that it is possible (in my situation) to have multiple 'masterList' elements on the same page and if these were logically related to the same service, the service would end up managing the selection state of multiple masterList elements. If you then consider each masterList element had an associated detailItem how are the right detailItem elements updated to reflect the state of its associated masterList?
<div>
<master-list id="master1"></master-list>
</div>
<div>
<master-list id="master2"></master-list>
</div>
<div>
<details-item href="#master1" ></details-item>
</div>
<div>
<details-item href="#master2" ></details-item>
</div>
Finally I was trying to use directives, rather than using controller code (as has been sensibly suggested) as I'd really like the relationship between a masterList and its associated detailItems to be 'declared' in the html, rather than javascript, so it is obvious how the elements relate to each other by looking at the html alone.
This is particularly important as I have users that have sufficient knowledge to create a html ui using directives, but understanding javascript is a step too far.
Is there a better way of achieving the same thing that is more aligned with the angular way of doing things?
I think I would use a service for this. The service would hold the details data you care about, so it would look something like this.
In your master-list template, you might have something like a list of items:
<ul>
<li ng-repeat"item in items"><a ng-click="select(item)">{{item.name}}</a></li>
</ul>
...or similar.
Then in your directives, you would have (partial code only)
.directive('masterList',function(DetailsService) {
return {
controller: function($scope) {
$scope.select = function(item) {
DetailsService.pick(item); // or however you get and retrieve data
};
}
};
})
.directive('detailsItem',function(DetailsService) {
return {
controller: function($scope) { // you could do this in the link as well
$scope.data = DetailsService.item;
}
};
})
And then use data in your details template:
<div>Details for {{data.name}}</div>
<ul>
<li ng-repeat="detail in data.details">{{detail.description}}</li>
</ul>
Or something like that.
I would not use id or href, instead use a service to retrieve, save and pass the info.
EDIT:
Here is a jsfiddle that does it between 2 controllers but a directive would be the same idea
http://jsfiddle.net/u3u5kte7/
EDIT:
If you want to have multiple masters and details, leave the templates unchanged, but change your directive controllers and services as follows:
.directive('masterList',function(DetailsService) {
return {
controller: function($scope) {
$scope.select = function(item) {
DetailsService.pick($scope.listId,item); // or however you get and retrieve data
};
}
};
})
.directive('detailsItem',function(DetailsService) {
return {
controller: function($scope) { // you could do this in the link as well
$scope.data = DetailsService.get($scope.listId).item;
}
};
})
.factory('DetailsService',function(){
var data = {};
return {
pick: function(id,item) {
data[id] = data[id] || {item:{}};
// set data[id].item to whatever you want here
},
get: function(id) {
data[id] = data[id] || {item:{}};
return data[id];
}
};
})
I would opt for a different approach altogether without directives. Directives are ideal for DOM manipulation. But in this case I would stick to using just the template and a controller that manages all the data and get rid of the directives. Use ng-repeat to repeat the items
Check out this fiddle for an example of this: http://jsfiddle.net/wbrand/2xrne4k3
template:
<div ng-controller="ItemController as ic">
Masterlist:
<ul><li ng-repeat="item in ic.items" ng-click="ic.selected($index)">{{item.prop1}}</li></ul>
Detaillist:
<ul><li ng-repeat="item in ic.items" >
{{item.prop1}}
<span ng-if="item.selected">SELECTED!</span>
</li></ul>
</div>
controller:
angular.module('app',[]).controller('ItemController',function(){
this.items = [{prop1:'some value'},{prop1:'some other value'}]
this.selectedItemIndex;
this.selected = function(index){
this.items.forEach(function(item){
item.selected = false;
})
this.items[index].selected = true
}
})

AngularJS - multiple use of component with same controller, using different data

I have a simple component that is being included using ng-include. The controller of the component is assigned in the included HTML.
I would like to use the same component twice (or more) but load different data.
What I've tried to do is parse the JSON and create new objects (which works fine) but i can't assign the data to the appropriate component using it's id attribute.
Any help will be nice. A directive? jQuery?
I know this is a some-what weird way of doing it, so I'll add that at the moment the project's business logic needs this r similar behaviour.
JSON:
[
{
"componentId" : 'component1",
"data" : "Hello"
},
{
"componentId" : 'component1",
"data" : "World"
}
]
Component Template (someTemplate.tpl.html)
<div data-ng-controller="DefaultController">
<h2>{{message}}</h2>
</div>
Main HTML
<div data-ng-controller="MainController">
<div data-ng-include="'someTemplate.tpl.html'" id="component1"></div>
<div data-ng-include="'someTemplate.tpl.html'" id="component2"></div>
</div>
Controller
function DefaultController($scope){
var componentId = ???
$scope.message = $scope[componentId].data;
}
Main controller
function MainController($scope, $http){
$http.get('data.json').then(function(response){
$scope.data = response;
for(var i in $scope.data){
$scope[$scope.data[i].componentId] = $scope.data[i].data;
}
}
}

Resources