Skip to content

Nav Controller #378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 98 additions & 39 deletions src/js/components/navbars.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@
* It uses scrollable areas to avoid scroll issues. In the following figure you can
* see the difference between fixed navbars and navbars with absolute positioning.
*
* <figure class="full-width-figure">
* <img src="/assets/img/figs/fixed-overflow.png" alt=""/>
* <figure class='full-width-figure'>
* <img src='/assets/img/figs/fixed-overflow.png' alt=''/>
* </figure>
*
* Here is the basic markup to achieve this.
*
* ``` html
* <div class="app">
* <div class="navbar navbar-app navbar-absolute-top">
* <div class='app'>
* <div class='navbar navbar-app navbar-absolute-top'>
* <!-- ... -->
* </div>
*
* <div class="navbar navbar-app navbar-absolute-bottom">
* <div class='navbar navbar-app navbar-absolute-bottom'>
* <!-- ... -->
* </div>
*
* <div class="app-body">
* <div class='app-body'>
* <ng-view></ng-view>
* </div>
* </div>
Expand All @@ -53,24 +53,25 @@
* Consider the following example:
*
* ``` html
* <div class="navbar navbar-app navbar-absolute-top">
* <div class='navbar navbar-app navbar-absolute-top'>
*
* <div class="navbar-brand navbar-brand-center">
* <div class='navbar-brand navbar-brand-center'>
* Navbar Brand
* </div>
*
* <div class="btn-group pull-left">
* <div class="btn btn-navbar">
* <div class='btn-group pull-left'>
* <div class='btn btn-navbar'>
* Left Action
* </div>
* </div>
*
* <div class="btn-group pull-right">
* <div class="btn btn-navbar">
* <div class='btn-group pull-right'>
* <div class='btn btn-navbar'>
* Right Action
* </div>
* </div>
* </div>
*
* ```
*
* `.navbar-brand-center` is a specialization of BS3's `.navbar-brand`. It will
Expand All @@ -85,48 +86,106 @@

var module = angular.module('mobile-angular-ui.components.navbars', []);

/**
* @directive navbarAbsoluteTop
* @restrict C
* @description
*
* Setup absolute positioned top navbar.
*
* ``` html
* <div class="navbar navbar-app navbar-absolute-top">
* <!-- ... -->
* </div>
* ```
*/
var global;
Copy link
Owner

@mcasimir mcasimir Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's quite too generic to be understood, perhaps a more specific name would be better, also global is kind of reserved namespace in node.js, that makes it even more confusing.

What is it supposed to hold, why u need a singleton for that?

I've still to read the rest but that looks like a very disruptive approach.

We'll fix it anyway i like your idea of supporting common patterns.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not my directive. It was there already.

On Sep 7, 2016 2:28 PM, "Maurizio Casimirri" notifications@github.com
wrote:

In src/js/components/navbars.js
#378 (comment)
:

@@ -85,48 +86,127 @@

var module = angular.module('mobile-angular-ui.components.navbars', []);

That's quite too generic to be understood. What is it supposed to hold,
why u need a singleton for that?

I've still to read the rest but looks like a disruptive approach. We'll
fix it anyway i like your idea of supporting common patterns.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/mcasimir/mobile-angular-ui/pull/378/files/ccdae045247c4f5e44c42d1cbd5319eb3fcb252f#r77907410,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADDzgxsXu_wrzZBpkfuSbTUdmL5AuQU6ks5qnyx0gaJpZM4Jx8nS
.

Copy link
Owner

@mcasimir mcasimir Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a code review, the comment is bound to a line on the diff. So each comment is specific to one line.

I was referring to var global specifically.

A name like var globalNavbarControllerSingleton would have been better. Long names are not bad, although the usually feel strange.

Abbreviations and generic names can be not obvious. Immagine i didn't even know what that line was related to. That may happen to someone editing your code after you.. in order to understand what is var global there for he/she should have read and understood all the rest of the code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... Ya I didn't like using that particular scope. But if you look at why
it is there either I am missing something important or it is just not
possible another way.

On Sep 7, 2016 3:08 PM, "Maurizio Casimirri" notifications@github.com
wrote:

In src/js/components/navbars.js
#378 (comment)
:

@@ -85,48 +86,127 @@

var module = angular.module('mobile-angular-ui.components.navbars', []);

Its a code review, the comment is bound to a line on the diff. So each
comment is specific to one line.

I was referring to var global specifically.

A name like var globalNavbarControllerSingleton would have been better.
Long names are not bad, although the usually feel strange.

Abbreviations and generic names can not be that obvious. Immagine i didn't
even know what that line was related to. That may happen to someone editing
your code after you.. in order to understand what is var global there for
he/she should have read and understood all the rest of the code.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/mcasimir/mobile-angular-ui/pull/378/files/ccdae045247c4f5e44c42d1cbd5319eb3fcb252f#r77913520,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADDzgwhQ2TwLIXy5xd3c_xWKlkYFOzR8ks5qnzXMgaJpZM4Jx8nS
.


/**
* @directive navbarAbsoluteBottom
* @restrict C
* @description
*
* Setup absolute positioned bottom navbar.
*
* ``` html
* <div class="navbar navbar-app navbar-absolute-bottom">
* <!-- ... -->
* </div>
* ```
*/
/**
* @directive navbarAbsoluteTop
* @restrict C
* @description
*
* Setup absolute positioned top navbar.
*
* ``` html
* <div class='navbar navbar-app navbar-absolute-top'>
* <!-- ... -->
* </div>
* ```
*/

/**
* @directive navbarAbsoluteBottom
* @restrict C
* @description
*
* Setup absolute positioned bottom navbar.
*
* ``` html
* <div class='navbar navbar-app navbar-absolute-bottom'>
* <!-- ... -->
* </div>
* ```
*/
angular.forEach(['top', 'bottom'], function(side) {
var directiveName = 'navbarAbsolute' + side.charAt(0).toUpperCase() + side.slice(1);
module.directive(directiveName, [
'$rootElement',
function($rootElement) {
return {
restrict: 'C',
link: function(scope) {
controller: function() {
this.registerController = function(name, ctrl) {
this[name] = ctrl;
};
},
link: function(scope, elem, attr, ctrl) {
$rootElement.addClass('has-navbar-' + side);
scope.$on('$destroy', function() {
$rootElement.removeClass('has-navbar-' + side);
});
if (!global) global = ctrl;
Copy link
Owner

@mcasimir mcasimir Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No one liners without brackets pls, although this one would be a ligit one for conciseness.

if (!global) {
  global = ctrl;
}

I'd favor this form

global = global || ctrl;

global.registerController(side, new (function() {
this.hide = function() {
$rootElement.removeClass('has-navbar-' + side);
elem.addClass('ng-hide');
};
this.show = function() {
elem.removeClass('ng-hide');
$rootElement.addClass('has-navbar-' + side);
};
this.slideout = function() {
$rootElement.removeClass('has-navbar-' + side);
elem.addClass('ng-hide');
};
this.slidein = function() {
elem.removeClass('ng-hide');
$rootElement.addClass('has-navbar-' + side);
};
})());
}
};
}
]);
});

/**
* @directive [hide|show|slidein|slideout][Top|Bottom|Both][Nav]
* @restrict C
* @description
*
* Toggle each(or both) nabar(s) with or without animations
*
* ``` html
* <view class='[hide|show|slidein|slideout]-[top|bottom|both]-[nav]'>
* <!-- ... -->
* </view>
* ```
*/
angular.forEach(['Top', 'Bottom', 'Both'], function(nav) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a directive? I mean, i kinda got that you are invoking the controller while linking on a view element, so perhaps you like to react to page changes (ng-view?), so when you navigate on some areas of your app you don't want to see the nav, while some other has it.

In that case, but also in general i suggest to re-approach the problem splitting it into 2 distinct phases:

    1. easily allow to hide, show the navbars (the slide part is unnecessary cause is better and easier achieved and customized via css)
    1. provide an easy way (if that's what you wanna achieve) to hide/show the navbar on certain pages (or when you navigate somewhere, or just the first time you open the app... let's say ..bound to the location)

Currently what you achieved with 2 directives could be done with a few css classes. Let's discuss outside the PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1) easily allow to hide, show the navbars (the slide part is unnecessary cause is better and easier achieved and customized via css)

I can agree with that.

Currently what you achieved with 2 directives could be done with a few css classes. Let's discuss outside the PR.

I don't think this is right though. The styles that fix the navbars are spread among classes at multiple node levels. I didn't really analyze the merit of that approach, because I assumed that there was a good reason for doing it(though I would have preferred if the height of the navbars could have been controlled by the font-size of the the nested copy). So you see in order to "eliminate" the styles at every level the controller has to know of all styles it is responsible for.

Now I did, at one time, use a hacky css override like, I am assuming, you suggest. But that, of course, made it unnecessarily cumbersome to switch between states when the view state changes.

This is much more versatile.

Copy link
Author

@ballenjr ballenjr Sep 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just remembered how I used to do it. So applying the override, selectively, outside of the ng-view required extra global scope and/or ui-state tracking. So I had to use a combination of kludgy ng-if's and ui-state changes to get it to work. This is not only less than ideal but it is severely limited in the scope of its usefulness.

angular.forEach(['hide', 'show', 'slidein', 'slideout'], function(method) {
var name = method + nav + 'Nav';
module.directive(name, function() {
return {
restrict: 'C',
link: function(scope, elem, attr) {
if (global)
if (nav === 'Both') {
global.bottom[method]();
global.top[method]();
} else
global[nav.toLowerCase()][method]();
}
};
});
});
});
})();
93 changes: 89 additions & 4 deletions test/unit/components/navbars.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ describe('components', function() {

describe('navbarAbsoluteTop', function() {
it('adds class has-navbar-top if navbarAbsoluteTop is present', function() {
let elem = angular.element('<div class="navbar-absolute-top" />');
let elem = angular.element('<div class="navbar-absolute-top" nav-controller />');
Copy link
Owner

@mcasimir mcasimir Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's cheating! :D

That kind of coverage is useless, coverage is just a number after all, and unit tests requires isolation to be reliable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I didn't think that it would be very useful either. I was just using the
other test as a guide.

On Sep 7, 2016 2:22 PM, "Maurizio Casimirri" notifications@github.com
wrote:

In test/unit/components/navbars.spec.js
#378 (comment)
:

@@ -21,15 +21,15 @@ describe('components', function() {

 describe('navbarAbsoluteTop', function() {
   it('adds class has-navbar-top if navbarAbsoluteTop is present', function() {
  •    let elem = angular.element('<div class="navbar-absolute-top" />');
    
  •    let elem = angular.element('<div class="navbar-absolute-top" nav-controller />');
    

That's a nice trick :D

That kind of coverage is useless, coverage is just a number after all, and
unit tests requires isolation to be reliable.

Try to write a test suite for your feature that works independently from
the rest.

Or leave it as it was and i'll try to cover it.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/mcasimir/mobile-angular-ui/pull/378/files/ccdae045247c4f5e44c42d1cbd5319eb3fcb252f#r77906493,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADDzgw1gTJQQaYoHcGATnBh5u1VZH_6Oks5qnysbgaJpZM4Jx8nS
.

compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).toContain('has-navbar-top');
});

it('removes class has-navbar-top if navbarAbsoluteTop is destroyed', function() {
let elem = angular.element('<div class="navbar-absolute-top" />');
let elem = angular.element('<div class="navbar-absolute-top" nav-controller />');
compile(elem)(scope);
scope.$digest();
scope.$destroy();
Expand All @@ -39,20 +39,105 @@ describe('components', function() {

describe('navbarAbsoluteBottom', function() {
it('adds class has-navbar-bottom if navbarAbsoluteBottom is present', function() {
let elem = angular.element('<div class="navbar-absolute-bottom" />');
let elem = angular.element('<div class="navbar-absolute-bottom" nav-controller />');
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).toContain('has-navbar-bottom');
});

it('removes class has-navbar-bottom if navbarAbsoluteBottom is destroyed', function() {
let elem = angular.element('<div class="navbar-absolute-bottom" />');
let elem = angular.element('<div class="navbar-absolute-bottom" nav-controller />');
compile(elem)(scope);
scope.$digest();
scope.$destroy();
expect($rootElement.attr('class')).not.toContain('has-navbar-bottom');
});
});

describe('hideTopNav', function() {
it('hides the navbarAbsoluteTop when present within the nested view', function() {
let value = '<div><top class="navbar-absolute-top" nav-controller />';
value += '<ng-view><div class="hide-top-nav"></div></ng-view></div>';
let elem = angular.element(value);
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).not.toContain('has-navbar-top');
expect(elem.find('top').attr('class')).toContain('ng-hide');
});
});

describe('hideBottomNav', function() {
it('hides the navbarAbsoluteBottom when present within the nested view', function() {
let value = '<div><bottom class="navbar-absolute-bottom" nav-controller />';
value += '<ng-view><div class="hide-bottom-nav"></div></ng-view></div>';
let elem = angular.element(value);
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).not.toContain('has-navbar-bottom');
expect(elem.find('bottom').attr('class')).toContain('ng-hide');
});
});

describe('slideoutBottomNav', function() {
it('hides the navbarAbsoluteBottom when present within the nested view', function() {
let value = '<div><bottom class="navbar-absolute-bottom" nav-controller />';
value += '<ng-view><div class="slideout-bottom-nav"></div></ng-view></div>';
let elem = angular.element(value);
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).not.toContain('has-navbar-bottom');
expect(elem.find('bottom').attr('class')).toContain('ng-hide');
});
});

describe('slideoutTopNav', function() {
it('hides the navbarAbsoluteTop when present within the nested view', function() {
let value = '<div><top class="navbar-absolute-top" nav-controller />';
value += '<ng-view><div class="slideout-top-nav"></div></ng-view></div>';
let elem = angular.element(value);
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).not.toContain('has-navbar-top');
expect(elem.find('top').attr('class')).toContain('ng-hide');
});
});

describe('hideBothNav', function() {
it('hides both navbarAbsoluteTop && navbarAbsoluteBottom when present within the nested view', function() {
let value = '<div><bottom class="navbar-absolute-bottom" nav-controller />';
value += '<top class="navbar-absolute-top" nav-controller /><ng-view>';
value += '<div class="hide-both-nav"></div></ng-view></div>';
let elem = angular.element(value);
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).not.toContain('has-navbar-bottom');
expect($rootElement.attr('class')).not.toContain('has-navbar-top');
expect(elem.find('bottom').attr('class')).toContain('ng-hide');
expect(elem.find('top').attr('class')).toContain('ng-hide');
});
});

describe('slideoutBothNav', function() {
it('hides both navbarAbsoluteTop && navbarAbsoluteBottom when present within the nested view', function() {
let value = '<div><bottom class="navbar-absolute-bottom" nav-controller />';
value += '<top class="navbar-absolute-top" nav-controller /><ng-view>';
value += '<div class="slideout-both-nav"></div></ng-view></div>';
let elem = angular.element(value);
compile(elem)(scope);
scope.$digest();

expect($rootElement.attr('class')).not.toContain('has-navbar-bottom');
expect($rootElement.attr('class')).not.toContain('has-navbar-top');
expect(elem.find('bottom').attr('class')).toContain('ng-hide');
expect(elem.find('top').attr('class')).toContain('ng-hide');
});
});

});
});