Execute different Event on First and second click angular js - angularjs

I have a situation where i need to execute different event on first and second click on div.
On first click div will be expanded and in second click it will be redirected to it's detail page. Div contains details of post.
<div ng-click="select(comment._id);"
ng-dblclick="changeurl(user.username,data.type,comment._id)"
enter ng-repeat="user in data.user">
Till now i was expanding div on first click and then on double click i was redirecting it to it's detail page.
what would be the best way to do it.
right now my idea is to make a counter on ng-click and then execute event.
can we execute it on the DOM End.
<div ng-click="counter = counter + 1" ng-repeat="user in data.user" ng-init="counter = 0" >
is there any other directive where i can execute select(comment._id) changeurl(user.username,data.type,comment._id) conditionally or how can i call these functions conditionally ?
Update: on Diana advice here is updated status:
Here i have no of div's coming from ng-repeat, here i have two type of layouts which are displayed according to data.
<div ng-repeat="data in comments">
<div ng-if="data.type=story">
<div ng-click="clickEvent($index,comment._id, user.username,data.type);" enter ng-repeat="user in data.user">
//displaying data here
</div>
</div>
<div ng-if="data.type=news">
<div ng-click="clickEvent($index,comment._id, user.username,data.type);" enter ng-repeat="user in data.user">
//displaying data here
</div>
</div>
</div>
$scope.secondClick = [];
//scope data
myservice.data().then(function(response){
$scope.comments = response.data;
for (i=0; i< $scope.comments.length;i++){
$scope.secondClick[i] = false;
}
});
$scope.clickEvent = function(index,id, userName, type){
if(!$scope.secondClick[index]){
// Execute first-click logic (call select function with the id).
$scope.secondClick[index] = true;
}
else{
// Execute second-click logic (call changeurl function with the id, userName, and type).
}
}
I tried providing index to function and manipulate them. Because of nested ng-repeat i am getting index '0'. .

UPDATE2:
I tried to understand your setup from the question and came up with a full working demo accordingly. I hope this works for your case!
http://plnkr.co/edit/ARsDpRH7orzZQLU1pin1
Please let me know how this goes.
/////////////////////////////////////
You could have some boolean variable that is initially set to false, say $scope.secondClick - when you click the button the first time set it to true. When clicking it the next time the variable is true so you execute the "second click" logic.
Something like:
<div ng-click="clickEvent(comment._id, user.username,data.type);" enter ng-repeat="user in data.user">
In your controller, define:
$scope.secondClick = false;
$scope.clickEvent = function(id, userName, type){
if(!$scope.secondClick){
// Execute first-click logic (call select function with the id).
$scope.secondClick = true;
}
else{
// Execute second-click logic (call changeurl function with the id, userName, and type).
}
}
UPDATE:
Given that you have more than one div, we can tweak the solution a bit by having an array of boolean values in the scope and passing the index to know which is which:
<div ng-click="clickEvent($index, comment._id, user.username,data.type);" enter ng-repeat="user in data.user">
In the controller:
$scope.secondClicks = [];
//Initialize all values to false, based on the number of divs.
for(var i = 0; i<NoOfDivs; i++){
$scope.secondClicks[i] = false;
}
$scope.clickEvent = function(index, id, userName, type){
if(!$scope.secondClicks[index]){
// Execute first-click logic (call select function with the id).
$scope.secondClicks[index] = true;
}
else{
// Execute second-click logic (call changeurl function with the id, userName, and type).
}
}
Let us know if this works please. If someone has a neater solution, I'd like to check it out as well :)

I think this may be a simple way to tell JavaScript to do different things depending on the number of clicks. This example shows swapping of two different events. Basicly, I just limited the counting from 0 to 1 and made sequence circular. Ofc, you can make more than just two. I think the advantige of this is that is circular, so the user may navigate freely (first he expands something, than he shrinks it back, than he expands it again etc...). Maybe by following this principle you will solve your code.
<!DOCTYPE html>
<head>
<style>
#division {background-color: red;color: white;width: 10%;height: 20%;position: fixed;top: 10%;left: 10%;-webkit-transition: 1s;-moz-transition: 1s;-ms-transition: 1s;-o-transition: 1s;transition: 1s;}
#division:hover {background-color: blue;cursor: pointer;}
</style>
</head>
<body>
<script type="text/javascript">
var clicks = 0;
function onClick() {
clicks += 1;
if (clicks != 1) {
clicks = 0;
};
if (clicks == 1) {
document.getElementById("division").style.left = 30+'%';
}else {
document.getElementById("division").style.left = '10%';
};
};
</script>
<div onClick="onClick()"><a id="division">Click me Twice!</a></div>
</body>
</html>

I did this by putting post id in cookie. On first click that post's id is stored in cookie and if user click again it is founded in cookie store second click action is triggered. Hence we do action on first and second click.
$scope.clickEvent = function(id,name,type){
var favoriteCookie = $cookieStore.get('divonclick');
$cookieStore.put('divonclick',id);
if(favoriteCookie == id){
//second click action
}else{
//first click action
}
}

Try This:
In html div:
ng-init="count=0"
ng-click="select(count)(count=count+1);"
In JS:
$scope.select = function(count) {
if(count%2 == 0 ) {
here write your SECOND click functionality.
} else {
here write your FIRST click functionality.
}
}

Related

ngRepeat doesn't refresh rendered value

I'm having an issue with ngRepeat :
I want to display a list of students in two different ways. In the first one they are filtered by group, and in the second they are not filtered.
The whole display being quite complex, I use a ngInclude with a template to display each student. I can switch between view by changing bClasseVue, each switch being followed by a $scope.$apply().
<div ng-if="currentCours.classesOfGroup !== undefined"
ng-show="bClassesVue">
<div ng-repeat="group in currentCours.classesOfGroup">
<br>
<h2>Classe : [[group.name]]</h2>
<div class="list-view">
<div class="twelve cell"
ng-repeat="eleve in group.eleves | orderBy:'lastName'"
ng-include="'liste_eleves.html'">
</div>
</div>
</div>
</div>
<div class="list-view" ng-show="!bClassesVue">
<div class="twelve cell"
ng-repeat="eleve in currentCours.eleves.all"
ng-include="'liste_eleves.html'">
</div>
</div>
My problem happens when my list of students change (currentCours here). Instead of refreshing the ngRepeat, both lists concatenate, but only in the unfiltered view.
I tried adding some $scope.$apply in strategic places (and I synchronize my list for example) but it doesn't help.
EDIT : the function used to refresh currentCours in the controller. It's called when a "cours" is selected inside a menu.
$scope.selectCours = function (cours) {
$scope.bClassesVue = false;
$scope.currentCours = cours;
$scope.currentCours.eleves.sync().then(() => {
if ($scope.currentCours.classe.type_groupe === 1) {
let _elevesByGroup = _.groupBy($scope.currentCours.eleves.all, function (oEleve) {
return oEleve.className;
});
$scope.currentCours.classesOfGroup = [];
for(let group in _elevesByGroup) {
$scope.currentCours.classesOfGroup.push({
name: group,
eleves: _elevesByGroup[group]
});
}
$scope.bClassesVue = true;
}
});
utils.safeApply($scope);
};
Well, I found a workaround, but I still don't know why it didn't work, so if someone could write an explanation, I would be very thankful.
My solution was simply to open and close the template each time I switch between views.

App Fails First Load When Listing Array's Item from a DataSnapshot from Firebase

I am storing a DataSnapshot into a $scope array, so the ng-repeat into HTML div updates my "result list"
The problem is, when I run the code, input a value and click to run the DataSnapshot function, the first result doesn't appear on the app screen, only on the app's log on browser's console. If I run the function again, the result appears. If I change the input and click the button (run the function) again, this input appears in the first try.
So, here's what you will probably need:
Print from first attempt(data appears on console but not on app):
Print from second attempt(data appears twice on console and once on app):
Print from third attempt with another input(data appears once on console and once on app):
Codes:
index.html
<div class="list card">
<div class="item item-body" ng-repeat="locker in lockers">
<a href="#" class="item item-icon-right text-center">
<img ng-src="{{imageSrc}}" style="width: 35px; height: auto;">
<p style="text-align: center">
Locker - {{locker.number}}
<br>
{{key}}
</p>
<i class="icon ion-chevron-right"></i>
</a>
</div>
app.js
angular.module('starter', ['ionic', 'firebase'])
.controller('LockerCtrl', ["$scope", "$firebaseArray", function($scope,$firebaseArray, snapshot){
var lockersRef = new Firebase('https://ifrjarmariosdb.firebaseio.com/armarios');
$scope.getButtonClicked = function() {
var lockernumber = document.getElementById('lockerNumberInput').value;
if(lockernumber.length > 0){
lockerInputedNumber = parseInt(lockernumber);
lockersRef.on('value', function(snapshot){
snapshot.forEach(function(data){
var number = data.val().number;
if(number == lockerInputedNumber){
$scope.key = data.key();
$scope.lockers = [data.val()];
console.log(data.val());
if(number == 101){ -
$scope.imageSrc = "img/locker_test2.png";
}
else{
$scope.imageSrc = "img/locker_test.jpg";
}
}
});
});
}
As you could see by the prints, I'm also facing some trouble to change the image source according to the number value from Firebase. If you could help me to solve that, it would be a great help.
Please, I'm not asking for the solution for this method, if you know a different method to do this, I ask you to post it too.
Thanks!
This code starts loading data from Firebase:
lockersRef.on('value', function(snapshot){
snapshot.forEach(function(data){
var number = data.val().number;
if(number == lockerInputedNumber){
$scope.key = data.key();
$scope.lockers = [data.val()];
console.log(data.val());
if(number == 101){ -
$scope.imageSrc = "img/locker_test2.png";
}
else{
$scope.imageSrc = "img/locker_test.jpg";
}
}
});
The loading happens asynchronously. Since it takes time before the data is available, the browser continues executing the JavaScript after this code.
When the data comes back from the server, it executes your callback function. But at that point, AngularJS is no longer expecting any changes to the $scope.
The solution is to make AngularJS aware of the fact that you've changed the scope. The easiest way to do that, is to wrap the callback into a $timeout() call:
lockersRef.on('value', function(snapshot){
$timeout(function() {
snapshot.forEach(function(data){
var number = data.val().number;
if(number == lockerInputedNumber){
$scope.key = data.key();
$scope.lockers = [data.val()];
console.log(data.val());
if(number == 101){ -
$scope.imageSrc = "img/locker_test2.png";
}
else{
$scope.imageSrc = "img/locker_test.jpg";
}
}
});
});
Some people that ran into the same problem:
Angular JS firebase.(child_added) not rendering on page
ng-show and ng-change unwanted delay
A few other things I note about your code:
you're downloading all lockers and then filtering for the one the user entered client-side. This is wasting bandwidth that your user might be paying for. A better way would be to leave the filtering to Firebase with a Query:
var query = lockersRef.orderByChild('number').equalTo(lockerInputedNumber);
query.on('value', function(snapshot){
there is a binding library for AngularJS+Firebase called AngularFire, which handles the $timeout() thing automatically. It's built on top of Firebase's JavaScript SDK that you're now using, so they interoperate perfectly.

AngularJS : why after loading more data filter stop working?

there is one filter functionality in my demo I will explain my problem I have one table in which i use infinite scroll is implemented In other words when user moves to bottom it load more data.There is search input field in top .Using this I am able to filter item in table .but I don't know why it is not working
When you search "ubs" and "ing" first time .it works perfectly .But when you load more data other words when user scroll to bottom and load more data the again it try to filter "ubs" and "ing" it not give any result why ?
<label class="item item-input">
<img src="https://dl.dropboxusercontent.com/s/n2s5u9eifp3y2rz/search_icon.png?dl=0">
<input type="text" placeholder="Search" ng-model="query">
</label>
secondly Actually I am implementing infinite scroll so only 100 element display .can we search element from 2000 (which I am getting from service )and display data the search result ?
Update :
Here's a Plunker with everything working together. I have separated all of the pieces into individual JS files, as it was getting unruly:
Plunker
Search
The built in filter will only return results from the current view data that the ng-repeat is displaying. Because you're not loading all of the data into the view at once, you'll have to create your own search functionality.
In the demo, click the search icon to show the search box, then type your search value and press the ENTER key or click the search button to return the results.
Since you want to check whether the user pressed ENTER you have to pass both the event and the querystring to the function, so you can check for the enter keycode. The function should also run when someone clicks or taps the search button. I set ng-model="query" on the input, so query is the reference in the view. Therefore, you'll add ng-click="searchInvoices($event, query)" to your search button, and ng-keyup="searchInvoices($event, query)" to the input. And, finally, to make it easy to clear the input field, add a button that displays when the input is not empty with ng-show="query" and attach a click event with ng-click="query=null; resetGrid()".
Add the searchInvoices function to your controller. It will only run the search if either the query is empty (because you need to reset the view if the person uses the backspace key to empty the input) OR if the user pressed ENTER OR if the event was a click event in case the user clicks the search button. The inner if statement, prevents the search from running if the query is empty and just resets the view. If the query is not empty, against the total dataset and builds an array of matching results, which is used to update the view.
The last line sets the scroll position to the top of the scrollview container. This makes sure that the user sees the results without having to click somewhere in the scrollview container. Make sure you inject the $ionicScrollDelegate into your controller for this to work and set delegate-handle="invoicegrid" on your ion-scroll directive.
$scope.searchInvoices = function(evt, queryval) {
if (queryval.length === 0 || evt.keyCode === 13 || evt.type === 'click') {
if (queryval.length === 0) {
$scope.invoice_records = $scope.total_invoice_records;
} else {
var recordset = $scope.total_invoice_records;
results = [];
var recordsetLength = recordset.length;
var searchVal = queryval.toLowerCase();
var i, j;
for (i = 0; i < recordsetLength; i++) {
var record = recordset[i].columns;
for (j = 0; j < record.length; j++) {
var invoice = record[j].value.toLowerCase();
if (invoice.indexOf(searchVal) >= 0) {
results.push(recordset[i]);
}
}
}
$scope.invoice_records = results;
$ionicScrollDelegate.$getByHandle('invoicegrid').scrollTop();
}
}
};
Lastly, you need to modify the loadMore() function that is used by the infinite scroll directive, so that it doesn't try to load additional data when scrolling through the search results. To do this, you can just pass the query into loadMore on the directive like: on-infinite="loadMore(query)", then in your function, you can just run the broadcast event when the query exists. Also, removing the ngIf will ensure that the list remains dynamic.
$scope.loadMore = function(query) {
if (query || counter >= $scope.total_invoice_records.length) {
$scope.$broadcast('scroll.infiniteScrollComplete');
} else {
$scope.counter = $scope.counter + showitems;
$scope.$broadcast('scroll.infiniteScrollComplete');
}
};
You used filter in wrong way inside ng-repeat like ng-repeat="column in invoice_records | filter:query" instead of ng-repeat="column in invoice_records | query"
<div class="row" ng-repeat="column in invoice_records |filter:query">
<div class="col col-center brd collapse-sm" ng-repeat="field in column.columns" ng-show="data[$index].checked && data[$index].fieldNameOrPath===field.fieldNameOrPath">{{field.value}}</div>
<div class="col col-10 text-center brd collapse-sm"></div>
</div>
Demo Plunkr

unable to get ng-checked property for a radio button to check the button when populating a form

EDIT: for those of you who don't want to go through the code, I'm basically passing the form a "node" object with node.selectedAnswer = "4,1,4" or some string like that. The form has radio buttons and one of the buttons has a value "4,1,4". the radio button also has ng-checked="node.selectedAnswer" expression. But that doesn't work. I know for sure that node.selectedAnswer has the appropriate value.
I have a series of radio button questions that I'm asking the user. I want them to be able to go previous and next. I'm using a stack to store the data retrieved from ajax call, as well as selectedAnswer when they select an option and click next. I've commented the code itself to explain the situation where I can. Everything seems to be working, except ng-checked is just not picking up node.selectedAnswer, even though I can output {{node.selectedAnswer}} properly to the page.
<div class="container-fluid" ng-app="AccountRequest" ng-controller="GameNode" ng-init="outside={}">
<div class="row-fluid">
<div class="span2"></div>
<div class="span10">
<form>
<!-- node.selectedAnswer displays the selectedAnswer correctly when clicking previous and going back.
However, ng-checked is somehow not selecting the appropriate radio button. -->
<span>{{node.Question.Text}} selected answer: {{node.selectedAnswer}}</span>
<div class="radio" ng-repeat="answer in node.Answers">
<input type="radio" id="answerGroup" name="answerGroup" ng-checked="node.selectedAnswer" ng-model="outside.selectedAnswer"
value="{{answer.BranchId}},{{node.LeafId}},{{answer.Id}}"/> {{answer.Text}}
</div>
<div>
<input type="button" ng-click="previous()" value="Previous"/>
<input type="button" ng-click="next(outside.selectedAnswer)" value="Next"/>
</div>
</form>
</div>
</div>
</div>
//below is the script
app.controller('GameNode', function ($scope, $http) {
var nodes = [];
function load(branchId, leafId, answerId) {
$http.get("/AccountRequest/GetNode?branchId=" + branchId +
"&leafId=" + leafId +
"&answerId=" + answerId)
.success(function (data) {
//get data and push it in the stack
nodes.push(data);
$scope.node = data;
});
}
function populateValues(selectedAnswer) {
var answer = null;
if (selectedAnswer === undefined || selectedAnswer == null)
selectedAnswer = "0,0,0";
//when next is clicked, retrieve the selectedAnswer from form and store it in current node as a property.
if (nodes.length > 0) {
var curNode = nodes.pop();
curNode.selectedAnswer = selectedAnswer;
nodes.push(curNode);
}
answer = selectedAnswer.split(',');
if (answer != null) {
load(answer[0], answer[1], answer[2]);
}
}
$scope.next = populateValues;
$scope.previous = function () {
//when previous is clicked, pop the current node out and throw it away.
//then pop the previous node out, read it, and push it back in as current node.
if (nodes.length > 1) {
nodes.pop();
var prevNode = nodes.pop();
nodes.push(prevNode);
$scope.node = prevNode;
}
};
populateValues();
});
Older Answer - This works, (was marked correct) but using $parent can get a bit messy in nested repeats.
In this instance, you don't need to use ng-checked at all. Since this is a radio group, the checked attribute will be bound to the model. If the model is bound to the value of an individual radio button, then your ability to change which button is "checked" becomes very simple.
Here is a plunk that demonstrates the concept.
So in your case a few changes need to be made.
1. Get rid of 'id' attribute - the ID must be unique for each element.
2. Each item created in an ng-repeat creates its own child scope. So to access the original model, "$parent" must be invoked.
<input type="radio" name="answerGroup" ng-model="$parent.someAnswerAttribute"
value="{{answer.BranchId}},{{node.LeafId}},{{answer.Id}}"/>
In your controller define the model as you already did, then modify it to be tied to a value of a button, which in your case will be a bit lengthy, since you have multiple attributes within your value.
$scope.someAnswerAttribute = // exactly what the value of a radio button would be.
Again, the plunker above reflects this concept. Hope this helps!
..
..
Edit - Better Answer:
Since the ng-repeat creates its own child scope, and two-way binding is necessary, the ng-model should be referencing an object instead of a primitive. In other words, if the model was $scope.myModel="Biff", the child scope can not access that without invoking $parent (in the answer below). However, if the model is referencing a property of an object, the child will receive prototype inheritance of that object. (I think I said that right).
So using the older answer example, we can change:
From this in the parent controller:
$scope.someAnswerAttribute = "Biff";
To this in the parent controller:
$scope.someAnswerAttribute = {value: "Biff"}
And in the radio group:
<input type="radio" name="answerGroup" ng-model="someAnswerAttribue.value"
value="{{answer.BranchId}},{{node.LeafId}},{{answer.Id}}"/>
This plunk is forked from the older answer and demonstrates model as an object property.

Add class to DIV if checkbox is checked onload

I need help with a script to add an "active" class to a div when a hidden checkbox is checked. This all happening within a somewhat complex form that can be saved and later edited. Here's the process:
I have a series of hidden checkboxes that are checked when a visible DIV is clicked. Thanks to a few people, especially Dimitar Christoff from previous posts here, I have a few simple scripts that handle everything:
A person clicks on a div:
<div class="thumb left prodata" data-id="7"> yadda yadda </div>
An active class is added to the div:
$$('.thumb').addEvent('click', function(){
this.toggleClass('tactive');
});
The corresponding checkbox is checked:
document.getElements("a.add_app").addEvents({
click: function(e) {
if (e.target.get("tag") != 'input') {
var checkbox = document.id("field_select_p" + this.get("data-id"));
checkbox.set("checked", !checkbox.get("checked"));
}
}
});
Now, I need a fourth ( and final ) function to complete the project (using mootools or just plain javascript, no jQuery). When the form is loaded after being saved, I need a way to add the active class back to the corresponding div. Basically reverse the process. I AM trying to figure it out myself, and would love to post an idea but anything I've tried is, well, bad. I thought I'd at least get this question posted while I work on it. Thanks in advance!
window.addEvents({
load: function(){
if (checkbox.checked){
document.getElements('.thumb').fireEvent('click');
}
}
});
Example: http://jsfiddle.net/vCH9n/
Okay, in case anyone is interested, here is the final solution. What this does is: Create a click event for a DIV class to toggle an active class onclick, and also correlates each DIV to a checkbox using a data-id="X" that = the checkbox ID. Finally, if the form is reloaded ( in this case the form can be saved and edited later ) the final piece of javascript then sees what checkboxes are checked on page load and triggers the active class for the DIV.
To see it all in action, check it out here: https://www.worklabs.ca/2/add-new/add-new?itemetype=website ( script is currently working on the third tab, CHOOSE STYLE ). You won't be able to save/edit it unless you're a member however, but it works:) You can unhide the checkboxes using firebug and toggle the checkboxes yourself to see.
window.addEvent('domready', function() {
// apply the psuedo event to some elements
$$('.thumb').addEvent('click', function() {
this.toggleClass('tactive');
});
$$('.cbox').addEvent('click', function() {
var checkboxes= $$('.cbox');
for(i=1; i<=checkboxes.length; i++){
if(checkboxes[i-1].checked){
if($('c_'+checkboxes[i-1].id))
$('c_'+checkboxes[i-1].id).set("class", "thumb tactive");
}
else{
if($('c_'+checkboxes[i-1].id))
$('c_'+checkboxes[i-1].id).set("class", "thumb");
}
}
});
// Add the active class to the corresponding div when a checkbox is checked onLoad... basic idea:
var checkboxes= $$('.cbox');
for(i=1; i<=checkboxes.length; i++){
if(checkboxes[i-1].checked){
$('c_field_tmp_'+i).set("class", "thumb tactive");
}
}
document.getElements("div.thumb").addEvents({
click: function(e) {
if (e.target.get("tag") != 'input') {
var checkbox = document.id("field_tmp_" + this.get("data-id"));
checkbox.set("checked", !checkbox.get("checked"));
}
}
});
});

Resources