I'm familiar with using something like:
$scope.gotoBottom = function(){
$location.hash('bottom');
$anchorScroll();
}
and this works.. yet what I'm seeing is an issue when retrieving data that's being used in an ng-repeat and attempting to resize when that data comes in.
Example (in controller):
users.get({userList:$routeParams.conversationId}, function(data){
$scope.userList = data;
$scope.gotoBottom();
})
The gotoBottom method is firing to fast, while the ng-repeat is looking on $scope.userList and buidling it's table based off that.
I want to be able to toggle gotoBottom after that list has been remade (or whenever it's modified). Is there a better way to achieve this?
Thank you!
You can use $watch listener to fire gotoBotton when an AngularJs variable change.
$scope.ActivityList = new Array();
$scope.$watch("ActivityList", function () {
$scope.$evalAsync(function () {
$scope.DoSomething();
});
}, true);
$scope.DoSomething = function () {
$(function () {
//$scope.gotoBottom();
});
};
Also you can run scrolling bottom after page is loaded
angular.element($window).bind('load',
function() {
var element = document.getElementById("messages-list").lastElementChild;
element.id = "bottom";
/*-------------*/
$location.hash('bottom');
$anchorScroll();
}
Related
When I trigger deleteQuestion() a second time 2 questions get deleted. Any idea? Let me know if you need to see more of my code.
controller.js
crtPromoCtrl.controller('surveyCtrl', ['$scope', 'surveySrv', function($scope, surveySrv)
{
$scope.questions = surveySrv.getQuestions();
$scope.editQuestion = function(index)
{
surveySrv.setEditQuestion(index);
};
$scope.deleteQuestion = function(index)
{
$(document).off('click', '#confirmationModal #confirm');
$('#confirmationModal').modal('show');
$(document).on('click', '#confirmationModal #confirm', function()
{
surveySrv.deleteQuestion(index);
$scope.$apply();
});
};
}]);
service.js
crtPromoSrv.service('surveySrv', function()
{
var questions = [];
var editQuestion;
this.getQuestions = function()
{
return questions;
};
this.addQuestion = function(question)
{
questions.push(question);
};
this.setEditQuestion = function(index)
{
editQuestion = questions[index];
};
this.getEditQuestion = function()
{
return editQuestion;
};
this.clearEditQuestion = function()
{
editQuestion = undefined;
};
this.deleteQuestion = function(index)
{
questions.splice(index, 1);
console.log(questions);
};
});
EDIT: I'm thinking it's an event propagation thing, since when I have 5 q's it deletes #2 and #3 when I delete #2.
EDIT: Fixed, see controller.js code.
It appears you are adding the 'click' function to your #confirmationModal #confirm button multiple times. The first time $scope.deleteQuestion is called, it adds the function. The second time you call it, it adds it again so when it is clicked, the function is called twice.
A simple fix would be to unbind the 'click' event before adding it again. Something like this: $('#confirmationModal #confirm').off('click');
The better solution here is to not use jQuery at all for these event bindings. Using a simple Angular modal directive (like the one provided in the Angular-UI library, for instance) would be the correct way to do this. Then you can just have an ng-click on the button and never have this problem.
I would like to edit accordion header (formationName) and once I click on the refresh button, it should update the accordion header. I couldn't figure out how to approach the problem.
$("#refresh").click(function(){
myData.offsetFormations[0]["FormationName"]="party";
json = JSON.stringify(myData);
alert( json );
});
fiddle: http://jsfiddle.net/xg7cr0g4/76/
It depends on your scenario. If there is a ton of data being refreshed often, you would want to do an in place edit.
If you just refresh when clicked and the data is insubstantial, just rebuild the table the way you originally did. An example of this (although it's not quite working):
http://jsfiddle.net/xg7cr0g4/78/
var build = function(){
//...build the grid/accordion here on demand (load,reload,programmatically)
};
var refresh = function(){
//update json
build(); //rebuild;
};
$(document).ready(function(){
// build on load
build();
});
jQuery UI accordion needs to be destroyed on rebuild:
re-initialize jquery accordion on callback
Here is the working fiddle:
http://jsfiddle.net/xg7cr0g4/79/
You can also organize your code better and avoid global functions by following the module pattern:
var Grid = function(){
var self = this;
this.build = function(){
...
};
this.rebuild = function(){
...
};
this.init = function(){
...
this.build();
$('#refresh').on('click', this.refresh);
...etc
};
$(this.init);
};
var grid = new Grid();
grid.rebuild();
To answer your other question, just add an additional method:
this.setHeader = function(header){
// similar to rebuild, change the json using param
x[...] = header;
this.build();
};
I'm trying to create a simple click catcher where if you click .image-class the javascript will take the href from another element with a class name of .btn and send you to it's destination. Though I keep getting errors on lines 7 & 10 saying that undefined is not a function. How do I make this work?
<script>
var ClickCatcher=
{
init:function(){
var link = jQuery('.btn')[1].href;
var imgCatch = jQuery('.image-class');
imgCatch.addEventListener("click", ClickCatcher.clickListener, false);
},
clickListener:function(){
window.location = link;
}
};
ClickCatcher.init();
</script>
You can do this with jquery with a simple click event
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
But if you still want to write in the way you have you can do:
var ClickCatcher = {
init: function () {
jQuery('.image-class').on('click', function (){
window.location = jQuery('.btn').eq(1).attr('href');
});
}
};
ClickCatcher.init();
Just make sure to fire the init method after dom load.
update: One issue with it is that you have coded your target etc in the code rather then pass it, so its going to be hard to reuse, you'd be better off doing:
var ClickCatcher = {
init: function ($button, loc) {
$button.on('click', function (){
window.location = loc;
});
}
};
ClickCatcher.init(jQuery('.image-class'), jQuery('.btn').eq(1).attr('href'));
That way the internal working is seperate from the dom (as you are passing the dom dependencies to the function.
#atmd showed a very good way of doing this. If you just want to know what your mistake was though. It is wa an error in your jQuery stament to get the btn href
jQuery('.btn')[1].href
you need to call the attr function and then get the href attr. and use .eq(1) to reduce the set to the first btn
jQuery('.btn').eq(1).attr('href);
Having some issues with a function in Knockout.js. Basically it is a menu where the first menu item "Översikt" should fetch a JSON array and populate the view.
The knockout code:
self.ongoingAuctions = ko.observableArray([]);
self.getOngoingAuctions = function(data) {
$.getJSON("assets/json/auctions.json", function(data) {
self.ongoingAuctions(data);
});
}
My click binding:
The problem is that this only works the first time I click on the menu item. The JSON doesn't get fetched the second, third, n:th time.
What am I doing wrong? Or have I misunderstood something?
Thanks in advance!
I have shared this fiddle for you that shows something else is wrong in your code that you havent not specified in the question:
It makes the call to the non existing json (in my case) every time you click
JS Fiddle to working code
var viewModel = function(){
var self = this;
self.ongoingAuctions = ko.observableArray([]);
self.getOngoingAuctions = function(data) {
$.getJSON("assets/json/auctions.json", function(data) {
self.ongoingAuctions(data);
});
}
self.setHeadline = function(){
console.log('set headline')
}
self.headline = function(){
console.log('headline');
}
}
var myVm = new viewModel();
ko.applyBindings(myVm);
Let's say I have the following code:
$(function () {
$(".buy-it-now.ribbon").click(function () {
$(".bid-to-beat.ribbon.active").removeClass("active");
$(".bid-to-beat.ribbon").addClass("inactive");
$(".buy-it-now.ribbon.inactive").removeClass("inactive");
$(".buy-it-now.ribbon").addClass("active");
$(".bid-now").hide();
$(".buy-now").show();
$(".add-to-cart").hide();
})
$(".bid-to-beat.ribbon").click(function () {
$(".buy-it-now.ribbon.active").removeClass("active");
$(".buy-it-now.ribbon").addClass("inactive");
$(".bid-to-beat.ribbon").removeClass("inactive");
$(".bid-to-beat.ribbon").addClass("active");
$(".buy-now").hide();
$(".bid-now").show();
$(".add-to-cart").show();
});
});
It is a simple function that allows for multiple UI related things to happen on the front-end of a site I am working on. I am fairly (very) new to jQuery and JavaScript in general and am learning about refactoring and making my code more condensed now. The way I currently write code is sort of line per thought I have. So my question is how would an experienced developer write this same code? Or rather, how could I refactor this code?
Try the following:
$(function () {
var $handlers = $('.buy-it-now.ribbon, .bid-to-beat.ribbon');
$handlers.click(function() {
$handlers.toggleClass("active inactive");
var $elements = $(".bid-now, .add-to-cart"),
$buyElement = $(".buy-now");
if($(this).is('.buy-it-now.ribbon')) {
$elements.hide();
$buyElement.show();
} else {
$elements.show();
$buyElement.hide();
}
});
});
This question would be better suited for codereview, but yes it can be condensed a little using method chaining.
$(function () {
$(".buy-it-now.ribbon").click(function () {
$(".bid-to-beat.ribbon").removeClass("active").addClass("inactive");
$(".buy-it-now.ribbon").removeClass("inactive").addClass("active");
$(".bid-now").hide();
$(".buy-now").show();
$(".add-to-cart").hide();
})
$(".bid-to-beat.ribbon").click(function () {
$(".buy-it-now.ribbon").removeClass("active").addClass("inactive");
$(".bid-to-beat.ribbon").removeClass("inactive").addClass("active");
$(".buy-now").hide();
$(".bid-now").show();
$(".add-to-cart").show();
});
});
You could condense it further by pre selecting the elements and caching them in variables before the click events as long as no elements are added or removed during the life of the page.
As your code it is you can combine some of the selectors into a single line. And also because your elements looks to be static you can cache them into a variable and use them later as it reduces the number of times a element is looked up in the DOM reducing the accessing time..
Also you can limit the scope of these variables or selectors by encasing them in an object or a closure..
Maybe something in these lines..
$(function () {
cart.init();
});
var cart = {
elems : {
$buyRibbon : null,
$bidRibbon : null,
$bidNow: null,
$buyNow: null,
$addToCart: null
},
events : {
},
init : function() {
this.elems.$buyRibbon = $(".buy-it-now.ribbon");
this.elems.$bidRibbon = $(".bid-to-beat.ribbon");
this.elems.$bidNow = $(".bid-now") ;
this.elems.$buyNow = $(".buy-now") ;
this.elems.$addToCart = $(".add-to-cart") ;
this.events.buyClick();
this.events.bidClick();
}
};
cart.events.buyClick = function() {
cart.elems.$buyRibbon.on('click', function(){
cart.elems.$bidRibbon.removeClass('active').addClass('inactive');
cart.elems.$buyRibbon.removeClass('inactive').addClass('active');
cart.elems.$bidNow.hide();
cart.elems.$buyNow.show();
cart.elems.$addToCart.hide();
});
}
cart.events.bidClick = function() {
cart.elems.$bidRibbon.on('click', function(){
cart.elems.$buyRibbon.removeClass('active').addClass('inactive');
cart.elems.$bidRibbon.removeClass('inactive').addClass('active');
cart.elems.$bidNow.show();
cart.elems.$buyNow.hide();
cart.elems.$addToCart.show();
});
}
So basically in here your whole cart is a object ..And the cart has different properties which are related to this.. You follow the principles of object oriented programming here..
Using closures I heard gives you better design limiting the scope of your code..
Might I suggest something like this:
$(function () {
var buyNowButton = $('buy-it-now.ribbon'),
bidToBeatButton = $('.bid-to-beat.ribbon'),
buyNowEls = $('.buy-now'),
bidToBeatEls = $('.bid-now,.add-to-cart');
var toggleButtons = function(showBuyNow){
buyNowButton.toggleClass('active', showBuyNow);
bidToBeatButton.toggleClass('active', !showBuyNow);
buyNowEls.toggle(showBuyNow);
bidToBeatEls.toggle(!showBuyNow);
}
buyNowButton.click(function(){ toggleButtons(true) });
bidToBeatButton.click(function(){ toggleButtons(false) });
});
You could save a some lines by removing the selectors at the start and just do the selection in place, if the saved space would be more important than the minor performance hit. Then it would look like this:
$(function () {
var toggleButtons = function(showBuyNow){
$('buy-it-now.ribbon').toggleClass('active', showBuyNow);
$('.bid-to-beat.ribbon').toggleClass('active', !showBuyNow);
$('.buy-now').toggle(showBuyNow);
$('.bid-now,.add-to-cart').toggle(!showBuyNow);
}
$('buy-it-now.ribbon').click(function(){ toggleButtons(true) });
$('.bid-to-beat.ribbon').click(function(){ toggleButtons(false) });
});
The first version selects the elements once and holds them in memory; the second selects them each time the button is clicked. Both solve the problem I believe would occur with the selected answer where clicking the same button twice would cause the .active and .inactive classes to get out of sync with the shown/hidden elements.