Angular.js - $scope not updating from setTimeout() - javascript

The idea is that I show a loading .gif file while I load the data and then once all the data has loaded, wait an x amount of time, currently 3000ms, but will be lowered, then after the given amount of time, set the value of data.show to true, so that the loading .gif is no longer showing and that the Table is.
So, how do I reference show from the JavaScript in the HTML, ng-show?
Any help would be appreciated!
Thanks :)
edit: This works regarding to this to answer - setTimeout v $timeout
HTML
<div ng-show="!show">
<img id="loader" src="resources/ajax-loader.gif">
</div>
<div ng-show="show">
<div class="ui grid center aligned">
<div class="column thirteen wide">
<table id="errorTable" class="ui compact celled definition table">
<p>Stuff</p>
</table>
</div>
</div>
</div>
JavaScript
function onSuccess(response) {
controller.errors = response.data;
setTimeout(function () {
$('#errorTable').DataTable({
"lengthMenu": [[6], [6]],
"dom": '<"toolbar">frtip'
});
$("div.toolbar").html('<div class="ui input left floated"><input type="text" placeholder="Search..."></div>');
$scope.show = true;
}, 3000);
}

The problem is you have not defined $scope.show in the beginning. You probably don't need show.data . Please add the following line at the beginning of the controller
$scope.show = false;
And Change html to:
<div ng-show="!show">
<img id="loader" src="resources/ajax-loader.gif">
</div>
<div ng-show="show">
<div class="ui grid center aligned">
<div class="column thirteen wide">
<table id="errorTable" class="ui compact celled definition table">
<p>Stuff</p>
</table>
</div>
</div>
</div>
And in controller:
You have added the $scope.show.data outside setTimeout bring it within the setTimeout function as per your need and add:
function onSuccess(response) {
controller.errors = response.data;
$timeout(function () {
$('#errorTable').DataTable({
"lengthMenu": [[6], [6]],
"dom": '<"toolbar">frtip'
});
$("div.toolbar").html('<div class="ui input left floated"><input type="text" placeholder="Search..."></div>');
$scope.show = true;
}, 3000);
}
Here is the plunker that I tried

Try adding $scope.show = { data: false }; in the controller

As there is no div.toolbar, meaning, $('div.toolbar') would be null/undefined, which leads to a Null Pointer at that statement and the next statement is not being executed, as the execution has stopped just before.
Simple solution could be to place the $scope.show.data = true; as the first statement in the function called by setTimeout.

Related

Get content of the dynamically created element using jquery

Using server side process I have created my table and I am loading some data in a element like bellow
I want the content of class more element, I tried using ready, load but I am getting undefined when alert is made so please someone help me
My Whole code is here
<div class="container margin_120" id="form1">
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<div id="tools">
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<h4 class="inner margin_5_tb"><i class=""></i>View Counts of Students Needing Financial Assistance<button type="button" id="button1" style="border:none;background-Color:#EAECEE; float: right;font-size:12px" ><u>Hide</u></button></h4>
</div>
</div>
</div><!--/tools -->
<div class="form-group" id="countTableDes">
<table class="table table-bordered" id="loan_data">
<thead>
<tr>
<th>Student Name</th>
<th>Student Photo</th>
<th>some data</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<script src="<?php echo base_url(); ?>assets/js/jquery-1.11.2.min.js"></script>
$(document).ready(function(){
var dataTable = $('#loan_data').DataTable({
"processing":true,
"serverSide":true,
"autoWidth": false,
"order":[],
"ajax":{
url:'<?php echo site_url("home/getLoans"); ?>',
type:"POST"
},
"columns":[
{ 'data' : 'stu_firstName' },
{ 'data' : 'id',
'render':function(id,data,row){
if(row.stu_passportPhoto && row.stu_passportPhoto!=null){
return '<p class="more"><b>About me</b> <br/>'+row.stu_aboutStudent+'</p>';
}
}
},
{
'data' : 'stu_somedata'
},
]
});
//Here I want to get each more class content.
$(document).find(".more").each(function(){
var content = $(this).text();
alert(content);
});
});
</script>
As the elements with the class "more" are created dynamically by the DataTable, you have to wait until the table or at least the row has been drawn completely. In order to do so, you have to hook into one of the available callbacks that DataTables offers:
https://datatables.net/reference/option/
Which one exactly depends on your needs, presumably drawCallback should work for your case, or you could also use createdRow for each row separately.
This is the example snippet from the docs, adapted to illustrate your case:
$('#loan_data').dataTable( {
// rest of your options goes here...
"drawCallback": function( settings ) {
// you'll want to select only inside your table,
// not the whole document, for performance reasons:
$('#loan_data').find(".more").each(function(){
var content = $(this).text();
alert(content);
});
}
} );
The problem might be the way datatable is rendered and how column:render is executed,
Worst case scenario is to use a mutator, or an event listener
such as Arrive.js
here is the link,
https://github.com/uzairfarooq/arrive/blob/master/src/arrive.js
It will listen to newly created elements and you can run something inside its function,
Do something like this,
$(document).arrive(".more", function() {
// 'this' refers to the newly created element
var content = $(this).text();
alert(content); /* do something */
});

Retain scroll position when a button is toggled

I am working on an angularJS application which has a page where I display around 30 items using ng-repeat. In front of each item, there is a toggle button (enabled/disabled). With the current code that I have, I can toggle these items. But the problem is if I scroll down and toggle lets say item 25, then automatically it scrolls to the top of the page. If I now scroll down, I can see that the toggle actually took place.
So the requirement now is to make sure that the scroll position is retained after the toggle button is clicked.
Please see below the code that I have.
HTML
<div id="eventTypes" class="panel-body">
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
<div ng-if="spinner" class="spinner">
<div class="spinner-container container1">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container2">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
<div class="spinner-container container3">
<div class="circle1"></div>
<div class="circle2"></div>
<div class="circle3"></div>
<div class="circle4"></div>
</div>
</div>
</div>
JS
(function () {
'use strict';
angular.module('myApp').controller('itemsController', function ($scope, itemsService) {
var serviceError = function (errorMsg) {
console.log(errorMsg);
$scope.turnOffSpinner();
};
$scope.items = [];
$scope.item = {};
$scope.spinner = true;
$scope.toggleEnabled = function (item) {
$scope.turnOnSpinner();
itemsService.toggleEnabled(item)
.then(function () {
$scope.loaditems();
});
};
$scope.loaditems = function () {
itemsService.getitems().then(function (response) {
$scope.items = response.data;
}, serviceError);
$scope.turnOffSpinner();
};
$scope.turnOnSpinner = function () {
$scope.spinner = true;
};
$scope.turnOffSpinner = function () {
$scope.spinner = false;
};
$scope.loaditems();
});
}());
How this works right now is, once I click the toggle button, a spinner is enabled. Meanwhile the controller will call the itemService.toggleEnabled() method which does an ajax call to the server to just change the status of the item(enabled to disabled or vice-versa) in the backend. On successful change of the status and when the ajax call returns, the $scope.loadItems() method is called in the controller. This method will then do another ajax call to fetch the items (now with the updated status of the item that was toggled). The spinner is disabled and the data is then displayed on the UI.
When all of this is done, the page is scrolled to the top. This is annoying when I want to toggle an item which is way down in the list.
I want the page to be present at the same position when I clicked the toggle button of the corresponding item and not scrolling up to the top.
I am new to AngularJS and any help in this regard would be really helpful.
It looks like your spinner scheme is what's causing you problems:
...
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
...
<div ng-if="spinner" class="spinner">
...
Whenever you click your button, you are removing every single element in your ng-repeat from the DOM when you $scope.turnOnSpinner(). That's why it appears to jump to the top. It's not really jumping, there just aren't enough DOM elements to fill up the page, making the page so short that the scrollbar disappears (even if it's only for a second). Then when the spinner is done, your ng-repeat fills up the page with DOM elements again, resulting in your scroll position being lost.
So basically what you are trying to fix is a symptom of a less than ideal loading spinner implementation.
ng-if is a "brutal" way of hiding things in Angular. It's mostly meant to hide things for a longer period of time than "softer" directives like ng-show/ng-hide. One solution to your problem is to use ng-disabled on each one of your buttons to prevent the user from interacting with it while the spinner is active, rather than doing a hard removal of each element:
Before:
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
After:
<div ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
ng-disabled="spinner"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
Another solution, which I really like and use myself is this Angular module: https://github.com/darthwade/angular-loading
You can attach it to any element in the page and it will put a loading spinner over it and prevent you from interacting with it until your ajax or whatever is done.
If you don't like either of those, try putting your ng-repeat into a container that you can use to prevent interaction with your elements when the spinner is up:
<div class="container" ng-class="{'you-cant-touch-this': spinner}">
<div ng-repeat="item in items" class="panel-body">
<div class="row">
<div class="col-md-9">{{item.itemName)}}</div>
<div class="col-md-3">
<input id="toggleEnabled"
type="button"
ng-class="{'btn-primary': item.enabled}"
value="{{item.enabled ? 'enabled' : 'disabled'}}"
ng-click="toggleEnabled(item)">
</div>
</div>
</div>
</div>
Now you can style it in some way to prevent interaction without having to remove all those items from the DOM:
.you-cant-touch-this {
pointer-events: none;
}

how to assign array element value to ng-click in angular js

In my program I have an Array which consists of header name and function name.
I am using ng-repeat in a div which consists of a span tag. I want to add different functionality for each iterated span so I stored function name in array.
my html code is:
<div ng-repeat="header in header" ng-init="head=header">
<h4 class="headers">{{ header.name}}</h4>
<div class="arrow-up" ng-show={{ header.arrowup}} ng-click={{header.close}}> </div>
</div>
my angular code is:
$scope.header=[{"name":"Subsection Header #1","arrowup":"arrowup","close":"close()"}];
$scope.close = function() {
console.log(hello);
};
I want to assign close() to the ng-click and arrowup to ng-show. How can I assign them to ng-click and ng-show
change:
<div class="arrow-up" ng-show={{ header.arrowup}} ng-click={{header.close}}>
To:
<div class="arrow-up" ng-show="header.arrowup" ng-click="this[header.close]()">
<button>
CLOSE ME
</button>
</div>
DEMO= http://jsfiddle.net/Lvc0u55v/1788/
ng-click is the problem as i see,
<div class="arrow-up" ng-show="header.arrowup" ng-click="header.close"></div>
we need not give {{}} to ng-click and ng-show.
hope it helps.
First you need to pass a real functions to array:
function arrowUp(){
// ng-show needs to receive true or false
return true;
}
function close(){
// do something here
}
$scope.headers=[{"name":"Subsection Header #1","arrowup":arrowUp,"close":close}];
The bind them in a view:
<div ng-repeat="header in headers">
<h4 class="header">{{ header.name}}</h4>
<div class="arrow-up" ng-show="header.arrowup()" ng-click="header.close()"> </div>
</div>
You need to use the $eval method, you can read more about it here
$scope.$eval Executes the expression on the current scope and returns the result. Any exceptions in the expression are propagated (uncaught). This is useful when evaluating Angular expressions
HTML:
<div ng-controller="TestController">
<div ng-repeat="header in headers">
<h4>{{ header.name}}</h4>
<button type="button" ng-click="onExecuteFunctionFromString(header.close)">Click Here</button>
</div>
</div>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('TestController', ['$scope', function($scope) {
$scope.headers = [{
"name": "Subsection Header #1",
"arrowup": "arrowup",
"close": "close()"
}];
$scope.close = function() {
console.log('hello');
};
$scope.onExecuteFunctionFromString = function(stringFunction) {
$scope.$eval(stringFunction)
};
}]);
Please see working example here

Create custom follow button for my application

I would like a directive that dynamically knows if I'm following the user in my App.
I have a resource to get the currentUser
this.following = function () {
var defer = $q.defer();
var user = $cookies.getObject('currentUser');
UserResource.get({id: user.id}).$promise.then(
function (data) {
defer.resolve(data.following);
});
return defer.promise;
};
This is in one of my services. It returns all users that I'm following.
When instantiating my controller I fetch the users I follow within my app:
UserService.following().then(
function (data) {
$scope.following = data;
});
I would like to move that into a directive so that I can easily reuse it somewhere else in my app.
This is the HTML I am using right now (and it's not really beautiful) :
<div class="item" ng-repeat="user in users">
<div class="right floated content">
<div ng-show="isFollowing(user)" class="ui animated flip button" tabindex="0"
ng-click='unFollow(user)'>
<div class='visible content'>
Following
</div>
<div class="hidden content">
Unfollow
</div>
</div>
<div ng-hide="isFollowing(user)" ng-click="follow(user)" class="ui button">Follow</div>
</div>
</div>
But instead something like :
<div class="item" ng-repeat="user in users">
<um-follow-button um-user="user"></um-follow-button>
</div>
then depending if I'm following the user or not then render one of the two options.
I don't know if I will have to use a controller in my directive.
I have looked at : https://gist.github.com/jhdavids8/6265398
But it looks like a mess.

Get AngularJS app to display modal on click

I'm listing my schedule entries, now I'm trying to display a modal when clicking on edit for a specific entry. My goal is to be able to edit the values of my entries. But first, can't get the modal to even display
See my plunker or code below
html
<div class="container" ng-app="appUserSchedule">
<div ng-controller="CtrlUserSchedule" >
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">User Schedule</div>
<div class="panel-body">
<div ng-cloak style="max-width:400px;">
<header>
<h3>User schedule</h3>
</header>
<ul>
<li ng-repeat="x in userscheds">{{x.week_day}} {{x.time_start}}-{{x.time_end}}
<span ng-click="showModal($index)" style="cursor:pointer;">Edit</span>
<span ng-click="removeItem($index)" style="cursor:pointer;">Delete</span>
</li>
</ul>
<div>
<div>
<div>
<input placeholder="Add user schedule entry here" ng-model="addMe">
</div>
<div>
<button ng-click="addItem()">Add</button>
</div>
</div>
<p>{{errortext}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="modalContent.html" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Edit</h4>
</div>
<div class="modal-body">
<timepicker ng-model="dt1" hour-step="1" minute-step="15" show-meridian="true"></timepicker>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div> <!-- ng-controller -->
</div> <!-- ng-app -->
js
var app = angular.module("appUserSchedule", []);
app.controller("CtrlUserSchedule", function($scope,$http,$location) {
$http.get('userschedule.json').success(function(data, status, headers, config) {
$scope.userscheds = data.userschedules;
console.log(data);
}).error(function(data, status, headers, config) {
console.log("No data found..");
});
$scope.addItem = function () {
$scope.errortext = "";
if (!$scope.addMe) {return;}
if ($scope.userscheds.indexOf($scope.addMe) == -1) {
$scope.userscheds.push($scope.addMe);
} else {
$scope.errortext = "The item is already in your list.";
}
}
$scope.removeItem = function (x) {
$scope.errortext = "";
$scope.userscheds.splice(x, 1);
}
$scope.showModal = function (action, x) {
var modalInstance;
modalInstance = $modal.open({
templateUrl: 'modalContent.html',
controller: 'CtrlUserSchedule',
scope: $scope
});
// This code for later
// Save User Schedule Entry after making a change, then close modal
saveUserScheduleEntry = function(event) {
$http.put('').success(function(eventsuccess){
}).error(function(err){
/* do something with errors */
});
modalInstance.close();
};
// This code for later
// Close modal
closeUserScheduleEntry = function(event) {
$http.put('').success(function(eventsuccess){
}).error(function(err){
/* do something with errors */
});
modalInstance.close();
};
}
});
ng-repeat creates its own scope, hence the scope of your controller and the scope inside of the ng-repeat is not the same.
therefore $scope.showModal will not be defined inside of the ng-repeat.
the main mistake you are doing here is the "there always has to be a dot in there" rule.
look here:
Why don't the AngularJS docs use a dot in the model directive?
i would recommend to take a little tutorial about the most made angularjs mistakes first, because indeed there is some stuff to know about angularjs before you stop running into little traps like this.
furthermore you did not inject $modal to your controller which should lead in an error like $modal is undefined or something like that.
also you didn't even create/add the corresponding html file you are about to open.
and last but not least you didn't add any dependencies to your angular module regarding bootstrap. so your app won't be able to use/inject $modal anyhow.
see a working plunkr here:
http://plnkr.co/edit/rOjXt1C4E6lHRXUqHdHq?p=preview
have a look at the line where i am "putting the dot in"
$scope.ctrl = this;
i replaced the templateUrl by a simple template because i think it's enough to give the idea.
in the end i have to say that there was so many things wrong about your code that you really should try to debug more on your own and try to accomplish things more step by step.
since there have been like 4-5 mistakes in it this code barely can be your own, but rather is some random copy&paste stuff from anywhere.
otherwise you could not have accomplished that many different lines of code which don't do anything, sorry.
First is you need to add ui.bootstrap module to you ar angular app and pass $modal to your controller like this
var app = angular.module("appUserSchedule", ['ui.bootstrap']);
app.controller("CtrlUserSchedule", function($scope,$http,$location, $modal) {
Then you need to add modalContent.html. This displays the html inside modal window
This may not be the answer, but make sure showModal is receiving the correct arguments. Appears you are only sending $index.
Would have simply made this a comment, but not quite there on the reputation yet. :-)

Categories