I'm newbie in AngularJS, I have a question, please explain to me the reason of my mistake. At first, I have a factory name CategoryParent with this function declared like this,
routerApp.factory('CategoryParent', function($http) {
var categoryParentFactory = {};
var hostCMSAPI="http://***.***.***.***:****";
// get all categories
categoryParentFactory.allCategoryParents = function() {
console.log("call get allCategoryParents");
return $http.get(hostCMSAPI+'/api/categoryparents/');
};
.......
and a controller call this function named categoryParentController:
.controller('categoryParentController', function(CategoryParent,$scope) {
console.log("cateParent ctrl");
$scope.processing=true;
$scope.dataList=[];
$scope.getAllCategoryParents=function(){
CategoryParent.allCategoryParents().success(function(response){
$scope.processing = false;
$scope.list=response;
});
I'm using ui.router like this (nested view):
.state('home.cateParentMenu',{
url:'/cateParentMenu',
templateUrl:'categoryParentTop.html',
controller:'categoryParentController',
controllerAs:'categoryParent'
})
Parent view trigger controller function here:
The Homey Page
This page demonstrates nested views.
<a ui-sref=".list" class="btn btn-primary">List</a>
<a ui-sref=".paragraph" class="btn btn-danger">Paragraph</a>
<a ui-sref=".cateParentMenu" class="btn btn-warning" ng-click="getAllCategoryParents()">cateParentMenu</a>
and last, the children view come here
data show
{{processing}}
{{list}}
{{item.cate_parent_name}}
<ul class="nav navbar-nav" >
data show
{{processing}}
{{list}}
<li data-ng-repeat="item in list.data"><a ui-sref=".detail({cate_parent_id:item.cate_parent_id})" ng-click="getById(item.cate_parent_id)" ui-sref-active="active" id="{{item.cate_parent_id}}">{{item.cate_parent_name}}</a></li>
</ul>
<div class="col-sm-6">
<div ui-view=""></div>
<!--<div ui-view="serviceRef"></div>-->
<!--<div ui-view="categoryRef"></div>-->
</div>
I set $scope.list and $scope.processing to transfer result to view. But in view I show {{list}}, nothing appears and {{processing}} = true ???
Why? please help me, many thanks to all your suggests.
Controller
Fix $scope.dataList=[]; to $scope.list=[];
Related
I'm new in angular and I reeding A.Freeman's book "Pro Angular JS".
So I stuck in one of examples trying to understand why filter in ng-repeat is triggered.
Here is the code:
<body ng-controller="sportsStoreCtrl">
<div class="navbar navbar-inverse">
<a class="navbar-brand" href="#">SPORTS STORE</a>
</div>
<div class="panel panel-default row" ng-controller="productListCtrl">
<div class="col-xs-3">
<a ng-click="selectCategory()" class="btn btn-block btn-default btn-lg">Home</a>
<a ng-repeat="item in data.products | orderBy:'category' | unique:'category'" ng-click="selectCategory(item)" class=" btn btn-block btn-default btn-lg">
{{item}}
</a>
</div>
<div class="col-xs-8">
<div class="well" ng-repeat="item in data.products | filter:categoryFilterFn">
<h3>
<strong>{{item.name}}</strong>
<span class="pull-right label label-primary">
{{item.price | currency}}
</span>
</h3>
<span class="lead">{{item.description}}</span>
</div>
</div>
</div>
</body>
and
angular.module("sportsStore")
.controller("productListCtrl", function ($scope, $filter) {
var selectedCategory = null;
$scope.selectCategory = function (newCategory) {
selectedCategory = newCategory;
}
$scope.categoryFilterFn = function (product) {
return selectedCategory == null ||
product.category == selectedCategory;
}
});
categoryFilterFn is one that confuses me. Why it's invoking when I press catefory buttons (with selectCategory() method on ng-click) since I never call categoryFilterFn explicitly?
Answering you question - because of $digest. You don't have call categoryFilterFn directly. Your selectedCategory has changed which is used in categoryFilterFn and categoryFilterFn is bound to scope.
Not sure how I can describe it correctly but here my explanation.
There are two "independent" parts :
The repeat iterate over an array of items.
If you select an category via ng-click function you set the new category in the scope.
Here kicks the filter function in, witch ties it up.
It is triggered because a new category is selected ($digest) and "reordering" the array (like map function in plain Javascript) and the angular magic (ng-repeat) displays only items with this category.
And that's the reason why I love angular so much 🤓
In angularjs to show/hide dynamic submenu, I am adding and removing dynamic classes in js file. Every time when the state changes in url (i.e ui-sref={{mymenu.url}}) sub menu is not visible. If there is no state change sub menu is working properly. Can anyone please suggest.
Html
<li ng-repeat='mymenu in menuItems' ng-click="showHideMenu($event);" >
<a class="iconsize" ui-sref={{mymenu.url}}>
<i class={{mymenu.image}}></i> <strong>{{mymenu.menuName}}</strong>
<span class="fa fa-chevron-down"></span>
</a>
<ul class="submenuHide">
<li class="" ng-repeat='submenu in mymenu.submenu'>
<a>{{submenu.submenuName}}</a>
</li>
</ul>
JS
$scope.showHideMenu = function(event) {
var classname = event.target.parentElement.parentElement.children[1].className;
if(classname == 'submenuHide'){
$(event.target.parentElement.parentElement.children[1]).removeClass('submenuHide');
$(event.target.parentElement.parentElement.children[1]).addClass('submenuShow');
}else if(classname == 'submenuShow'){
$(event.target.parentElement.parentElement.children[1]).removeClass('submenuShow');
$(event.target.parentElement.parentElement.children[1]).addClass('submenuHide');
}
}
A couple things. One, you'll need to make sure your menu is outside of the individual templates you're working with. Two, using ng-class bound to an ng-model ensures that your menu state is included in the digest cycle. This will save you the jqLite and dom parsing logic.
Here's an example plunker.
So your code might look like this:
<body ng-controller="MainCtrl">
<a ui-sref="hello">Hello</a>
<a ui-sref="about">About</a>
<button ng-click="toggleMenu()">Show / Hide Menu</button>
<ui-view></ui-view>
<ul ng-class="{showMenu: show, hideMenu: !show}">
<li ng-repeat="letter in ['a','b','c','d']">{{letter}}</li>
</ul>
</body>
With this JS:
app.controller('MainCtrl', function($scope) {
$scope.show = false;
$scope.toggleMenu = function() {
$scope.show = !$scope.show;
};
});
I am working on an angular app which integrates with facebook. I have given the code below,
$scope.getCompanyPages = function () {
fb.login(function (response) {
fb.api('/me/accounts', function (apiresponse) {
if (typeof apiresponse !== 'undefined' && typeof apiresponse.data !== 'undefined') {
$scope.facebookPages = apiresponse.data;
$scope.facebookPages.push({ id: "", name: 'Please select a page' });
$scope.selectedItem = $scope.facebookPages[2];
console.log($scope.facebookPages);
}
});
}, { scope: 'manage_pages' });
};
HTML
<div class="row">
<div class="col-xs-12 col-sm-6 center">
<a href="#" ng-click="shareToMyWall()">
<div class="primary-task">
<i class="fa fa-user fa-3x">
</i>
<h2 class="heading-text fa-bold">Personal Wall</h2>
</div>
</a>
</div>
<div class="col-xs-12 col-sm-6 center">
<a href="#" ng-click="getCompanyPages()">
<div class="primary-task">
<i class="fa fa-building fa-3x">
</i>
<h2 class="heading-text fa-bold">Company Wall</h2>
</div>
</a>
<select ng-if="facebookPages.length > 0" ng-change="shareToFacebookPage(selectedItem.id)" ng-model="selectedItem" ng-options="item.name for item in facebookPages track by item.id"></select>
</div>
</div>
The above method is called in hyperlink click (ng-click) event and the when the response comes back the drop down should update with data. The data does come back but it doesn't update straight away, rather I have to do another click anywhere in the page to update drop down list.
The data in the model is updated when you do the first ng-click. However, AngualrJS probably does not do check so the view still shows the old data. You can force a $digest() check and tell AngualrJS to compare old data and new data and update it if there is any difference. You can use $timeout, $evalAsync, or $apply to trigger $digest() which will update the data immediately.
For example:
$timeout(function () {
$scope.getCompanyPages = function () {...}
//or
$scope.facebookPages = apiresponse.data;
})
Edit: according to Angular JS best coding practice
Always wrap 3rd party API call-backs in to $apply to notify AngularJS
regarding out of environment changes.
This is probably pretty basic, but I'm going round in circles.
I have a nested controller in a ng-repeat - I would like to trigger an event in an instance of the repeat that will affect only the nested controller in that instance, not the nested controller in all instances.
Here is a basic example to try and make things clearer:
<div ng-controller="PostsCtrl">
<div ng-repeat="post in posts">
<p>{{post.body}}</p>
<div ng-controller="CommentsCtrl">
<div ng-repeat="comment in comments">
<p>{{comments.body}}</p>
<a href ng-click="showComments(post.id)">Show comments</a>
</div
</div>
</div>
</div>
angular.module('myApp')
.controller('PostsCtrl', function ($scope, Restangular) {
var posts = Restangular.all('posts');
posts.getList().then(function(posts) {
$scope.posts = posts;
});
$scope.showComments = function(id) {
$scope.$broadcast('SHOW_COMMENTS', id);
};
});
angular.module('myApp')
.controller('CommentsCtrl', function ($scope, Restangular) {
$scope.$on('SHOW_COMMENTS', function(event, id) {
var post = Restangular.one('posts', id);
post.get().then(function(post) {
$scope.comments = post.comments;
});
});
});
What would happen in this example is that all instances of the comments controller would be populated with the same comments - I just need to be able to target the relevant one. I'm sure I'm missing something pretty fundamental, and probably going about this the wrong way.
Many thanks.
You're broadcasting "SHOW_COMMENTS" to all repeated posts. You need to isolate the scope per Post.
Isolate the scope using ng-controller="PostCtrl".
<div ng-controller="PostsCtrl">
<div ng-repeat="post in posts" ng-controller="PostCtrl">
<p>{{post.body}}</p>
<div ng-controller="CommentsCtrl">
<div ng-repeat="comment in comments">
<p>{{comments.body}}</p>
<a href ng-click="showComments()">Show comments</a>
</div
</div>
</div>
</div>
Move show comments from PostsCtrl to PostCtrl.
angular.module('myApp')
.controller('PostCtrl', function ($scope) {
$scope.showComments = function() {
$scope.$broadcast('SHOW_COMMENTS', $scope.post.id);
};
});
Do you really need to use events? You could use ng-if instead.
Your comments are embedded in each Post anyway so why request them again. In this case you could do like something this...
<div ng-controller="PostsCtrl">
<div ng-repeat="post in posts" ng-controller="PostCtrl">
<p>{{post.body}}</p>
<a href ng-click="toggleComments()">Show comments</a>
<div ng-controller="CommentsCtrl" ng-if="showComments">
<div ng-repeat="comment in post.comments">
<p>{{comments.body}}</p>
</div
</div>
</div>
</div>
Toggle comments...
angular.module('myApp')
.controller('PostCtrl', function ($scope) {
$scope.showComments = false;
$scope.toggleComments = function() {
$scope.showComments = !$scope.showComments;
};
});
There is a list of users retrieved from a rest api. Here is the template
<div ng:controller="UserController">
<a ng-click="createUser()">Create User</a>
<div ng-view>
<ul>
<li ng-repeat="user in users">
{[{user.first_name}]} {[{user.last_name}]}
</li>
</ul>
</div>
</div>
The JS:
function UserController($scope, User, Group){
$scope.users = User.query();
$scope.createUser = function(){
//$scope.users = null;
//$scope.users.pop();
//$scope.users.push(new User({id:'5'}));
console.log($scope.users);
}
}
The service: http://dpaste.com/1065440/
All users a retrieved and listed correctly. The problem is that I cannot manipulate the rendered list at all. No matter what I do push, pop or set to null. The list does not change in the template. However the last log statement shows the changes, it prints e.g. NULL when the users array is set to null.
Any ideas where the problem is?
The object you push into the array should be an instance of User
function UserController($scope, User){
$scope.users = User.query();
$scope.createUser = function(){
$scope.users.push(new User({first_name:'Bob', last_name: 'Schmitt'}));
}
}
So, use new User({})
From our conversation, it seems the problem was in the routing. The same outer controller was assigned to the partial that was being loaded in the ng-view. Removing ng:controller="UserController" and moving the createUser button to the partial would solve the problem, but if there's really a need to call the createUser method from outside of ng-view, then all the data related to it will need to be in the outer controller. So, you can keep your outer controller as it is, and change your route to use an empty placeholder controller.
make sure createUser is being called. IE ng-click or something.
<button type="button" ng-click="createUser()">Create User</button>
Your push function looks correct, but your binding in html looks wrong. It should be double curly brackets.
<li ng-repeat="user in users">
{{user.first_name}} {{user.last_name}}
</li>
Added Example I've used previously on adding object.
<div ng-app="main">
<div ng-controller="MyCtrl">
<button ng-click="add()" >Add</button>
<div id="container">
<div ng-repeat="test in tests>{{test.name}}</div>
</div>
</div>
</div>
$scope.tests = {};
$scope.add = function() {
var newTest = {name: 'Test Message'};
$scope.tests.push(newTest);
};