Using templating and filtering in AngularJS - javascript

I'm pretty new to Angular, and I've run into an issue. I'm hoping that there is an easy way to solve this problem.
Basically, I have a list of Appointments that I retrieve from the server.
$scope.appointments = [{starts_at: "2015-1-1", name: "First"}, {starts_at: "2015-1-1", name: "Last"}, {starts_at: "2015-1-2", name: "Next Day"}];
Now I'd like to show these appointments in a list. I decided that I could use ng-repeat + filters for this, but now I'm stuck.
<ion-slide-box on-slide-changed="slideHasChanged($index)">
<ion-slide>
<div class='box'>
<div class="list list-inset" ng-repeat="appt in appointments | filter:????>
<div class="item" ng-click="openAppointmentModal(appt)">
<span>Appt {{appt.name}}</span>
<span>Starts at {{appt.starts_at}}</span>
</div>
</div>
</div>
</ion-slide>
</ion-slide-box>
I wasn't sure if I could use the built-in filterFilter to do this, so I ended up writing my own filter filterDateFilter, which takes in an array, a property name, and what date you want to filter for. I can then write something like ng-repeat='appt in appointments | filterDate:starts_at:curDate' where curDate is defined by $scope.curDate = new Date() in the controller.
Phew. That works, but only for filtering for whatever $scope.curDate is defined as.
Now, I want to the user to be able to swipe to the previous/next days, and show only the appointments for that day. I assume I'd have to move the <ion-slide> to a template, but I'm not sure where to go from there. What are my next steps?
Sorry for the long post and detailed explanation, but I didn't want to just throw up a question and show no previous effort.

I think that instead of ion-slide, you can use the ionic on-slide-left and on-slide-right directives to call a function you define in your controller which increments or decrements $scope.curDate by 1. This seems a simpler alternative to me than simulating infinite slides.
http://ionicframework.com/docs/api/directive/onSwipeLeft/

Related

loading large array in oi-select takes too much of time in angularjs

I am using oi-select library, i have customized it according to my project need and i have written directive for it. The problem is its taking too much of time say 10secs to load around 3k array data. I want to minimize the time for it. Here I have created plunker for this.
I have directive which loads all factory data and provides it to oi-select, in my code here is html
<small><b>select {{MPhardwaresListDropDown.length}}</b></small>
<div style="padding-top: 7px">
<div title="" class="selected-multiple-items">
{{MPselectedHardwares.length}} selected
</div>
<grid-multi-select id="hardwareId" clean="clean" optionlist="MPhardwaresListDropDown" colval="name"></grid-multi-select>
</div>
HTML code in directive looks like
<div>
<div ng-repeat="optionVal in tempOptionList | orderBy : ['-originalcheck','+label']" prevent-close>
<div ng-if="optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.label.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.label}}</span>
</label>
</div>
<div ng-if="!optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.val.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check" ng-change="checking(typeFilterOptions)"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.val}}</span>
</label>
</div>
</div>
angular code is too big to mention in this question please refer plunker, but this is how it loops
scope.selection = scope.selectionorg;
scope.checkedall=scope.checkedallorg;
scope.OptionList=parentScope[scope.parentListName].sort();
scope.tempOptionList=[];
var i=0;
for(i=0;i<scope.OptionList.length;i++) {
scope.tempOptionList[i] = {val: scope.OptionList[i], check: false, originalcheck: false};
}
if(scope.optionListSelectedList.length>0) {
angular.forEach(scope.optionListSelectedList, function(obj){
angular.forEach(scope.tempOptionList, function(obj1){
if(obj===obj1.val){
obj1.check=true;
obj1.originalcheck=true;
}
});
});
}
else{
scope.checkedall=false;
}
};
I want something like which will load partial data on scroll it loads more data, any help will be appreciated. Thank you so much.
EDIT
Now i have edited my plunker with limitTo in ng-repeat, for that i have written new directive which will trigger addmoreitems function when scroll will reach bottom. updatedPlunker
Now problem is when i am typing and searching something its searching in only available records with respect to limitTo its not searching in all data, say now the limitTo is 50 then search is happening only in 50 records not in 3k records.
Correct way of doing this kind of requirement is to do with pagination, since you are loading data from the server side, you should make your api to support pagination.
It should accept three parameters such as number of items, start, limit and you should initially get the number of items and repeat it until you get the whole result set.
There should be another request to get the total number of items. By doing this you can retrieve all the elements at once loaded in the client side. Until that you should have a loading indicator, or you could load this data when the login process/application starts.
limitTo will not be able to help with the search , because you are limiting the results.
The problem is not the array, the browser can easly handle that, the problem is that you're rendering all the 3k DOM elements, that's really heavy work even for an actual machine, also since there is bindings in each dom element {{}} they're being watching by AngularJs, I got the same problem and solved using Virtual Repeat from AngularJS Material, what this does is it doesn't render the whole 3k DOM elements generated by the ng-repeat, instead it just renders the ones that are visible, also I've found another library if you don't want to use Angular Material, this seems to work the same way: Angular VS-Repeat
You may try the limitTo filter in ng-repeat in angularjs which takes the additional argument to start the iteration.
https://docs.angularjs.org/api/ng/filter/limitTo
On scroll, you can then change that argument based on the number of items pending or left for rendering or the number of items already rendered. This should help you in approach of selective loading of data on scroll.

Best practice for displaying Firebase many-to-many relationships in the view (AngularFire)

I have found a lot of examples on how to structure many-to-many relationships in Firebase. Following the most recommended way I've seen to set it up I have some basic posts and tags data in Firebase:
/posts:
-K_GOdSQvCQ2sAcHfo1x
- descpripton: "This is a post..."
- title: "This is a title..."
- tags:
-K_aBTTDKVUovZe3l0lX: true
.....
-K_GFDQjPoSmCJM3YAlB:
- description: "Another post..."
- title: "This title..."
- tags:
-K_aBTTKDhYsnbFv1Tuc: true
......
/tags:
-K_aBTTDKVUovZe3l0lX
- name: "Sport"
- postIds:
-K_GOdSQvCQ2sAcHfo1x: true
.....
-K_aBTTKDhYsnbFv1Tuc:
- name: "Movies"
- postIds:
-K_GFDQjPoSmCJM3YAlB: true
.....
My problem is figuring out how to handle these relationships in my views. I haven't been able to find any tutorials or examples covering it.For example, in the index view I wish to list all posts with their respective tags attached.
So far I've been simply returning all posts and all tags and storing them in separate $scope variables. That's fine but I haven't been able to figure out how to display only the tags relative to each post. I first thought it should be possible trying a nested ng-repeat structure. But it's proving trickier than I expected to get this working.
This is the idea of what I am trying to achieve.
<!-- For each tag in post object iterate tags -->
<div data-ng-repeat="t in posts.tags">
<span data-ng-repeat="tag in tags">
<!-- display only tags here that match with post.tag.id -->
</span>
</div>
I'm thinking that it is perhaps possible using Angular's filter in conjunction with ng-repeat but I've had no luck finding any examples to work from. Maybe what I am trying to do is not possible in the view alone or too cumbersome/ugly. If anyone can give me some pointers on how I might approach it that would be great!
UPDATE
Ok, I have found a way to achieve this but I'm sure it's not optimal. If anyone can inform me on a better approach I'd be grateful.
For example, in the code below I feel that using <h5 ng-if="key === post.$id"> isn't the right way to match the posts to the tags. Maybe there is a standard way of using the boolean values to do this? I'm still a bit new to Firebase/AngularFire so I am eager to learn any emerging design patterns for solving this problem.
Again, the goal is pretty standard. The posts and tags are coming from separate Firebase nodes that have a many-to-many relationship(allowing posts to have many tags and tags to belong to many posts).
In the controller for the index view all posts and all tags are retrieved and saved separately to their own $scope variables. The following view code is how I managed to display the relevant tags inside each post preview.
<li data-ng-repeat="post in posts">
........
<div ng-repeat="tag in tags"> // Iterate tags
<div ng-repeat="(key, value) in tag.postIds"> // Iterate postIds in each tag
// Check tag.postId key against the current post id.
// Should be using booleans some way here instead??
<h5 ng-if="key === post.$id">
<span class="label label-default">
{{tag.name}}
</span>
</h5>
</div>
</div>
</li>
Short answer: denormalize your data.
A bit longer answer: You need to save the data in a way that helps you be efficient when you query it. So for this particular case, you could save the tag's name instead of just true which only shows association.
So instead of
{
posts:
-K_GOdSQvCQ2sAcHfo1x: {
descpripton: "This is a post...",
title: "This is a title...",
tags: {
-K_aBTTDKVUovZe3l0lX: true
}
}
}
You could save it like
{
posts:
-K_GOdSQvCQ2sAcHfo1x: {
descpripton: "This is a post...",
title: "This is a title...",
tags: {
-K_aBTTDKVUovZe3l0lX: "Sport" // <----- Notice the change
}
}
}
Watch this (and others, like this playlist) videos from the official Firebase chanel. Extremely helpful.

How to include date/timestamp with each new entry?

Im working on a project that uses AngularJS and Ionic.
The project will have inputs that forms a list and anyone may comment on each list item.
How do I include a date/time that a user adds an list item or comments on it?
I done some research and understand that AngularJS has a existing item, kind of like a inbuilt method ("[ ].created") for this. I saw many projects just use that without any controllers. I tried but it didnt work for me. I tried to then add a js code, didnt work as well. My code below.
Not too sure if its a Ionic compatibility issue if any? Would appreciate some help. Sticking to the AngularJS method will be preferred! Thank you!
The AngularJS documentation for dates
The resultant code is as follows with a filter:
html portion (included ng-controller="MainCtrl")
<div class="post row" ng-repeat="post in posts"></div>
<a href="{{ post.url }}">{{ post.title }}
<span class="url">({{ post.url | hostnameFromUrl }})</span>
</a>
<br> {{ post.created | date }}
<!-- this is the angularJS code added -->
I'm not quite sure what are you tiring to achieve with this "$scope.post" variable
It will throw undefined because you are creating static object "$scope.post" without "get()" function here:
$scope.post = {url: 'http://', title: ''};
If you are tying to revive some data form server try using angular $http component
Here also "$scope.post[0]" and "post.getDate" will become undefined
$scope.date = post.getDate( $scope.post[0].created);

AngularJs - Holding value of for loop string to use elsewhere

I've come a bit stuck in my angularjs project.
I run a for loop to query some nested JSON data and output it in 3 different variables inside an ng-repeat. So it makes up a title where i have the control over the elements that make up the title {{ number }} {{ shots }} {{ goals }}.
However, my knowledge of angularjs is stretched here because when I click on one of the events (from the ng-repeat list) it gives me a new tab, but I want to bring the title of that event to the new tab.
I can't call it as a scope variable as the last variable is still being held in there. I thought about assigning it as a new variable.. but was unsure how to actually do that in angularjs.
Here is the code i'm working with:
<li ng-repeat="event in events.events">
<div ng-if="actionType(event)" >
{{number}}
{{shots}}
{{goals}}
</div>
<a class="showPlayer" ng-click="showPlayer(event)">
View more stats
</a>
</li>
my angularjs is just a standard for loop which looks for values inside the js and assigns them as variables.
Any advice is very much appreciated.
EDIT: 24 hours later and I still can't crack this (very frustrating).
I'm not sure if there is a way to grab the string from the ng-click and clone that?
I don't want to have to run another check for the title when I already have the information, surely there is an 'angular' way to do this??
Please try this I am not sure but it might help you
<li ng-repeat="event in events.events">
<div ng-if="actionType(event)" >
{{event.number}}
{{event.shots}}
{{event.goals}}
</div>
<a class="showPlayer" ng-click="showPlayer(event.number,event.shots,event.goals)">
View more stats
</a>
</li>
Try using $rootScope.
Define a rootscope variable where event is handled and write your title in html like you did. {{title}}
$rootScope.title = "example";

AngularJS bind specific data to template

I'm currently making a switch from Knockout to Angular. The main problem I'm having right now is in transferring my original templates to something Angular will recognise.
Specifically, here's a bit of code I'm having trouble transferring:
<!-- ko template: { name: 'SquareTempl', data: Squares[5] } --><!-- /ko -->
In Knockout, this will attach Squares[5] to SquareTempl, so that when the template gets rendered, it does so using the members within Squares[5](or whatever data that gets attached).
I need to repeat the process for Squares[0]~Squares[11]. I can't use ng-repeat though since I won't be iterating through them in numerical order.
Ideally, it would be nice if I could do something along the lines of
<td class="Square" id="five" ng-include src="'SquareTempl.html'" ng-data="Squares[5]">
Any ideas?
Here's a JSFiddle I've written to outline a failed attempt I've tried using ng-model.
http://jsfiddle.net/fZz3W/9/
Two things: First, you can make ng-data be available by implementing it yourself YourApp.directive("ngData", function() {}) Secondly, do you need the HTML to be part of another file? An easy way to accomplish what you're looking for in Angular is with ng-repeat like:
<td ng-repeat="item in Square">
<div>{{item.name}}</div>
</td>
When the Square array is updated an additional post will be made.
Review your modified JSFiddle: http://jsfiddle.net/TdWMF/1/
So this is mostly a hack to achieve what you want, until I can offer you a better solution:
Second update using mixed order ng-repeat: http://jsfiddle.net/TdWMF/3/
Basically:
<div ng-repeat="index in [4, 2, 0, 3, 1]">
square index: {{index}}<br />
square: {{Squares[index]}}
</div>
Pretty ugly, and non-ideal, I know. I'd also recommend performing the order array generate in a function and then doing: ng-repeat="index in displayOrder(Squares)"

Categories