I have a problem. I need to know how to get grandparent's ID in AngularJS.
I need "{{parent}}" to become "grand-parent".
(it should be <div id="me-and-my-grand-parent">)
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
var pid = document.getElementsByClassName("i-am-a-child");
var pid = this.parentNode.id;
if (this.parentNode&&this.parentNode.id)
var pid=this.parentNode.id;
$scope.parent = var pid;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div id="grand-parent{{$index}}" ng-repeat="item in items">
<div>
<div>
<div id="me-and-my-{{parent}}" class="i-am-a-child">
</div>
</div>
</div>
</div>
</div>
My actual code
<li ng-repeat="project in projects" ng-class="{active: project.childToggle, '': !project.childToggle,hasChild: project.children.length > 0 }" ng-dblclick="childToggleCt(project)" id="project-{{$index}}">
<div class="project-overview">
<header class="clearfix flip-area">
<span ng-if="!project.inCart" class="status dropdown-button warning pull-left" id="id-{{ParentIdShow}}" data-intro="Status bar" data-position="right">Pending</span>
And for now JS was like tis :
$scope.ParentIdShow = function(obj)
{
alert(obj.target.parentNode.parentNode.parentNode.id);
}
The answer to these types of "parent of my parent of my parent of my..." is to use the controller as syntax. Read more about it here. In short, it lets you do stuff like
<div ng-controller="ctrl1 as first">
<div ng-controller="ctrl2 as second">
...
<div ng-controller="ctrlN as Nth">
<div ng-repeat="i in arr">
{{first.property}}
{{second.otherProperty}}
{{Nth.nProperty}}
Note how you dont need any parent calls.
Related
A quick explanation: this is a simple app meant to recreate Reddit, so each comment on a post (which I'm calling a "node") contains an array of comments (nodeList), and each comment can have any number of nodes in this list. What I want to do is push an added comment into the current ng-repeat object's "nodeList", so that I can add the comment without refreshing.
This recursive template works, but only when I refresh the page, so it isn't being pushed into the current scope of the ng-repeat. I've done some research about $index, but because it's recursively generated there is no way of knowing how deep down the tree of nested arrays you could be.
What I tried was $scope.x.nodeList = [] and then pushing a new "node" into that, thinking that each instance of "x" is a new $scope within the ng-repeat, but this is not working.
Controller:
redditLite.controller('post', function($routeParams, $scope, $http) {
var postId = $routeParams.id;
var controller = this;
var postComment = {};
var node = {};
$scope.show = true;
$scope.addComment = {};
$scope.post = {};
$scope.x = {};
$scope.x.nodeList = [];
controller.comment = function(parentId) {
postComment.comment = $scope.addComment.body;
postComment.parentId = parentId;
postComment.rootPostId = $scope.post.id;
$http.post('add-comment',postComment, config).then(function(response) {
node = response.data;
$scope.x.nodeList.push(node);
});
}
});
HTML:
<script type="text/ng-template" id="postTree">
<div class="username">
{{x.username}}
</div>
<div class="body">
{{x.body}}
</div>
<div class="metrics">
<ul>
<li>
Likes: {{x.likes}}
<br>
<button class="like-button" ng-click="controller.likeNode(x.nodeId); x.likes = x.likes + 1">Like</button>
</li>
<li>
Comments: {{x.cmmnts}}
<br>
<button class="comment-button" ng-click="show = !show" ng-show="show">Comment!</button>
<div class="comment-field" ng-hide="show">
<input type="text" placeholder="Enter a comment." ng-model="addComment.body">
<br>
<button ng-click="controller.comment(x.nodeId); show = !show">Submit</button>
<br>
<button ng-click="show = !show">Cancel</button>
</div>
</li>
<li>
Date: {{x.submit_date}}
</li>
</ul>
</div>
<div class="nodes">
<ul ng-if="x.nodeList" ng-model="x.nodeList">
<li ng-repeat="x in x.nodeList" ng-include="'postTree'"></li>
</ul>
</div>
</script>
<div class="post">
<div class="username">
{{post.username}}
</div>
<div class="body">
{{post.body}}
</div>
<div class="metrics">
<ul>
<li>
Likes: {{post.likes}}
<br>
<button class="like-button" ng-click="controller.like(post.id); post.likes = post.likes + 1">Like</button>
</li>
<li>
Comments: {{post.cmmnts}}
<br>
<button class="comment-button" ng-click="show = !show" ng-show="show">Comment!</button>
<div class="comment-field" ng-hide="show">
<input type="text" placeholder="Enter a comment." ng-model="addComment.body">
<br>
<button ng-click="controller.comment(post.id); show = !show">Submit</button>
<br>
<button ng-click="show = !show">Cancel</button>
</div>
</li>
<li>
Date: {{post.submit_date}}
</li>
</ul>
</div>
</div>
<ul class="master-list">
<li ng-repeat="x in post.nodeList" ng-include="'postTree'"></li>
</ul>
There is some missing logic that needs to be handled, but for now I'm trying to get this working to push an object into an array no matter how deeply nested that array is within other objects.
Edit: I've included an image of the JSON structure as a referencece, and it can be seen that each node object can contain an array of node objects, so this is the array that I am attempting to push a new node into.
I've done recursive templating like this successfully several times. If I recall, having the ng-repeat and ng-include on the same html tag is problematic. Try wrapping the contents of your script in a div, remove the ng-include attribute from your li elements, and instead add an ng-include element inside of your li elements. Also, you have basically added your template twice, once for the root item, then again to add recursive behavior. You should be able to only have 1 template (the script one) if you wrap your post variable inside of an array in your markup.
<script type="text/ng-template" id="postTree">
<div>
...
<div class="nodes">
<ul ng-if="x.nodeList" ng-model="x.nodeList">
<li ng-repeat="x in x.nodeList">
<ng-include src="'postTree'"></ng-include>
</li>
</ul>
</div>
</div>
</script>
<ul class="master-list">
<li ng-repeat="x in [ post ]">
<ng-include src="'postTree'"></ng-include>
</li>
</ul>
Update:
When comment is clicked, send the parent to the function instead of just the parent id. This way, you can push the child onto it's parent after the $http post completes.
JS:
angular
.module('redditLite', [])
.controller('post', function($scope, $http) {
var controller = this;
$scope.show = true;
$scope.post = {
id: 1,
parentId: null,
username: 'jeff',
body: 'my fantastic comment',
likes: 152,
submit_date: new Date(),
nodeList: []
};
$scope.comment = function(parent) {
var postComment = {
id: 2,
parentId: parent.id,
username: 'jeff',
body: parent.addComment,
likes: 0,
submit_date: new Date(),
nodeList: []
};
console.log('adding comment', postComment, parent)
$http.post('add-comment',postComment, config).then(function(response) {
var node = response.data;
parent.nodeList.push(node);
});
}
});
HTML
<script type="text/ng-template" id="postTree">
<div>
<div class="username">
{{x.username}}
</div>
<div class="body">
{{x.body}}
</div>
<div class="metrics">
<ul>
<li>
Likes: {{x.likes}}
<br>
<button class="like-button" ng-click="controller.likeNode(x.nodeId); x.likes = x.likes + 1">Like</button>
</li>
<li>
Comments: {{x.nodeList.length}}
<br>
<button class="comment-button" ng-click="show = !show" ng-show="show">Comment!</button>
<div class="comment-field" ng-hide="show">
<input type="text" placeholder="Enter a comment." ng-model="x.addComment">
<br>
<button ng-click="comment(x); show = !show">Submit</button>
<br>
<button ng-click="show = !show">Cancel</button>
</div>
</li>
<li>
Date: {{x.submit_date}}
</li>
</ul>
</div>
<div class="nodes">
<ul ng-if="x.nodeList" ng-model="x.nodeList">
<li ng-repeat="x in x.nodeList">
<ng-include src="'postTree'"></ng-include>
</li>
</ul>
</div>
</div>
</script>
<ul class="master-list">
<li ng-init="x = post">
<div ng-include="'postTree'"></div>
</li>
</ul>
Plunker:
https://plnkr.co/edit/X40JoHduKYy12QPuLBCo?p=preview
You might be getting hurt by lexical scope. Basically:
$http.post('add-comment',postComment, config).then(function(response) {
node = response.data;
$scope.x.nodeList.push(node); //this $scope is not actually your $scope
});
Try doing:
var vm = this;
$http.post('add-comment',postComment, config).then(function(response) {
node = response.data;
vm.$scope.x.nodeList.push(node); //should be the right scope.
})
If this doesn't work, try double checking your bindings. A value not updating normally means the binding is not correct.
Try using $scope.x.nodeList.concat([node]); instead of pushing the value, like this avoid mutations in the object.
I m new in Anguar js .
I have created a controller and pass the data but my controller not working can u please help me .
My code is this
Angular code is
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.person=[
{name:"Raj", gender:"M"},
{name: "raja", gender:"M"},
{name:"sevitra" gender:"F"}
]
});
HTML
Code is
<body ng-app="myApp">
<div controller="myController">
<a href="javascript:void()">
<button>Add New Field</button>
</a>
<div class="advance-menu-wraper">
<ul>
<li>
{{"person[0].name"}} + {{"person[0].gender"}}
<div class="head-text">Field 1:</div>
<div class="description-text">
How many staff members are proficient in Oracla programing
</div>
</li>
<li>
<div class="head-text">Field 2:</div>
<div class="description-text">
<form name="addForm">
<textarea rows="2"></textarea>
<div class="send-btn">
<button>
<i class="fa fa-check">Submit</i>
</button>
</div>
</form>
</div>
</li>
</ul>
</div>
</div>
</body>
Demo link
Your expression won't work:
{{"person[0].name"}} + {{"person[0].gender"}}
yields: "{{"person[0].name"}} + {{"person[0].gender"}}" in your html.
The correct expression would be:
{{person[0].name + person[0].gender}}
Moreover you have an syntax error in your array. The last object misses a comma.
This is a working plunkr: http://plnkr.co/edit/R9ojp8TWd7AloRrlPlZh?p=preview
You need to use the ngController directive
change
<div controller="myController">
to
<div ng-controller="myController">
{name:"sevitra" gender:"F"} should be {name:"sevitra", gender:"F"}
controller="myController" should be ng-controller="myController"
{{"person[0].name"}} + {{"person[0].gender"}} should be {{person[0].name}} + {{person[0].gender}}
three things which need to be change that i can see
change the controller to
app.controller('myController', [ '$scope',function($scope) {
change the <div controller="MyController"> to <div ng-controller="MyController"
and in the {{ " Person[0].Name "}} and {{ " Person[0].gender "}} remove the quote marks so it becomes {{Person[0].Name}} and {{Person[]0.gender}}
I got the following code:
<div ng-repeat="i in placeholders" square class="set-holder {{i.class}}" droppable="{{i.type}}"></div>
How I make the first item has the directive bigsquare, while the others have just square.
I've tried:
<div ng-repeat="i in placeholders" {{= $first ? 'big' : ''}}square class="set-holder {{i.class}}" droppable="{{i.type}}"></div>
but sadly I the result is:
<div ng-repeat="i in placeholders" {{= $first ? 'big' : ''}}square class="set-holder test" droppable="3"></div>
a.k.a. the binding don't get compiled.
You can use ng-repeat-start and ng-repeat-end as follows:
angular.module('example', [])
.controller('ctrl', function Ctrl($scope) {
$scope.items = [1, 2, 3];
})
.directive('big', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.css('font-size', '30px');
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="example" ng-controller="ctrl">
<div ng-repeat-start="item in items" ng-if="$first" big>
big item {{item}}
</div>
<div ng-repeat-end ng-if="!$first">
item {{item}}
</div>
</div>
The documentation can be found under ng-repeat.
See this fiddle http://jsfiddle.net/nicolasmoise/xLfmK/2/.
You can create one directive to which you pass a condition. Depending on that condition it will either display the square or the big-square as such.
<div ng-repeat="repeat in repeater" condition="$first" square></div>
Note
If you don't want to alter the directives you're already made, you can always have square be a master directive that calls the other two.
If you don't mind using another <div> inside of your <li>, you should be able to get away with doing conditional blocks of <div> using ng-if="$index == ??".
Maybe something like this:
<div ng-repeat="i in placeholders">
<div bigsquare class="set-holder {{i.class}}" droppable="{{i.type}}" ng-if="$index == 0">
...
</div>
<div mediumsquare class="set-holder {{i.class}}" droppable="{{i.type}}" ng-if="$index == 1">
...
</div>
<div square class="set-holder {{i.class}}" droppable="{{i.type}}" ng-if="$index > 1">
...
</div>
</div>
It's a little more verbose, but it nicely separates out the templates so that you can have them pretty independent of each other.
<!-- Here is a code sample which I used in one of my angularjs ionic apps. -->
<!-- Regular ng-repeat -->
<!-- ng-if="$first" to determine <input focus-input> or not -->
<ion-item class="item item-input item-stacked-label" ng-repeat="input in template.inputs">
<label class="input-label bh-dark" for="{{input.id}}">{{input.title}}</label>
<div class="clearfix">
<div class="bh-left">
<input ng-if="$first" focus-input id="{{input.id}}" type="{{input.type}}" placeholder="{{input.choices[0]}}" ng-model="input.answer">
<input ng-if="!$first" id="{{input.id}}" type="{{input.type}}" placeholder="{{input.choices[0]}}" ng-model="input.answer">
</div>
<div class="bh-right">
<i class="icon ion-ios-close-empty bh-icon-clear" ng-click="clearField(input.id)" ng-show="input.answer"></i>
</div>
</div>
</ion-item>
I'm having problems avoiding dupes with my code. Here is a simplified example. I know the problem is in the array object being a reference of the same scope variable, but what is the best way to avoid it?
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type="text" ng-model="item" />
<div ng-repeat="item in items">
{{ item }}
</div>
<button ng-click="save()">Save</button>
</div>
</div>
<script>
angular.module('myApp', []).
controller('myCtrl', function ($scope) {
$scope.items = [];
$scope.save = function() {
$scope.items.push($scope.item);
}
});
</script>
Here is a fiddle that demonstrates the problem:
http://jsfiddle.net/u8Fuk/8/
Use track by syntax to fix this problem.
<div ng-repeat="item in items track by $index">
{{ item }}
</div>
Here is a fiddle.
Depends on what your goal is.
If you want to allow for duplicate values you need to change the code a bit as each item in the ngRepeat has to have a unique id. See the track by section here.
That would work like this:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type="text" ng-model="item" />
<div ng-repeat="item in items">
{{ item.value }}
</div>
<button ng-click="save()">Save</button>
</div>
</div>
<script>
angular.module('myApp', []).
controller('myCtrl', function ($scope) {
$scope.items = [];
$scope.save = function() {
$scope.items.push({value:$scope.item});
}
});
</script>
See the updated fiddle here.
If you don't want to allow for the same values you need to search for it.
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type="text" ng-model="item" />
<div ng-repeat="item in items">
{{ item }}
</div>
<button ng-click="save()">Save</button>
</div>
</div>
<script>
angular.module('myApp', []).
controller('myCtrl', function ($scope) {
$scope.items = [];
$scope.save = function() {
var found = $scope.items.reduce(function(previous, i){
if ($scope.item === i) return true;
return previous;
}, false);
if (found){
alert('duplicate value');
}
else{
$scope.items.push($scope.item);
}
}
});
</script>
See the updated fiddle here.
I'm trying to paginate my blogroll using AngularJS and the MEAN Stack. I have my Articles (blog posts) controller set up, and I've list.html to display everything. I can either get all the blogs to display, or I can get the pagination to increment, but I can't get the blogroll to show 5 items at a time and increment with the pagination.
angular.module('mean.articles')
.controller('ArticlesController', ['$scope', '$routeParams', '$location', 'Global', 'Articles', function ($scope, $routeParams, $location, Global, Articles) {
$scope.global = Global;
$scope.currentPage = 0;
$scope.pageSize = 5;
//I've my create, update and delete methods here.
$scope.find = function() {
Articles.query(function(articles) {
$scope.articles = articles;
});
};
$scope.numPages = function() {
return Math.ceil($scope.Articles.length / $scope.pageSize);
};
And here's my list.html:
<div class="container" style="width:900px; margin:auto">
<div class="container" style="width:600px; float:left">
<section data-ng-controller="ArticlesController" data-ng-init="find()">
<ul class="articles unstyled" style="width:600px">
<li data-ng-repeat="article in articles | filter:search | limitTo:pageSize">
<h2><a data-ng-href="#!/articles/{{article._id}}">{{article.title}}</a></h2>
<div>{{article.content}}</div>
<div>{{article.tags}}</div>
<h4><small>
<div class="well well-small text-center" style="width:300px">
<div style="margin:auto">
<span>{{article.created | date:'medium'}}</span> /
<span>{{article.user.name}}</span>
</div>
</div>
</small></h6>
</li>
</ul>
<h1 data-ng-hide="!articles || articles.length">No articles yet. <br> Why don't you Create One?</h1>
<button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">
Previous
</button>
{{currentPage+1}}/{{numPages()}}
<button ng-disabled="currentPage >= numPages()-1" ng-click="currentPage=currentPage+1">
Next
</button>
</section>
</div>
<div class="container" style="width:300px; float:left">
<input type="text" ng-model="search" placeholder="Enter your search terms" />
</div>
http://jsfiddle.net/2ZzZB/56/ from Pagination on a list using ng-repeat. It looks like the main thing you're missing is a way to indicate where to start your ng-repeat. The fiddle I linked to solves it by creating a startFrom filter.