AngularJS (ng-repeat directive) with Meteor - angularjs

I have a question regarding the directive ng-repeat. Is it possible to perform manipulation to each item in a collection?
<div ng-repeat = "item in items">
</div>
Is there any possible way to call a Meteor method to perform some sort of manipulation to each item?
I'll be more specific. What I'm doing is using the meteor twitter accounts and every time a user logs in, it stored in a database.
index.ng.html :
<div ng-controller = "TweetsCtrl">
<div ng-repeat = "t in twitterAccs">
<img src = "{{t.services.twitter.profile_image_url_https}}">
<br>
<i>#{{t.services.twitter.screenName}}</i>
<br>
<br>
</div>
</div>
JS file :
if (Meteor.isClient) {
angular.module('twitter-example',['angular-meteor']);
angular.module('twitter-example').controller('TweetsCtrl', ['$scope','$meteor', function($scope,$meteor) {
$scope.twitterAccs = $meteor.collection(Meteor.users);
}]);
}
Right now, its able to get the image and the twitter handle. What I want to do is extract the number of followers and friends but that requires OAUTH from Twitter. Since those require special keys from Twitter, I know I have to put that information in the if (Meteor.isServer) scope.

Related

Page render speed in AngularJS using ng-include

I've got an issue where I populate a page in pieces. There are an arbitrary number of categories with an arbitrary number of items. The code is generally something like the below (warning, transposed).
$scope.getItems = function(key) {
$http.get('get-items?key=' + key)
.then(function(res) {
for (let item of res.data) {
$scope.categories[item.category].items.push(item);
}
});
}
let populateCategories = function() {
for (let key in $scope.categories) {
$scope.getItems(key);
}
}
$scope.getCategories = function(next) {
$http.get('get-categories')
.then(function(res) {
$scope.categories = res.data;
next();
});
$scope.getCategories(populateCategories);
}
The idea is to first get what categories will be on the page, and render them, empty (but w/ a busy icon). After that, hit and endpoint one time per category and populate w/ the results. The busy icon is shown via ng-show & a boolean pointing to the size of the items. 1 or more items = no busy icon, an the items should show.
The loading of the categories more or less works. Populating them though, is not so free flowing. Watching the console output, it takes ages for the browser to render. The busy icon goes away somewhat quickly, but I don't see the items until a bunch of them are ready.
Worth noting, (I think) I saw this problem appear when I moved the html that displays each item from a single file, an template and used ng-include, as I'm using it on two different places. Surely that would not be a cause would it?
EDIT: Adding the html - simplified
item-template.html
<div class="row">
<div class="col-xs-2 col">
<img src="{{item.img}}">
</div>
<div class="col-xs-10 col">
<div>{{item.details}}</div>
</div>
</div>
list.html
<body>
<div class ="container-fluid">
<div class="row">
<div ng-repeat="(key, value) in categories">
<div>{{key}}</div>
<div ng-show="value.busy"">
<img ng-src="{{busy_image}}">
</div>
<div ng-repeat="item in value.items track by $index">
<!-- This in fact seems to be the culprit -->
<div ng-include="item-template.html">
</div>
</div>
</div>
</div>
</body>
So, playing around, if I simply paste the contents of template.html into list.html, the response is much, much better. Looking at this issue, the solution seems to be to use a cache service. I'm happy to use something like that but I'm still curious as to why. The template I'm using isn't small (166 lines) but I can't imagine it being that heavy either on a modern computer.
Several things from the top of head:
amount of items to be shown in the HTML. Large lists with x properties = alot of Angular watchers.
if there are alot of items, maybe check for an alternative to ng-repeat
instead of ng-include item-template.html, create a Component
ng-repeat with track by
use bind once
in this case you can replace ng-show with ng-if

How to handle conditional sub-category in AngularJS product gallery

Just starting out learning AngularJS and decided to mock up a basic product gallery using what I've learned so far and I've hit a roadblock. Currently I have a simple product gallery with 3 templates(category listing, products in category listing and product overview). What I would like to do is set up some sort of conditional, where if the products in a selected category have a sub-category, it displays a list of sub-categories using the category-list template. If they don't have a sub-category it just goes straight to the product-list template.
I have created this Plunker showing where I am at so far.
In the above example, if someone clicks on "Cars" I want it to then show a listing of sub-categories using the category-list template. So when you click "Cars" it would take you to a screen with 2 buttons: 4-door and 2-door. Clicking on one of those buttons would then show you the products from those sub-categories using the product-list template. However, if you were to click on "Trucks" from the initial screen, it would just take you directly to the product-list template since the trucks don't have sub-categories.
Here is my category-list template:
<section id="categories" ng-hide="productsVisible">
<div ng-repeat="product in vm.products" class="category">
<div ng-click="vm.selectCategory(product); showProducts();">
<button>{{product.category}}</button>
</div>
</div>
</section>
And here is my product-list template:
<section id="products" ng-show="productsVisible">
<div ng-repeat="product in vm.selectedCategory.items" class="product">
<a href ng-click="vm.selectProduct(product); showResults();">{{product.name}}</a>
</div>
</section>
See my updated plunker
Basically, you need to extend the selectCategory method by grouping the sub-categories and checking whether we're about to enter this sub-category in subsequent click. Like this:
vm.selectCategory = function(category) {
var subCats = [],
map = {};
if (category.items && !category.items[0].subCategory){
vm.selectedCategory = category;
vm.inSubCat = true;
return;
}
vm.inSubCat = !category.items;
if (category.items) category.items.forEach(function(e){
if (!map[e.subCategory]) subCats.push({category: e.subCategory, name: category.category});
map[e.subCategory] = true;
});
vm.products = subCats;
if (vm.inSubCat) vm.selectedCategory = {items: vm.data.filter(function(c){
return c.category == category.name;
})[0].items.filter(function(p){
return p.subCategory == category.category;
}) };
}
I would suggest your data model could use some work, and put all the products in a single array with categories and subcategories as properties. However, you can get what you want with this change to the products-list.html.
<div ng-show="vm.selectedCategory.category=='Cars'">
<input type="radio" ng-model="subcategory" value="2-Door">Coupe
<input type="radio" ng-model="subcategory" value="4-Door">Sedan
</div>
<section id="products" ng-show="productsVisible">
<div ng-repeat="product in vm.selectedCategory.items" class="product">
<a ng-show="product.subCategory===subcategory" href ng-click="vm.selectProduct(product); showResults();">{{product.name}}</a>
</div>
</section>
I advice you to refactor the code in two possible ways:
a) Try to remove lines from controller that control the view (the process of displaying different directives) and use events in directives
b) Control your view by using ng-show and ng-hide directives that will show or hide some part of your code.

Updating and deleting one record within object

I'm trying to create a system in which I can display, edit and delete information on players and teams using angular along with a RESTful API. I have the parts working in which I show all the data and post data to the database.
The part I am having trouble with is updating data as I can't manage to get the http put working with the correct data being sent.
HTML
<script type="text/ng-template" id="team-single.html">
<div class="team-box">
<div class="badge">
<img ng-src="images/{{x.club_name}}.png" width="100" height="100"></div>
<div ng-hide="editorEnabled">
<div class="team-name">{{x.club_name}}</div>
<p><b>Manager:</b> {{x.club_manager}}</p>
<p><b>Ground:</b> {{x.club_ground}}</p>
<p><b>Nickname:</b> {{x.club_nickname}}</p>
<div class="team-p">{{x.club_info}}</div>
Edit Team
</div>
<div ng-show="editorEnabled">
<p><input ng-model="x.club_name"></p>
<p><input ng-model="x.club_manager"></p>
<p><input ng-model="x.club_ground"></p>
<p> <input ng-model="x.club_nickname"></p>
<p><input ng-model="x.club_info"></p>
<input type="hidden" name="id" ng-value="" />
Save
</div>
</script>
<div class="row teams">
<div class="container">
<div class="col-md-4" ng-repeat="x in teams" ng-include="'team-single.html'"></div>
</div>
JS
var app = angular.module('footballApp', []);
app.controller("TeamCtrl", function ($scope, $http) {
$scope.updateTeam = function () {
$http({
method: 'PUT',
url: 'clubs.php/teams/' + id,
data: ??,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
};
});
I have enabled an editor on the front end to edit the fields. I don't know how to pass the one object being edited back into the updateTeam function while not passing the entire team array.
Also in the HTTP PUT, I have to use the id field of the relevant club in the URL but I'm not sure how to send this back.
Any help would be greatly appreciated.
To solve your problem you might need to rethink your UI. Why do you want to show edit option for all teams at once in the UI. Ideally you should show the team details along with an option to edit them.
When user click on edit call a function with team data and then show a form where those details can be edited and later can be send for submission.
Refer to this plnkr example https://plnkr.co/edit/muqnmIhO77atLyEHS9y7?p=preview
<div class="row">
<div class="col-xs-6 col-lg-4" ng-repeat="team in teams">
<h2>{{ team.club_name }}</h2>
<p>{{ team.club_info }}</p>
<p><a class="btn btn-default" ng-click="onEditDetails(team)" href="javascript:void(0);" role="button">Edit details ยป</a></p>
</div>
</div>
and then in controller
$scope.onEditDetails = function(team) {
$scope.team = team;
};
This will give you the reference of current selected team. You can use $scope.team then to show a form in UI which can be submitted along with its new edited data.
Note: In your example you are using a template to show HTML in UI but since they are in a ng-repeat each of your template will be using the last variable of loop. A template included using ng-include doesn't create a different scope for each of your team in teams.
If you want to create reusable HTML (though un-necessary as per your requirement) you can create a directive and include it in your ng-repeat as <my-team-directive data="x"></my-team-directive>

Get django template variables within ng-repeat

I have a list of items, each one with a checkbox. The user selects some of items, and then he may search for other items by typing a query, and then pressing "Search" button which do a POST.
I don't want to lose the previous selected items. So in my views, I get the checked items ids which are stored in the checkbxes values, and then re-send them to the template.
The problem is that I am using ng-repeat and not {% for %} to create the items' list because I am doing some filtering tasks with angularJS, so I am not able to know if the current item's id exists in the checked ids list which is passed to the template after the POST or not.
Views code:
if 'btn_search' in request.POST:
checked_ids = get_integers_from_checked_checkboxes(request, 'checkbox_parent')
search_query = request.POST['txtbox_search']
sub_queries = search_query.split(' ')
for sub_query in sub_queries:
items= all_items.filter(items__name__contains=sub_query)
return render_to_response('my_page.html',
{'items': items,
'checked_items' : checked_ids},
context_instance=RequestContext(request))
A simplified Template code:
<!-- Search -->
<input type="text" name="txtbox_search">
<button name="btn_search"><b>Search</b></button>
<!-- Items' list -->
<input type="text" ng-model="filter_Search_box" placeholder="Filter search">
<ul class="list">
<li ng-repeat="item in items | filter:filter_Search_box">
<!-- This check box should be checked if the item.id is in {{checked_items}} -->
<input type="checkbox" value="{/item.id/}" name="checkbox_item"/>
<b>{/item.name/} </b>
</li>
You should consider using ajax communication between your angular and django apps. So your django view would return json object which would be loaded dynamically by your angular app. Also you could achieve it in a dirty way:
views.py
from django.core import serializers
if 'btn_search' in request.POST:
checked_ids = get_integers_from_checked_checkboxes(request, 'checkbox_parent')
search_query = request.POST['txtbox_search']
sub_queries = search_query.split(' ')
for sub_query in sub_queries:
items = all_items.filter(items__name__contains=sub_query)
items = serializers.serialize('json', items)
return render_to_response('my_page.html',
{
'items': items,
'checked_items': checked_ids
},
context_instance=RequestContext(request))
file_.html:
<script>
window.items = eval('(' + '{{ items }}' + ')');
</script>
in your angular controller initialize:
$scope.ang_items = window.items;
Then you will be able to make ng-repeat out of ang_items. But I HIGHLY DON'T RECOMMEND THIS SOLUTION.
Also your for loop (for sub_query in sub_queries:) overwrite items with every iteration, that's probably not desirable thing. I would RECOMMEND getting familiar with this.

Angular load additional data in ng-repeat

I have an ng-repeat that has a bunch of ids and loops over them. I then want to load the data from an external source. So let's say I have an array
[1,2,3,4,5]
And I have an ng-repeat that loops over then shows
<div>1</div>
<div>2</div>
...
I want to load additional details from an external source (firebase) to get the details of the users profile so I can show first name and last name which is part of another section and then load them by calling
http://firebase/users/user_1
http://firebase/users/user_2
...
And then somehow map it to the dom or scope so I can then print it using normal angular stuff.
Any idea on how to do this best?
First thing I'd do is make that array usable both before and after the extra data is loaded. I'll assume your full user object has field id
$scope.users = [1,2,3,4].map(function(id) {
return {id: id};
});
This should let you use ng-repeat like this
<div ng-repeat="user in users">
{{user.id}}
</div>
Now, in your controller, load up the user details. I'll assume you have a User $resource or similar service
var userPromises = $scope.users.map(function(user) {
return User.get({id: 'user_' + user.id}).$promise;
});
$q.all(userPromises).then(function(users) {
$scope.users = users;
});
Then just change your template to use the extra details
<div ng-repeat="user in users">
{{user.id}}
<dl ng-if="user.$resolved">
<dt>First name</dt>
<dd>{{ user.firstName }}</dd>
<dt>Last name</dt>
<dd>{{ user.lastName }}</dt>
</dl>
</div>

Resources