Get django template variables within ng-repeat - angularjs

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.

Related

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.

Binding raw object output to a form representation

I'm trying to update models from a JSON representation of an object to a form. Here's a link to an example
To recreate my issue,
Change the data in the form (see that the JSON changes).
Change the JSON (See that the form doesn't change).
Here's my code:
JS
var ppl = {
createdby: "foo",
dateCreated: "bar",
}
angular.module('myApp', [])
.controller("Ctrl_List", function($scope) {
$scope.people = ppl
$scope.print = JSON.stringify($scope.ppl)
})
HTML
<div ng-app="myApp">
<div class="container" ng-controller="Ctrl_List">
<!-- FORM -->
<div class="row" ng-repeat="(key,val) in people track by $index">
<div class="col-md-12">
<label for="{{key}}">{{key}}</label>
<input class=form-control" id="{{key}}" ng-model="people[key]">
</div>
</div>
<!-- JSON -->
<div class="editable" contenteditable="true" ng-model="people">{{people}}</div>
</div>
</div>
When a user changes the JSON, the form should update in real-time.
Here's some things I have tried:
Change the JSON display element from div to input but it prints [Object][Object]
Also <input ng-model="JSON.stringify(people)"> but I get an "unbindable element" error.
Also tried adding a new model: $scope.print = JSON.stringify(people) but it shows nothing in the raw output.
Is it even possible to update a live object or am I gonna have to do some sort of event that triggers the form to change?
PS: Angular 1.5.8
There are several reasons why this doesn't work:
ng-model on a div doesn't do anything
even if it did, it would save a string to people, and your form would thus not work anymore.
You should use a textarea to make it work, and bind it to another variable, of type string. Using ng-change on the textarea, and on the inputs of the form, allows populating the people object by parsing the JSON string, and vice-verse, populating the JSON string from the people object.
See https://codepen.io/anon/pen/peexPG for a demo.
Refering to Contenteditable with ng-model doesn't work,
contenteditable tag will not work directly with angular's ng-model because the way contenteditable rerender the dom element on every change.

Angular.js ng-repeat unique identifier

I am using ng-repeat on a <tr> tag to populate the <td> tags with data pulled from mysql and converted into Json. This works just fine. However, one of the <td> tags that I'm using contains a button.
What I would like to do, is have each of these buttons identified somehow in the DOM, so that I can target then with specific requests.
Example: Page loads, ng-repeat repeats a button 4 times. Each of these buttons would have an ng-click attached to it. I want each of them to open and filter different information in a json file.
Am I correct in assuming that ng-repeat would simply open the same item for each button, and how would I go about making them seperate? thanks.
You can do something like this on the front-end:
<button ng-repeat="item in items track by $index" ng-click="someFunction($index)" >Something happens</button>
Then in your controller:
$scope.someFunction = function (index) {
if (index === 1):
// etc.
else...
// Or use switch, whichever works for you.
You could create the specific function on each item in the array.
<button ng-repeat="button in buttons" ng-click="button.functionName()">{{button.name}}</function>
There's $index for that. It's a very good habit to take for any of your ng-repeat. Also don't forget bind-once if your buttons UI isn't subject to modifications once the DOM has loaded.
<ul>
<li ng-repeat="page in pages">
<a ng-class="{ testClass: $index == pageNumber }" ng-click="setPageNumber($index)">{{ page }} - index is : {{$index}}</a>
</li>
</ul>
http://jsfiddle.net/bonatoc/4z1t4gsm/3/
Also you could do (using bind-once):
<button
ng-repeat="button in ::buttons track by $index"
id="button_{{$index}}"
class="{{button['css_class']}}"
...given your buttons were a JSON object as well (beware, ng-repeat likes arrays, not objects. Arrays of objects are fine):
$scope.buttons = [
0: {
'css_class': someClass,
'functionToTrigger': function(...
// ...

AngularJS (ng-repeat directive) with Meteor

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.

ng-repeat Circular Reference Screen Refresh

I'm a noob to AngularJS. As a learning exercise I am creating a typeahead control.
The typeahead is comprised of a text box for filtering the options, an unordered list for displaying the menu of short-listed options, and a Show/Hide button for manually toggling the list.
The text box filters the li elements using ng-repeat. When selecting an li item from the list, I populate the text box with the selected value, and then hide the list.
<div class="xtab-typeahead" ng-controller="xTabTypeAheadCtrl">
<input type="text" class="xtab-typeahead-search" placeholder="{{type}}" ng-model="query.id" ng-focus="showMenu()" ng-init="query.id = undefined" />
<button ng-click="toggleMenu()">Show/Hide</button>
<ul class="xtab-typeahead-menu" ng-class="{true:'active',false:''}[isActive]">
<li ng-repeat="item in menuItems | filter:query"
ng-click="hideMenu(); query.id = item.id">
{{item.id}}
</li>
</ul>
</div>
My issue is that when the value is assigned to the text box, the list is momentarily filtered to the one option selected (because the text box is also the source of the ng-repeat filter), before hiding the menu. What I want id for the menu to be hidden without being refreshed with the filter first.
It should be noted that this only occurs when using CSS transitions which I am using to fade the menu out.
Here is a plnkr to illustrate.
Here's a working Plnkr. I basically gave your menu time to finish its animation before setting the value. Relevant code is as follows:
$scope.selectItem = function(id){
$scope.isActive = false;
$timeout(function(){
$scope.query.id = id;
}, 250);
}
...and the HTML using the newly created selectItem method:
<div class="xtab-typeahead" ng-controller="xTabTypeAheadCtrl">
<input type="text" class="xtab-typeahead-search" placeholder="{{type}}" ng-model="query.id" ng-focus="showMenu()" ng-init="query.id = undefined" />
<button ng-click="toggleMenu()">Show/Hide</button>
<ul class="xtab-typeahead-menu" ng-class="{true:'active',false:''}[isActive]">
<li ng-repeat="item in menuItems | filter:query"
ng-click="selectItem(item.id)">
{{item.id}}
</li>
</ul>
</div>

Resources