$watch only triggering once - javascript

I'm using the smart table (http://lorenzofox3.github.io/smart-table-website/) for AngularJS, and I've created a flag called isReset that will trigger a table reload. This happens because I have a directive watching the flag and will run refresh when isReset is set, and after it's done refreshing, it will set the flag off again.
My problem is, when I set the flag, it runs the first time, but after monitoring the behavior of the flag, it seems like it is never set back to false. I tried manually setting the flag to false, but next time around the $watch did not even trigger. My code is as follows, it would be great if you can help me shed some light on the issue. The weirdest thing is, I have another place where I am using it the exact same way, and it works as intended.
JS
$scope.resetFilter = function() {
$scope.timestampFilter = "";
$scope.levelFilter = "";
};
$scope.getAPIServerLogs = function (tableState) {
$scope.isLoading = true;
ServerLog.get({
"serverType": "API",
"timestampFilter": $scope.timestampFilter,
"levelFilter": $scope.levelFilter,
"offset": tableState.pagination.start,
"limit": tableState.pagination.number,
"sortField": tableState.sort.predicate,
"order": tableState.sort.reverse ? "desc" : "asc"
}, function (response) {
$scope.isLoading = false;
$scope.serverlogs = response.data;
$scope.displayedserverlog = [].concat($scope.serverlogs);
tableState.pagination.numberOfPages = response.pages;
});
};
Directive
directives.directive('stReset', function () {
return {
require: '^stTable',
replace: false,
scope: {stReset: "=stReset"},
link: function (scope, element, attr, ctrl) {
scope.$watch("stReset", function () {
if (scope.stReset) {
// reset scope value
var tableState = ctrl.tableState();
tableState.pagination.start = 0;
tableState.sort.prediate = {};
tableState.search = {};
ctrl.pipe();
scope.stReset = false;
}
}, true);
}
};
HTML
<table st-table="displayedserverlog" st-safe-src="serverlogs" st-pipe="getAPIServerLogs"
class="table table-striped table-hover logtable">
<thead st-reset="isReset">
<tr>
<th st-sort-default="reverse" st-sort="timestamp" width="11%">Timestamp</th>
<th st-sort="logger" width="30%">logger</th>
<th st-sort="level" width="3%">Level</th>
<th st-sort="thread" width="11%">Thread</th>
<th st-sort="message" width="45%">Message</th>
</tr>
</thead>
<tbody ng-repeat="serverlog in serverlogs">
<tr ng-click="click(serverlog)" ng-class="{'tr-active':serverlog.isClicked, 'pointer danger':serverlog.exception}">
<td>{{serverlog.timestamp | date: 'yyyy-MMM-dd hh:mm:ss'}}</td>
<td>{{serverlog.logger}}</td>
<td>{{serverlog.level}}</td>
<td>{{serverlog.thread}}</td>
<td>{{serverlog.message}}</td>
</tr>
<tr ng-show="serverlog.isClicked">
<td colspan="6">
<div class="row">
<div class="col-md-12">
<div>{{serverlog.exception}}</div>
<pre><div ng-repeat="trace in serverlog.stacktrace track by $index" class="stacktrace">{{trace}}
</div></pre>
</div>
</div>
</td>
</tr>
</tbody>
<tfoot ng-hide="isLoading">
<tr>
<td colspan="10" class="text-center">
<div st-pagination="" st-items-by-page="50"></div>
</td>
</tr>
</tfoot>

This plunker simulates your problem: http://plnkr.co/edit/c8crhe9ZR44GQBJ2sqm6?p=preview (have a look at the console)
scope.$watch("flag", function(neww, old){
count ++;
console.log("inWatch " + count + ": " + neww + ', ' + old);
if (scope.flag === true) {
scope.flag = false;
}
});
Setting the flag to false in $watch basically means it will always be false (because: you modify the value --> $watch runs --> at the end of the function it sets the value to false --> value is false)

I have discovered a solution. Still not sure why it works, but I added
scope.$parent.$parent.isReset = false;
to the end of the directive, it works the way it is intended. However, replacing the existing
scope.stReset = false;
broke the other place I am using the directive. For now, I will do both. In the future when I'm smarter at AngularJS, I will revisit this issue. I hope this helps someone in the future so they don't waste 3 days trying to figure it out like I did.

try this.
var watcher = $scope.$watch('someScope', function(newValue, oldValue){
if(newValue === 'yourValue') {
watcher(); // stop this watch
}
});

Related

angularjs smart-table programmatically sort

I have a table set up using the smart-table plug in for AngularJS. Everything appears to work nicely. Rather than having the user click on the table header to trigger a sort, I'd like to programmatically trigger sorting from my Angular controller. I do not see a way of doing this in the documentation here:
http://lorenzofox3.github.io/smart-table-website/
Am I overlooking something?
Found this on JSFiddle, might help you: http://jsfiddle.net/vojtajina/js64b/14/
<script type="text/javascript" ng:autobind
src="http://code.angularjs.org/0.10.5/angular-0.10.5.js"></script>
<table ng:controller="SortableTableCtrl">
<thead>
<tr>
<th ng:repeat="(i,th) in head" ng:class="selectedCls(i)" ng:click="changeSorting(i)">{{th}}</th>
</tr>
</thead>
<tbody>
<tr ng:repeat="row in body.$orderBy(sort.column, sort.descending)">
<td>{{row.a}}</td>
<td>{{row.b}}</td>
<td>{{row.c}}</td>
</tr>
</tbody>
</table>
A quick hack i found on how to do this is by setting the table header st-sort property and then triggering a click() on that element
<tr>
<th id="myelement" st-sort="date" st-sort-default="reverse">Date</th>
...
</tr>
Then later:
setTimeout(function() {
document.getElementById('myelement').click()
},
0);
Here is the 'angular' way to do this. Write a directive. This directive will have access to the smart table controller. It will be able to call the controller's sort by function. We will name the new directive stSortBy.
The below HTML includes the standard smart table syntatic sugar. The only new attribute directive here is st-sort-by. That's where the magic will happen. It's bound to a scoped variable sortByColumn. This is a string value of the column to sort
<table st-sort-by="{{sortByColumn"}}" st-table="displayedCollection" st-safe-src="rowCollection">
<thead>
<tr>
<th st-sort="column1">Person</th>
<th st-sort="column2">Person</th>
</tr>
</thead>
</table>
<button ng-click="toggleSort()">Toggle sort columns!</button>
Here is the stSortBy directive that hooks into the st table controller
app.directive('stSortBy', function() {
return {
require: 'stTable',
link: function (scope, element, attr, ctrl) {
attr.$observe('stSortBy', function (newValue) {
if(newValue) {
// ctrl is the smart table controller
// the second parameter is for the sort order
ctrl.sortBy(newValue, true);
}
});
}
};
});
Here is the controller. The controller sets the sort by in it's scope
app.controller("MyTableWrapperCtrl", ["$scope", function($scope) {
$scope.sortByColumn = 'column2';
$scope.toggleSort = function() {
$scope.sortByColumn = $scope.sortByColumn === 'column2' ? 'column1' : 'column2';
// The time out is here to guarantee the attribute selector in the directive
// fires. This is useful is you do a programmatic sort and then the user sorts
// and you want to programmatically sort back to the same column. This forces a sort, even if you are sorting the same column twice.
$timeout(function(){
$scope.sortByColumn = undefined;
}, 0);
};
}]);

Dynamic ng-click does not call function

I wish to make a dynamic table in AngularJS, but the problem is ng-click does not call the function.
Here is the fiddle : fiddle
Here is the code :
General template :
<div class="box">
<dynamic-table></dynamic-table>
</div>
Directive template :
<table class="table table-striped">
<thead>
<tr>
<th ng-repeat="column in columns" ng-bind="column.label"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="content in data">
<td ng-repeat="column in columns">
<!-- Problem is here (column[function] displays 'displayCategory') -->
<a href ng-click="column[function]()">{{ content[column.field] }}</a>
</td>
</tr>
</tbody>
</table>
Directive code :
app.directive('dynamicTable', function () {
return {
restrict: 'E',
templateUrl:'/template/Directive/dynamic-table.html',
scope: true,
link: ['$scope', function($scope) {
$scope.updateCategory = function() {
console.log("WOW");
};
}]
};
});
When I display : column[function], it shows updateCategory. I don't understand why when I click on it, the function is not launched...
Have you got an idea ?
That's because column[function] returns a string, not a reference to the function itself. You should call the function directly, like:
<td ng-repeat="column in columns">
<!-- Problem is here (column[function] displays 'displayCategory') -->
<a href ng-click="updateCategory (column)">{{ column.field }}</a>
</td>
and inside the directive to have something like:
controller: ['$scope', function($scope) {
$scope.updateCategory = function(columnData) {
console.log(columnData.field);
};
}]
Check demo: JSFiddle.
First of all, you link function declaration is not correct:
link: ['$scope', function($scope) {
$scope.updateCategory = function() {
console.log("WOW");
};
}]
It is the format of controller function. Change it to:
link: function($scope) { ... }
Angular will do the injection for you.
Secondly, specify a dispatcher function on the scope. Inside the dispatcher, determine which function to call:
$scope.dispatcher = function (column) {
var fn = column.function;
fn && angular.isFunction($scope[fn]) && $scope[fn]();
};
And specify ng-click="dispatcher(column)" in the HTML.
Please see this fiddle as maybe it will suit your needs.
http://jsfiddle.net/tep78g6w/45/
link:function(scope, element, attrs) {
scope.updateCategory = function() {
console.log("WOW");
};
scope.doSomething = function(func) {
var test = scope.$eval(func);
if(test)
test();
}
}
}
Also, link function has parameters that are sent to it, this is not a place to use DI. Please see in the fiddle the correct approach. As far as the dynamically calling the function, I went with different approach and it works. The approach you took is not going to work because you need a way for the string to be a function, it needs to have a reference to a function.

Set values after creating a JSON object Angular.js

I am having some trouble setting some values for a widget I am making. I am using Ozone widget framework, but that part is negligible to this discussion. Here us the html where I am trying to set the variable (for now just focus on {{user.user}} part.
<div class="col-lg-12">
<p>User: {{user.user}}</p>
<table class="table table-bordered table-hover table-striped">
<thead>
<th>#</th>
<th>Model</th>
<th>Score</th>
<th>Date</th>
</thead>
<tr data-ng-repeat=" item in records | orderBy : '-score' | limitTo : 10 " ng-click="">
<td>{{$index+1}}</td>
<td>{{item.model}}</td>
<td>{{item.score}}</td>
<td>{{item.date}}</td>
</tr>
</table>
</div>
And here is the Angular / owf to go with it:
angular.module('myapp', ['cgOwf'])
.controller('MainCtrl', function($scope, $http, owf) {
var records;
$scope.selectPost = '';
$scope.searchText = '';
console.debug("before IT HERE!!!");
owf.ready(function(){
console.debug("MADE IT HERE!!!");
owf.Eventing.subscribe('user-status', function(sender, msg, channel) {
console.debug('[%s] - received message %o', channel, msg);
$scope.user = msg;
});
});
$scope.search = function() {
//clear the select, go here http://jsonplaceholder.typicode.com/comments
//and display/filter emails based on the search input
$scope.selectPost = "";
$scope.selectedItem = null;
$http.get('https://api.myjson.com/bins/1jvst').success(function(data2) {
$scope.records = [];
data2.forEach(function(r) {
if (r && r.user && r.user.toLowerCase().indexOf($scope.searchText.toLowerCase()) !== -1) {
$scope.records.push(r);
}
});
});
};
});
The part I am having trouble with is $scope.user = msg;. At that point in the code, msg is a JSON object, and I am sure of that because it checks out in the js debugger in chrome. AFAIK that is how I would set the object so I could access it in the html, though something clearly doesn't work.
The owf event probably isn't triggering a $digest cycle, so the view never updates. You can run $scope.apply() to force a $digest
owf.Eventing.subscribe('user-status', function(sender, msg, channel) {
console.debug('[%s] - received message %o', channel, msg);
$scope.$apply(function() {
$scope.user = msg;
});
});

Where should I write general purpose controller function in angular.js?

I am writing some functions for check/uncheck all for table list and it is working fine,
Controller is,
invoiceApp.controller('itemController', ['$scope', 'itemService', '$route', function ($scope, itemService, $route) {
$scope.checkAllItem;
$scope.listItem = {
selected: []
};
$scope.checkUncheck = function () {
if ($scope.checkAllItem) {
$scope.listItem.selected = $scope.items.map(function (item) {
return item.id;
});
} else {
$scope.listItem.selected = [];
}
};
HTML TABLE,
<table id="dt_basic" class="table table-bordered table-hover" width="100%">
<thead>
<tr>
<th class="text-center" width="5%">
<input type="checkbox" name="checkbox-inline" ng-model="checkAllItem" ng-click="checkUncheck()">
<input type="checkbox" name="checkbox-inline" ng-click="uncheckAll()">
</th>
<th width="15%" ng-click="sort()">Name<i class="fa fa-sort small"></i></th>
<th width="65%">Description</th>
<th width="5%">Unit</th>
<th width="10%">Rate</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items" data-toggle="modal" data-target="#itemModel" ng-click="getItem(item.id)" style="cursor: pointer">
<td class="text-center">
<input type="checkbox" checklist-model="listItem.selected" checklist-value="item.id">
</td>
<td><a>{{item.name}}</a></td>
<td>{{item.description}}</td>
<td>{{item.unit}}</td>
<td>{{item.rate}}</td>
</tr>
</tbody>
</table>
It is working fine,Here my problem is,In my project I have many tables in different pages,I have to copy past this same code (Talking about Controller only ) to everywhere.Is there any method to write it generally?
I tried with $routescope,
but It is not working with ng-model,Is there any method to implement the same?
You could turn it into a service then inject the service to whichever controller needs it. You can now also include other commonly used functions used to manipulate data in those. See,
http://jsbin.com/madapaqoso/1/edit
app.factory("toolService", function(){
return {
checkUncheck: function(listItem) {
listItem.selected = [];
}
}
});
I didn't add the added complexity of your function, but you get the idea.
Alternatively, use a directive. I show it in the jsbin as well. Though, I'd prefer a service since services are made for managing data and directives are more concerned with DOM editing and binding $watchers/events etc. Or perhaps you could persist the data with a service, then use a custom directive to handle all the clicks on the table.
I have written a custom directive
invoiceApp.directive('checkUncheck', function () {
return {
restrict: 'E',
replace: true,
template: '<input type="checkbox" name="checkbox-inline" ng-model="checkAllItem" ng-click="checkUncheck()">',
link: function (scope) {
//check/uncheck and delete
scope.checkAllItem;
scope.listItem = {
selected: []
};
scope.checkUncheck = function () {
if (scope.checkAllItem) {
scope.listItem.selected = scope.items.map(function (item) {
return item.id;
});
} else {
scope.listItem.selected = [];
}
};
}
};
});
In HTML,
<check-uncheck></check-uncheck>
Now I can share checkUncheck function with most of table view in my project.

How do I use `console.log` in conjunction with knockout and subscribe with Knockoutjs?

I'm using Knockoutjs for the first time and I'm having trouble debugging because of my inability to log variables in console. I can see that my JS is loading properly in console, when I enter:
Home.TwitterFeedComponent I see an object returned. How do I use console.log in conjunction with knockout and subscribe?
var Home = Home || {};
var inheriting = inheriting || {};
Home.TwitterFeedComponent = function(attributes) {
if (arguments[0] === inheriting)
return;
Home.OnScreenComponent.call(this, attributes);
var component = this;
var recent_tweets = ko.observableArray();
var url = 'https://twitter.com/search.json?callback=?';
this.attributes.twitter_user_handle.subscribe(function(value) {
var twitter_parameters = {
include_entities: true,
include_rts: true,
from: value,
q: value,
count: '3'
}
result = function getTweets(){
$.getJSON(url,twitter_parameters,
function(json) {
console.log(json)
});
}
console.log(twitter_parameters);
});
};
Home.TwitterFeedComponent.prototype = new Home.OnScreenComponent(inheriting);
Home.TwitterFeedComponent.prototype.constructor = Home.TwitterFeedComponent;
I don't see the problem in your code, but if you want to log 'Observables', you have to log it as follows:
console.log(observableVar());
I'm a little unclear as to the exact scope of the question -- however, if like mine, this question is directed toward the use of console.log within your HTML.
Here is a little set of code that may help:
<div class="tab-content" data-bind="with: ClientSearch.selectedClient">
...
<table class="table table-striped table-condensed table-hover">
<thead></thead>
<tbody>
<!-- ko foreach: { data: _general, as: 'item' } -->
<tr>
<td data-bind="text: eval( 'console.log(\' le item \', item)' )"></td>
</tr>
<!-- /ko -->
</tbody>
</table>
...
</div>
This code simply logs an item inside of a foreach to the console.
Hope this helps!

Categories

Resources