I have a page with blocks of information that you can toggle off and on. In order to know which blocks you would like shown I stored their state in an Angular cookie.
HTML:
<block-toggle-btn block-ref="{{ id }}" climbnodes="3" mytarget=".block__content"></block-toggle-btn>
Angular Directive:
myApp.directive('blockToggleBtn', ['$cookies', function($cookies) {
return {
restrict: 'E',
template:'-',
link: link,
scope: {
blockRef: '@',
mytarget: '@', // target .cssclass
climbnodes: '=' // Used to climb the dom
}
};
function link(scope, element, attr) {
var parent = element.parent();
for (var i = 0; i < scope.climbnodes; i++) {
parent = parent.parent();
}
var blockRef = scope.blockRef;
var blockToggleCookie = $cookies.get(blockRef);
scope.toggleVisibility = function() {
element.toggleClass('invisible');
parent.find(scope.mytarget).slideToggle();
if (element.hasClass('invisible')) {
element.html('+');
blockVisible = 'invisible';
scope.setBlockToggleCookie(blockRef, blockVisible);
} else {
element.html('-');
blockVisible = 'visible';
scope.setBlockToggleCookie(blockRef, blockVisible);
}
};
scope.setBlockToggleCookie = function(blockRef, blockVisible) {
var expireDate = new Date();
expireDate.setTime(2144232732000);
$cookies.put(blockRef, blockVisible, {'expires': expireDate});
};
// If cookie is set keep block closed
if (blockToggleCookie == 'invisible') {
scope.toggleVisibility();
}
element.bind('click', function(event) {
event.preventDefault();
scope.toggleVisibility();
});
}
}]);
This works fine for now, but it sets a cookie for each "block" of information I am showing. I need to, instead, move it all to a single cookie that contains information on which blocks to show and which to hide.
I know this sounds like a big question, and it is, but I figured it was more useful to post the whole thing rather than each portion I am struggling through so that I can better understand and put it all together. Currently, I have been down a rabbit hole of trying to figure out how to create an array of objects to even store in an Angular $cookies.putObject
. I am getting strange results when I try to .push()
into an array of objects. For example with this function I tried to call from the click event:
scope.buildCookieObj = function(blockRef, blockVisible) {
scope.cookieItems.push({blockRef: blockRef, blockVisible: blockVisible});
};
I know this isn't a code writing service and I have really tried to take this step by step but have found myself lost in the weeds. I am new to Angular and knowing when it use it or insert JQuery is becoming a bit overwhelming. So even any advice on the direction to take this in is welcome.
EDIT:
I think I have made some progress (although it is late so I'm not quite 100% this is working but I think so). This is updated code setting the cookie object. Now I just have to retrieve and iterate over it to determine (from the cookie) which blocks should initially be set to closed.
myApp.directive('blockToggleBtn', ['$cookies', function($cookies) {
var cookieItems = [];
return {
restrict: 'E',
template:'-',
link: link,
scope: {
blockRef: '@',
mytarget: '@', // target .cssclass
climbnodes: '=' // Used to climb the dom
}
};
function link(scope, element, attr) {
var parent = element.parent();
for (var i = 0; i < scope.climbnodes; i++) {
parent = parent.parent();
}
scope.toggleVisibility = function() {
element.toggleClass('invisible');
parent.find(scope.mytarget).slideToggle();
if (element.hasClass('invisible')) {
element.html('+');
blockVisible = 'invisible';
scope.buildCookieObj(blockRef, blockVisible);
} else {
element.html('-');
blockVisible = 'visible';
scope.buildCookieObj(blockRef, blockVisible);
}
};
scope.buildCookieObj = function(blockRef, blockVisible) {
// If that item already exists remove so we can replace with new visibility setting
$.each(cookieItems, function(i){
if(cookieItems[i].blockRef === blockRef) {
cookieItems.splice(i,1);
return false;
}
});
cookieItems.push({blockRef: blockRef, blockVisible: blockVisible});
scope.setBlockToggleCookie(cookieItems);
};
scope.setBlockToggleCookie = function(cookieItems) {
// Maximum value for cookie expiration, i.e. the closest thing to
// "never expire" is the year 2038. See:
// http://stackoverflow.com/questions/532635/javascript-cookie-with-no-expiration-date/532638#532638 and
// http://stackoverflow.com/questions/34586494/make-cookies-never-expire-with-angularjs
var expireDate = new Date();
expireDate.setTime(2144232732000);
$cookies.putObject('personalization', cookieItems, {'expires': expireDate});
};
element.bind('click', function(event) {
event.preventDefault();
scope.toggleVisibility();
});
}
}]);
Further progress, except the opposite really:
Realized I have an additional problem setting the cookie in that it gets overwritten every time you reopen the app and toggle blocks off and on. Still trying to think through the logic on that. If I name it something dependent on the time or session or what not, not sure how I would then retrieve that to iterate over it.
As for the retrieval of the cookie and setting the corresponding blocks to be toggled on or off I have done this to retrieve the values:
var personalizationCookie = $cookies.getObject('personalization');
console.log(personalizationCookie);
$.each(personalizationCookie, function(key, value) {
var blockRef = value.blockRef;
var blockVisible = value.blockVisible;
console.log(blockRef, blockVisible);
});
But I haven't figured out how to target the blocks. I thought perhaps just simple Jquery .find()
but I think the dom isn't yet rendered so it isn't finding anything. Wrapping it in a $( document ).ready()
isn't working I'm guessing because we have a loading screen that would be "loaded" even if the dom elements I am looking for still aren't. But that might be something specific to our app I have to ask initial builders. Just figured I'd update here with all the info.
EDIT #2: So I have come up with a solution that works and I thought about "answering" my question here but the solution I came up with still needs some work. All the logic is in the directive and I know that's not where it should be. That said I wanted to post it here in case it was helpful to anyone, and also because I am working on moving all the logic into services and would welcome any direction with that as well. (I've already been down the service vs factory question hole and that's daunting enough without even beginning the actual task!)
myApp.directive('blockToggleBtn', ['$cookies', function($cookies) {
var personalizationCookie = $cookies.getObject('personalization');
return {
restrict: 'E',
template:'-',
link: link,
scope: {
blockRef: '@',
mytarget: '@', // target .cssclass
climbnodes: '=' // Used to climb the dom
}
};
function link(scope, element, attr) {
var parent = element.parent();
for (var i = 0; i < scope.climbnodes; i++) {
parent = parent.parent();
}
var blockRef = scope.blockRef;
scope.toggleVisibility = function() {
element.toggleClass('invisible');
parent.find(scope.mytarget).slideToggle();
if (element.hasClass('invisible')) {
element.html('+');
blockVisibility = 'invisible';
scope.buildCookieObj(blockRef, blockVisibility);
} else {
element.html('-');
blockVisibility = 'visible';
scope.buildCookieObj(blockRef, blockVisibility);
}
};
scope.buildCookieObj = function(blockRef, blockVisibility) {
// If that item already exists remove so we can replace with new visibility setting
$.each(personalizationCookie, function(i){
if(personalizationCookie[i].blockRef === blockRef) {
personalizationCookie.splice(i,1);
return false;
}
});
personalizationCookie.push({blockRef: blockRef, blockVisibility: blockVisibility});
scope.setBlockToggleCookie(personalizationCookie);
};
scope.setBlockToggleCookie = function(personalizationCookie) {
// Maximum value for cookie expiration, i.e. the closest thing to
// "never expire" is the year 2038. See:
// http://stackoverflow.com/questions/532635/javascript-cookie-with-no-expiration-date/532638#532638 and
// http://stackoverflow.com/questions/34586494/make-cookies-never-expire-with-angularjs
var expireDate = new Date();
expireDate.setTime(2144232732000);
$cookies.putObject('personalization', personalizationCookie, {'expires': expireDate});
var personalizationCookie = $cookies.getObject('personalization');
};
// Check to see if there's a cookie for this element's block
// If yes, then toggle minus to plus and hide the block content the button refers to
$.each(personalizationCookie, function(key, value) {
if ((value.blockRef == scope.blockRef) && (value.blockVisibility == 'invisible')) {
parent.find(scope.mytarget).hide();
element.html('+').addClass('invisible');
}
});
element.bind('click', function(event) {
event.preventDefault();
scope.toggleVisibility();
});
}
}]);