Dynamically access an array using ng-repeat - javascript

I need to access different arrays based on the users choice and then run through the array with a ng-repeat.
Controller:
$scope.allbooks={
book1:{price:"3.00",type:"non-fiction",chapters:book1chapters},
book2:{price:"4.00",type:"fiction",chapters:book2chapters},
};
$scope.pick = function(selectedBook) {
$rootScope.choice = selectedBook;
}
$scope.book1chapters=[
{title:"it begins"},
{title:"another one"}
];
$scope.book2chapters=[
{title:"hello"},
{title:"calling from the otherside"}
];
HTML:
<button ng-click="pick(allbooks.book1)">Book 1</button>
<button ng-click="pick(allbooks.book2)">Book 2</button>
<div ng-repeat:"m in choice.chapters"><-----this does not work
Chapter: {{m.title}}
</div>
This is a very simplified example just to make it easier to look at :) I don't know how t reference another array from inside an array. Thanks

It seems you did not define book1chapters and book1chapters for collection allbooks, instead you defined them in $scope which is not correct. Also change $rootScope to $scope since rootScope is not injected. The following code is working:
var book1chapters = [{
title: "it begins"
}, {
title: "another one"
}];
var book2chapters = [{
title: "hello"
}, {
title: "calling from the otherside"
}];
$scope.allbooks = {
book1: {
price: "3.00",
type: "non-fiction",
chapters: book1chapters
},
book2: {
price: "4.00",
type: "fiction",
chapters: book2chapters
},
};
$scope.pick = function(selectedBook) {
$scope.choice = selectedBook;
}
The code on plunker: http://plnkr.co/edit/83Ujp4R8BjIe39ROmp6n?p=preview

Short Answer
Basically you have incorrect ng-repeat syntax. It should have = instead of : before writing expression in front of ng-repeat directive like we do for value attribute
Markup
<div ng-repeat="m in choice.chapters"><-----this does not work
Chapter: {{m.title}}
</div>
Suggestions
You should not pollute $rootScope for sharing variables. For that you could create a shareable service which can share a data among-est various components of your app like controllers, directives, service, etc.
HTML
<button ng-click="sharableData.choice = 'book1'">Book 1</button>
<button ng-click="sharableData.choice = 'book2'">Book 2</button>
<div ng-repeat = "m in allbooks[sharableData.choice].chapters">
Chapter: {{m.title}}
</div>
Service
app.service('sharableData', function(){
var sharableData = this;
sharableData.sharedData = {
choice: undefined
};
});
Controller
app.controller('myCtrl', function($scope, sharableData){
//you other controller code
//add this additional line to expose service variable on html
$scope.sharableData = sharableData;
});

Related

Select DOM elements within template

There's this template that I call multiple times on the same page:
<div ng-controller="templateController">
<div class="source">
<div ng-repeat="item in info">
<div class="content" data-value="{{item.ID}}">{{item.name}}</div>
</div>
</div>
<br style="clear:both" />
<div class="receiver"></div>
</div>
and then I want to select all the elements with class="content" within each template scope in order to manipulate them.
How can I achieve this using JS?
EDIT :
Plunker
In this planker the console.log should print "1" twice and its printing "1" and then "2" when the template loads the second time
After more explanation here is a working example:
https://plnkr.co/edit/n5GOd6MDLyvG4ZAsuLvf?p=preview
The main idea is creating 2 lists and iterating over both and just moving data around between them on click.
angular.module("demo", []);
angular
.module("demo")
.controller("demoController", ["$scope", function($scope) {
}]);
angular
.module("demo")
.controller("templateController", ["$scope", "$timeout", function($scope, $timeout) {
$scope.sourceList = [
{
name: "john",
ID: 1
},
{
name: "Edward",
ID: 0
},
{
name: "Carl",
ID: 2
}
];
$scope.receiverList = [
{
name: "Bob",
ID: 1
}
];
$scope.moveToReceiver = function(item){
$scope.receiverList.push(item);
$.each($scope.sourceList, function(i){
if($scope.sourceList[i].name == item.name){
$scope.sourceList.splice(i, 1);
return false;
}
});
}
}]);
Most of the time you do not want to do DOM manipulation in Angularjs and instead hook into events with your controller. If you have to do DOM manipulation in AngularJS you would use directives
Docs on Creating a Directive that Manipulates the DOM
You could then use your link function to grab the children of your directive's element
function link(scope, element, attrs) {
var content = angular.element(element[0].querySelector('.content'));
}
https://stackoverflow.com/a/17329630/2033671

How to filter json using angular filter

Hey i have little problem with angular filter.
I import post from wordpress, and i would really like to filter them by tags, like, now show only post with tag = ENG, or show only post with tag = GER.
This is how my html looks like
<div ng-controller="ThreeMainPosts">
<div ng-repeat="threePost in threePosts | filter : {data.data.posts[0].tags[0].slug = 'ENG'} ">
<div three-post="threePost">
<h1>CONTENT</h1></div>
</div>
</div>
Controller
myModule.controller('ThreeMainPosts', function($scope, $location, WordPressNewsService) {
var postsPerPage = 3;
var orderBy = 0;
WordPressNewsService.getPosts(postsPerPage, orderBy).then(function(data) {
$scope.threePosts = data.data.posts;
});
});
and the json
{ id: 312, type: "post", slug: "la-bamba-3", url: "sada2", … }
attachments:[]
author:{ id: 1, slug: "ds", name: "hnews", first_name: "", last_name: "", … }
categories:[{ id: 6, slug: "ludzie", title: "Ludzie", description: "", parent: 0, post_count: 2 }]
tags: [{ id: 32, slug: "en", title: "EN", description: "EN", post_count: 1 }]
url:"http://xxx0.co,/?p=312"
previous_url:"http://hirsch-news.iterative.pl/?p=306"
status:"ok"
I tried make the filter in the controller but i can only do this for single element, for example:
if(data.data.posts[0].tags[0].slug == "ENG"){
$scope.threePosts = data.data.posts;
}
Any ideas guys?
Have nice day! :)
Made a quick filter, can change it up to your needs. Hope it helps. Angular $filter Docs
Here's a Codepen with angular's built in $filter.
myModule
.controller('ThreeMainPosts', function($scope, $location, WordPressNewsService, $filter) {
var postsPerPage = 3;
var orderBy = 0;
var tagToSortBy = 'EN'
WordPressNewsService.getPosts(postsPerPage, orderBy).then(function(data) {
$scope.threePosts = $filter('postFilter')(data.data.posts, tagToSortBy);
});
})
.filter('postFilter', function() {
return function(post, tag) {
if (post.tags.slug === tag) {
return post;
}
};
});
If you wanted to do this in the template it would be like this.
<div ng-controller="ThreeMainPosts">
<div ng-repeat="post in threePosts | postFilter : 'ENG' ">
<div three-post="threePost">
<h1>CONTENT</h1>
</div>
</div>
</div>
I think you want the Array.filter function, in combination with Array.some
var postsWithTagSlugENG = data.data.posts.filter(function (post) {
return post.tags.some(function (tag) {
return tag.slug == "ENG"
});
});
Here is another approach using the filter provided by Angular:
In javascript:
$filter('filter')(data.data.posts, tagToSortBy);
In HTML:
<input type="text" name="searchTag" placeholder="Filter by Any Property" ng-model="search.$" />
<input type="text" name="searchTag" placeholder="Filter by Tag" ng-model="search.tag" />
<div ng-repeat="post in threePosts | filter : search ">
The difference between this answer and #alphapilgrim is that you don't have to create your own logic to handle the filtering. Angular provides a base filter that works well in many, if not most situations.
Here are the docs if you would like to learn more about it. It is pretty powerful if you dig deep.
https://docs.angularjs.org/api/ng/filter/filter

Angularjs: Dynamically load directives in view with data

I am trying to make a dynamic framework with angularjs. I load sections with a webapi, that have data about the directive that it uses and the data that should be used in that directive. The data that I send can look like this:
[
{
id: "section1",
directive: "<my-directive></my-directive>",
content: {
title: "This is a title",
text: "This is a text"
}
},
{
id: "section2",
directive: "<my-other></my-other>",
content: {
title: "This is another title",
list: ["This is a text", "This is another text"]
}
}
]
When I load this data, I convert the directives in to element with $compile.
angular.forEach($sections, (value, key):void => {
value.directive = $compile(value.directive)($scope);
}
So I can actually load this data in the view, like this:
<div ng-repeat="section in sections">
{{section.directive}}
</div>
First of all, this doesn't show up in my view, so how do I fix this?
Then the second issue I have. When I actually get this directive loaded into the view, how will I be able to access the data that should be used in this directive? I do have an id added to the sections.This is what I tried:
angular.forEach($sections, (value, key):void => {
value.directive = $compile(value.directive)($scope);
var parent = angular.element('#sectionsparent'); //The parent element has this id
parent.append(value.directive);
}
This way the section elements show up, but I cannot access the data that should be loaded inside the directive.
Thank you for your help in advance, let me know if you need more information.
EDIT:
When the directive is eventually loaded, I want to be able to access the data that belongs to that section. So if we take first section in the sample data, I want to be able to do the following in the template of the directive:
<!-- This file is myDirectiveTemplate.hmtl -->
<div id="{{id}}>
<h1>{{title}}</h1>
<p>{{text}}</p>
</div>
I don't care if I have to access these properties through a viewmodel object, so it would be {{vm.id}} instead of {{id}}. But I prefer to not have any function calls inside my template to actually get data.
Alright. There may be another way to accomplish this, or perhaps using includes instead of directives, but here's one way at least.
Taking your example code, you can follow your second route with $compile and append but you also need to pass an html-attribute for the isolate scope's content and bind it with a new $scope with the section added. (You also need to wrap in a $timeout so querying the DOM happens after it's initially rendered).
var app = angular.module('app', []);
app.controller('AppCtrl', function($scope, $compile, $timeout) {
$scope.sections = [
{
id: "section1",
directive: "my-directive",
content: {
title: "This is a title",
text: "This is a text"
}
},
{
id: "section2",
directive: "my-other",
content: {
title: "This is another title",
list: ["This is a text", "This is another text"]
}
}
];
// Need to timeout so rendering occurs and we can query the DOM.
$timeout(() => {
angular.forEach($scope.sections, (section) => {
let newScope = $scope.$new();
newScope.content = section.content;
let dir = section.directive;
let compiled = $compile(`<${dir} content="content"></${dir}>`)(newScope);
let parent = angular.element(document.querySelector('#' + section.id));
parent.append(compiled);
});
});
});
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {content: '='},
template: `<div>
<h1>{{content.title}}</h1>
<p>{{content.text}}</p>
</div>`,
};
});
app.directive('myOther', function() {
return {
restrict: 'E',
scope: {content: '='},
template: `<div>
<h1>{{content.title}}</h1>
<ul>
<li ng-repeat="item in content.list">{{item}}</li>
</ul>
</div>`,
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="AppCtrl">
<div ng-repeat="section in sections" id="{{section.id}}"></div>
</div>

Show different buttons matching array values

So here's my problem, i'm using AngularJS and i'm getting JSON from PHP, and displaying all my data with ng-repeat. I already have done this.
Now I want to check if some data is in "Array1" and if it is, change the correspndent data from the ng-repeat. I know it sounds really weird, but let me put an example with code:
Here's array1 values
{
"23",
"48",
"51"
}
So when i get the data, it's something like this:
{
id : "23",
name: "example"
}
And for every JSON object i'm using ng-repeat to display them all like this:
<div ng-model="data.posts" ng-repeat="post in posts | orderBy:'-' | unique: 'id'">
...
<button>This button will show if "id" matches</button>
<button>This button will show if "id" not matches</button>
</div>
I want to compare if an id of array1 matches an id from the JSON data and if it matches show one button and if not show other.
I'm on this like 2 weeks, and i can't get the problem solved, and i don't see any way to get it.
Thx for reading at least and sorry for my bad english!
Your array1 should be an array and can add a function in controller to check match id.
in controller:
$scope.array1 = ["23","48","51"];
$scope.checkInArray1 = function(id) {
var index = $scope.array1.indexOf(id);
if(index < 0){
return false;
} else {
return true;
}
};
and in your html:
<button ng-if="checkInArray1(post.id)">This button will show if "id" matches</button><br>
<button ng-if="!checkInArray1(post.id)">This button will show if "id" not matches</button>
Making the assumption that {"23","48","51"} should be a array ["23","48","51"]
You could do something like this:
Working Fiddle: http://jsfiddle.net/ravenous52/rgyom4yd/
myApp.controller('MyCtrl', ['$scope',
function($scope) {
$scope.knownIds = ["23", "48", "51"];
$scope.data = {
posts: [{
id: "23",
name: "example23"
}, {
id: "51",
name: "example51"
}, {
id: "99",
name: "example99"
}]
}
}
]);
<section ng-controller="MyCtrl">
<div ng-repeat="post in data.posts">
<button ng-show="knownIds.indexOf(post.id) >-1">This button will show if "id" matches</button>
<button ng-hide="knownIds.indexOf(post.id) >-1">This button will show if "id" not matches</button>
</div>
</section>
https://jsfiddle.net/alair016/4wc44on1/
<div ng-app='myApp' ng-controller="MyCtrl">
<div ng-model="data.posts" ng-repeat="post in data.posts">
<button ng-if="array1.indexOf(post.id) >-1">{{post.name}} id matches</button>
<button ng-if="array1.indexOf(post.id) == -1">{{post.name}} id does not match</button>
</div>
</div>
var myApp = angular.module('myApp',[])
.controller('MyCtrl', ['$scope', function($scope) {
$scope.array1 = ["23","48","51"];
$scope.data = {
posts : [
{
id : "23",
name: "example"
},
{
id: "24",
name:"second example"
}
]
};
}]);

updating text on view from ng-repeat

I have problem with updating view by using ng-repeat.
When click on text, it doesnt update but it overwrites below. (I want have panel with names(links) and show its description on view)
I have searched everything and couldnt find answer or something useful what would help me.
html:
<body>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<ul>
<li><a ng-repeat="item in items" ng-click="getInfo(item)" > {{item.name}} <br> </a></li>
</ul>
</div>
<hr>
<div ng-controller="myInfo">
<div ng-repeat="info in item" >
<h3>Name: {{info.name}}</h3>
<p> ID: {{info._id}}</p>
<p> temp: {{info.temp}} </p>
</div>
</div>
</div>
</body>
js
var app = angular.module('myApp', [])
app.controller('myCtrl', function($scope, $http, shareDataService) {
$http.jsonp('data.json').success(function(data) {
$scope.items = data;
});
$scope.getInfo = function(item) {
shareDataService.addItem(item);
}
});
app.controller('myInfo', function( $scope, shareDataService ){
$scope.item = shareDataService.getItem();
});
app.service('shareDataService', function() {
var myItem = [];
var addItem = function(newObj) {
myItem.push(newObj);
}
var getItem = function(){
return myItem;
}
return {
addItem: addItem,
getItem: getItem
};
});
json
angular.callbacks._0([
{
"_id": 1,
"temp": "asdgdf",
"name": "name1"
},
{
"_id": 2,
"temp": "asdasdasd",
"name": "name2"
},
{
"_id": 3,
"temp": "asasdasdadgdf",
"name": "name3"
}
]);
Plunker: http://plnkr.co/edit/X65oH0yAkRnN8npKnFY2?p=preview
You have an error in your console. Just add track by to your ng-repeat:
<div ng-repeat="info in item track by $index">
ng-repeat needs a unique id to track the items in order to be able to update them. If you add the same item twice, ng-repeat sees the same item twice, ans loses its mind. Using $index (which is unique) resolves that issue.
Keep in mind that using $index is adequate here, but it's preferred to use a unique id from the object if you can.
EDIT:
If your issue is that you want to see only the one element you clicked on in your view, then the issue is that you are adding your item to an array, when you should just be setting a value in your service. And, obviously, no need of a ng-repeat in your view.
http://plnkr.co/edit/Wfg9KhCWKMDreTFtirhR?p=preview
JS:
app.controller('myCtrl', function($scope, $http, shareDataService) {
//[...]
$scope.getInfo = function(item) {
shareDataService.setItem(item);
}
});
app.controller('myInfo', function( $scope, shareDataService ){
$scope.$watch(function () {
return shareDataService.getItem();
}, function (value) {
$scope.info = value;
})
});
app.service('shareDataService', function() {
var myItem;
return {
setItem: function(newObj) {
myItem = newObj;
},
getItem: function(){
return myItem;
}
};
});
HTML:
<div ng-controller="myInfo" ng-show="info">
<h3>Name: {{info.name}}</h3>
<p> ID: {{info._id}}</p>
<p> temp: {{info.temp}} </p>
</div>
If you only want to display information of the item that you just have clicked, then we don't need the second ng-repeat (as jlowcs said).
We also don't have to defined myItem as a array, it's just unnecessary, I think.
Edit:
how embarrassing i am, my answer look exactly to same as jlowcs's. I guess I took to much time to figure out the answer.
One thing to add up:
Why do I need a $watch in myInfo controller?
Because at the first time, we use ng-repeat, this component do the watch part for us. Then when I remove ng-repeat, I need to watch for data changing by myself.

Categories