Today i am creating a page with angular, in controller i am write some jquery Code for toggle div.
Now my question is , is it good practice to write DOM level code in controller or create a directive for this, but i am want such type of code again and again.
vm.getDetails = function (id, event) {
$('.more-row').slideUp(300);
$('.open-content').text("+");
if ($(event.currentTarget).hasClass('open-content')) {
$('.open-content').removeClass('open-content');
return;
}
service.getDetails(id, function (err, model, logs) {
if(err) return;
vm.model.items = model;
vm.model.logs = logs;
vm.model.payRunDetailId = id;
$(event.currentTarget).parent().parent().next().find('td').stop().slideToggle();
$(event.currentTarget).addClass('open-content');
$(event.currentTarget).text("-");
});
};
Its definitely really bad practice. The HTML should go to View or as you pointed out to directive. Controller should just manage the control flow of aplication.
Is this what you are looking for?
https://github.com/EricWVGG/AngularSlideables
there is also link to jsfiddle
Related
I have an angular app and I would like to add a background process that runs without any view. All it is doing is regularly call a webservice and do something about it, no UI.
A quick and dirty way is to simply put the code in window.onLoad. But should I be thinking of doing this the angular way? If so, do I put the code in a Service? How would I "start" this service initially?
You answered it yourself, service is the right choice, you can inject it in any controller you have on app, if that's not the case and you have no controller (or directive) then you can do it in angular.run
angular.module('lookMaMyModule').run(function(injectables){
do something fancy on run
})
Here's simple concept for you, it might have small problems, but you will get the idea.
angular.bootstrap2 = function(module, element, callback){
angular.bootstrap(module, element);
callback();
}
remove ng-app tag from html, and bootstrap app
If background process is ng module, then use angular.module('name').run(); else use self bootstraping technique
app.js
angular.module('name', ['deps']);
angular.bootstrap2(['name'], document.body, function(){
var process_1 = new MyServ();
process_1.start();
});
service.js
var MyServ = function(){
this.intervalId = 0;
this.start = function(){
this.intervalId = setInterval(function(){
console.log('executing');
}, 1000);
}
this.stop = function(){
setInterval.cancel(this.intervalId); //don't remember api, sorry :<
}
});
I think what you're looking for is
app.run()
this gets called when the angular app is starting
see https://docs.angularjs.org/api/ng/type/angular.Module
Hi im currenty using $route.reload to refresh the content of my controller Every time I update my Database. the problem is when updating huge list of data, Every Time I update my Database and run $route.reload my browser lose its ability to scroll up or down my browser, it works fine with smaller list of Data.
below is a sample of my code
$scope.Undone = function(id){
$scope.index = $scope.GetID ;
CRUD.put('/UndoJda/'+$scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
$route.reload();
});
}
Your best bet would be some sort of lazy loading/pagination. So in case it's a really large list, like in the tenths of thousands, it might even be a DOM rendering problem. Also, if that isn't the case, you should try using AngularJS's bind once(Available since 1.3), as well as track by which does not create a watcher for each object on the scope, in your template. Assuming you are using ngRepeat, let's say something like this:
...<ul>
<li ng-repeat="item in Items">
<b>{{item.name}}</b>
</li>
</ul>
Change that to something like the following, in case the data does not update often:
...<ul>
<li ng-repeat="item in Items track by $index">
<b>{{::item.name}}</b>
</li>
</ul>
As a side note, try to always have a dot in your model's name. $scope.Something.list, for eaxample. ("If you don't have a dot, you are doing it wrong" - Misko Hevery himself said this.).
When the data is huge, try to use $timeout and reload the page.
This would prevent very fast refreshes and will keep your page responsive.
$scope.Undone = function(id){
$scope.index = $scope.GetID ;
CRUD.put('/UndoJda/'+$scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
$timeout(function() {
$route.reload();
}, 200);
});
}
You can do it by using $interval
$interval(function() {
CRUD.put('/UndoJda/'+$scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
// Update scope variable
});
}, 2000);
and also don't use $route.reload();. because Angularjs supporting SPA (Single Page Application). if you using $route.reload();. Every time page will loading, So it's not good. you need just call the Service code in inside of interval.
First I would recommend removing usage of $route.reload(), your use case doesn't require the view re-instantiating the controller. Instead you should update the $scope variable that holds the collection of entities your presenting in the view. You will also want to consider adding UX features such as a loading indicator to inform the user about the long running task.
Something similar too the code below would achieve what your looking for. I am unaware of what your CRUD js object instance is, but as long as its Angular aware you will not need to use $timeout. Angular aware usually means non third party APIs, but you can use $q to assist in exposing third party ajax results to angular.
// angular module.controller()
function Controller($scope, EntityService) {
$scope.entityCollection = [];
$scope.loadingData = false; // used for loading indicator
// Something will initialize the entity collection
// this is however your already getting the entity collection
function initController() {
$scope.refreshCollection();
}
initController();
$scope.refreshCollection = function() {
$scope.loadingData = true;
EntityService.getEntitites().then(function(resp) {
$scope.entityCollection = resp;
$scope.loadingData = false;
});
}
$scope.Undone = function(id) {
$scope.index = $scope.GetID ;
CRUD.put('/UndoJda/' + $scope.index).then(function(response){
toastr.info('Jda has been activated.', 'Information');
$scope.refreshCollection();
});
}
}
// angular module.factory()
function EntityService($q, $http) {
return {
getEntitites: function() {
var deferred = $q.defer();
$http.post('/some/service/endpoint').then(function(resp) {
deferred.resolve(resp);
});
return deferred.promise;
}
}
}
I am not too sure I am taking the best or correct approach to this, but basically what I am tyring to do is call a function in a directive. The reason being is I'm trying to clear a scope out that is controlled in the directive. For reference I am using this https://github.com/darylrowland/angucomplete directive, and the desired effect is when I add the selected item, it would clear out of the input.
So what I am trying to do is after I add the selectedObject to my list where I am storing (the add function) I am calling a broadcast to the directive like so
$scope.$broadcast('angucomplete:clearInput');
So for reference, here is the entire add function that is called
$scope.addLesson = function(){
var skillCheck = true;
for(i=0;i<$scope.lessonsHere.length;i++){
if($scope.lessonsHere[i].id === $scope.testObj.originalObject.id ){
errorOffScreen("You cannot add the same Lesson more than once");
skillCheck = false;
}
}
if(skillCheck){
$scope.lessonsHere.push($scope.testObj.originalObject);
$scope.testObj = {};
$scope.$broadcast('angucomplete:clearInput');
}
}
So in the directive itself I just have
$scope.clearInput = function(responseData, str) {
console.log("hit!");
};
It seems it does not work, I am not sure if this is the right approach, but I found another directive that calls itself from the controller with a broadcast so I figured it must be a good starting point.
Would appreciate any help here, as I am a bit in the dark with this topic. Thanks for reading!
I am getting into AngularJS, and I've been trying to understand directives because they are pretty much mandatory if you want to work with the DOM (when using AngularJS, correct me if I'm wrong). So here is the scenario, I am trying to create a simple login system (I am actually using the MEAN stack - MongoDB, ExpressJS, AngularJS, NodeJS). I'm not too worried about security (or otherwise less than perfect code) because I am just trying to learn how to use the frameworks. Here is the relevant code:
MemberModule.js:
var MemberModule = angular.module('MemberModule', ['ui.bootstrap']);
MemberModule.controller('MemberListController', function ($scope, $html)) {
$scope.members = [];
$scope.newMember = {
done : false
};
$scope.doneFilter = { done : true };
$scope.notDoneFilter = { done : false };
//various methods...
});
MemberModule.directive('usernameDir', ['$interval', function($interval) {
function link(scope, element, attrs) {
var newMember,
timeoutId;
function updateUsername() {
element.text(scope.newMember.username);
}
scope.$watch(attrs.myCurrentTime, function(value) {
format = value;
updateTime();
});
element.on('$destroy', function() {
$interval.cancel(timeoutId);
});
// start the UI update process; save the timeoutId for canceling
timeoutId = $interval(function() {
UpdateTime(); // update DOM
}, 1000);
}
return {
link: link
};
});
MemberModule.directive('passwordDir', function () {
// The above name 'myDirective' will be parsed out as 'my-directive'
// for in-markup uses.
return {
restrict: 'E',
transclude: true,
scope: {
'sub' : '&ngSubmit'
},
template: 'home'
}
});
As you can see above, I created the main angular.module and called it MemberModule - which gets referenced in my HTML (I am using jade templates - so by HTML I mean layout.jade). After that I created the controller with its various methods that I need. Finally, I created the directives which is what I need help with. I am trying to assign a DOM input element (in a form) to an object attribute, and then redirect (or render) a jade template (home.jade).
The relevant form HTML ('index.jade'):
extends layout
block content
div.container(ng-controller="MemberListController", ng-init="setMembers( #{JSON.stringify(members)} )")
h1 Welcome
h2 Sign Up
form(novalidate, ng-submit="addNewMember()")
input( type="text", username-dir info="userdir")
br
input( type="password", password-dir info="passdir")
br
input( type="password" )
br
button.btn.btn-primary(class="sub", type="submit") Submit
h2 Adding...
span(username dir)
span(password dir)
I am just pasting what I have so far so you can see where I am at in terms of progress. I am fully aware that my code is not functional as is - I am just looking for some help in pointing out what needs to go where to accomplish my goal. I realize that the two directives (while trying to attain the same goal) are not using the same style of directive code - this is just because of where I am at in terms of trying things. Again, my goal is (specifically for the username and password):
I am trying to assign a DOM input element (in a form) to an object attribute, and then redirect (or render) a jade template (home.jade).
Thanks.
Big ups to Julian Hollmann (check comments):
"You don't need both directives at all. Just use ng-model (docs.angularjs.org/api/ng/directive/ngModel) to bind your scope data to the input elements. Then use ng-submit to call a function in the controller."
Bingo - thanks!
We have a thick client app using jQuery heavily and want to profile the performance of the code using firebug's console.profile API. The problem is, I don't want to change the code to write the profile statements. Take this example:
var search=function(){
this.init=function(){
console.log('init');
}
this.ajax=function(){
console.log('ajax');
//make ajax call using $.ajax and do some DOM manipulations here..
}
this.cache=function(){
console.log('cache');
}
}
var instance=new search();
instance.ajax();
I want to profile my instance.ajax method, but I dont want to add profile statements in the code, as that makes it difficult to maintain the code.
I'm trying to override the methods using closures, like this: http://www.novogeek.com/post/2010/02/27/Overriding-jQueryJavaScript-functions-using-closures.aspx but am not very sure how I can achieve. Any pointers on this? I think this would help many big projects to profile the code easily without a big change in code.
Here is the idea. Just run the below code in firebug console, to know what I'm trying to achieve.
var search=function(){
this.init=function(){
console.log('init');
}
this.ajax=function(){
console.log('ajax');
//make ajax call using $.ajax and do some DOM manipulations here..
}
this.cache=function(){
console.log('cache');
}
}
var instance=new search();
$.each(instance, function(functionName, functionBody){
(function(){
var dup=functionBody
functionBody=function(){
console.log('modifying the old function: ',functionName);
console.profile(functionName);
dup.apply(this,arguments);
console.profileEnd(functionName);
}
})();
console.log(functionName, '::', functionBody());
});
Now what I need is, if i say instance.ajax(), I want the new ajax() method to be called, along with the console.profile statements. Hope I'm clear with the requirement. Please improvise the above code.
Regards,
Krishna,
http://www.novogeek.com
If you only want to modify the single instance of "search" then this should work:
$.each(instance, function(name, method){
if (typeof method !== 'function') return;
instance[name] = function() {
console.profile(name);
var ret = method.apply(this, arguments);
console.profileEnd(name);
return ret;
};
});
I know this is from a long time ago but I wanted to add this in case other people find this answer. You can make anonymous/private functions work by adding a name to each one. The other comments mention doing it manually bit I wanted to explain how to:
$('.stuff').each(function() { ... });
to
$('.stuff').each(function workOnStuff() { ... });