AngularJS video issue - angularjs

I am studying AngularJS by looking at the website http://campus.codeschool.com/courses/shaping-up-with-angular-js/contents and downloaded the videos, then going through the examples on my computer.
Everything went well until the video codeschool_1329.mp4, otherwise called "Shaping_Up_With_Angular_JS_Level_2b". The example works correctly when the logic for selecting the panels is in the HTML code, but when the logic is moved to a controller, it no longer works correctly. Thus I have the relevant HTML code:
<section ng-controller="PanelController as panel">
<ul class="nav nav-pills">
<li ng-class="{active:panel.isSelected(1)}">
<a href ng-click="panel.selectTab(1)">Description</a>
</li>
<!-- Repeated for Specifications and Reviews -->
</ul>
</section>
<div class="panel" ng-show="panel.isSelected(1)">
<h4>Description</h4>
<p>{{product.description}}</p>
</div>
<!-- Repeated for Specifications and Reviews -->
and for the JavaScript code I have:
app.controller('PanelController', function(){
this.tab = 1;
this.selectTab = function(setTab){
this.tab = setTab;
};
this.isSelected = function(checkTab){
return this.tab === checkTab;
};
});
exactly as in the video. The latter is with the Angular module and another Angular controller for the store.
With both Google Chrome and Firefox, when I click on the each of the tabs "Description", "Specifications" and "Reviews", the selected tab is highlighted, as in the video, albeit blue rather than dark purple, but the text that is supposed to be displayed below the selected tab does not show up at all. It looks as if there is some type of a problem with the isSelected function in PanelController with ng-show="panel.isSelected(1)", etc. in the lower part of the HTML code, although it appears to work correctly with ng-class="{active:panel.isSelected(1)}" when the tab is highlighted.
This works correctly when the logic for this is in the HTML code, as I said above, but no matter what I can do, I am unable to get this to work correctly when the logic is in PanelController.
There must be something simple that I am missing, and would be grateful to get this sorted out - many thanks.

<section ng-controller="PanelController as panel">
...
</section>
<div class="panel" ng-show="panel.isSelected(1)">
Only the section element is controlled by the panel controller, but you're trying to use panel.isSelected(1) out of that section. So that can't work.
Put the div inside the section, or wrap everything with another div and move ng-controller="PanelController as panel"to that wrapping div.

Related

Page render speed in AngularJS using ng-include

I've got an issue where I populate a page in pieces. There are an arbitrary number of categories with an arbitrary number of items. The code is generally something like the below (warning, transposed).
$scope.getItems = function(key) {
$http.get('get-items?key=' + key)
.then(function(res) {
for (let item of res.data) {
$scope.categories[item.category].items.push(item);
}
});
}
let populateCategories = function() {
for (let key in $scope.categories) {
$scope.getItems(key);
}
}
$scope.getCategories = function(next) {
$http.get('get-categories')
.then(function(res) {
$scope.categories = res.data;
next();
});
$scope.getCategories(populateCategories);
}
The idea is to first get what categories will be on the page, and render them, empty (but w/ a busy icon). After that, hit and endpoint one time per category and populate w/ the results. The busy icon is shown via ng-show & a boolean pointing to the size of the items. 1 or more items = no busy icon, an the items should show.
The loading of the categories more or less works. Populating them though, is not so free flowing. Watching the console output, it takes ages for the browser to render. The busy icon goes away somewhat quickly, but I don't see the items until a bunch of them are ready.
Worth noting, (I think) I saw this problem appear when I moved the html that displays each item from a single file, an template and used ng-include, as I'm using it on two different places. Surely that would not be a cause would it?
EDIT: Adding the html - simplified
item-template.html
<div class="row">
<div class="col-xs-2 col">
<img src="{{item.img}}">
</div>
<div class="col-xs-10 col">
<div>{{item.details}}</div>
</div>
</div>
list.html
<body>
<div class ="container-fluid">
<div class="row">
<div ng-repeat="(key, value) in categories">
<div>{{key}}</div>
<div ng-show="value.busy"">
<img ng-src="{{busy_image}}">
</div>
<div ng-repeat="item in value.items track by $index">
<!-- This in fact seems to be the culprit -->
<div ng-include="item-template.html">
</div>
</div>
</div>
</div>
</body>
So, playing around, if I simply paste the contents of template.html into list.html, the response is much, much better. Looking at this issue, the solution seems to be to use a cache service. I'm happy to use something like that but I'm still curious as to why. The template I'm using isn't small (166 lines) but I can't imagine it being that heavy either on a modern computer.
Several things from the top of head:
amount of items to be shown in the HTML. Large lists with x properties = alot of Angular watchers.
if there are alot of items, maybe check for an alternative to ng-repeat
instead of ng-include item-template.html, create a Component
ng-repeat with track by
use bind once
in this case you can replace ng-show with ng-if

Expanding tiles layout using Angular in responsive layout

I'm developing an Angular single-page app with a responsive layout. The current page I'm working on uses a tile-based layout that auto wraps extra tiles to the next row. The HTML is below and, in the responsive layout, it can show 1, 2, or 3 tiles per row depending on the width of the row (it positions them using floats).
<div class="tiled_panel">
<div class="tile_1">...</div>
<div class="tile_2">...</div>
<div class="tile_3">...</div>
<div class="tile_4">...</div>
<div class="tile_5">...</div>
...
</div>
Now each tile will be given a "Learn More" button. The design calls for a block to expand between the row of the selected tile and the row below. This block will be the full width of the row and will be closed when the user closes it or clicks on a different Learn More button.
My first thought was to arrange the HTML as below using ng-if to hide or display the expander divs but I can't figure out the CSS needed to have it display between the rows.
<div class="tiled_panel">
<div class="tile_1">...</div>
<div class="expander_1">...</div>
<div class="tile_2">...</div>
<div class="expander_2">...</div>
<div class="tile_3">...</div>
<div class="expander_3">...</div>
<div class="tile_4">...</div>
<div class="expander_4">...</div>
<div class="tile_5">...</div>
<div class="expander_5">...</div>
...
</div>
My second thought, since I'm using Angular, was to somehow use transcluding or including to insert the relevant HTML into the appropriate spot. Again, I can't figure out how to identify where I need to insert the HTML?
I've tried searching around for other people's solutions to similar problems since I figured its not that unusual a requirement but I haven't yet been able to find anyone else who is doing this.
Can anyone else suggest what I need to do to identify the where to insert the HTML or how to generate the CSS? Or even suggest another solution that I haven't considered yet?
Although I have 70% understand of what you mean, I think ng-class can simply solve what you've faced. The solution code is like below. And I setup jsfiddle.
Html looks like this.
<div ng-app="" ng-controller="MyCtrl">
<div class="tiled_panel">
<div>Tile 1 <a href ng-click="change_tile(1)">More</a></div>
<div class="expander" ng-class="{'close': opentile===1}">Expander 1<a href ng-click="change_tile(-1)">Close</a></div>
<div>Tile 2 <a href ng-click="change_tile(2)">More</a></div>
<div class="expander" ng-class="{'close': opentile===2}">Expander 2<a href ng-click="change_tile(-2)">Close</a></div>
</div>
</div>
Your controller code looks like this.
$scope.change_tile = function(value){
$scope.opentile = value;
}
$scope.change_tile(0);
Css looks like this.
.expander{
visibility: hidden
}
.close{
visibility: visible
}

Why does dynamically creating togglable tabs cause duplicate ngSwitchWhen divs in AngularJS?

I'm using Bootstrap and AngularJS to dynamically create clickable tabs in HTML. More specifically, I'm using the ng-switch and ng-switch-when directives to control which tab's content is displayed at any given time.
PLEASE NOTE: Below you'll notice I use a controller for DOM manipulation. I realize the "AngularJS way" to perform DOM manipulation is with directives (and not controllers). I'm not one to deliberately violate good coding practices, but since I'm trying to understand this exact issue I've stumbled across while learning about controllers and directives, I'm asking you to only consider using a controller for now since that's what I believe may be contributing to the issue.
If you keep reading you'll notice I've already found a directive-based solution (i.e., the "AngularJS way"), but I'm specifically asking about the issue that occurs when using a controller and ng-switch-when.
The Problem:
The issue I'm facing is as follows. When I append div tags (containing a ng-switch-when directive) onto a container, and then compile these tags using $compile, somehow the content of each tag using ng-switch-when becomes duplicated. To keep this question from becoming too long, I've placed the idea behind what the code's expected behavior is in the comments of the first JSFiddle below, which demonstrates the issue.
JSFiddle: What I have now -- Uses controller for DOM manipulation ("non-AngularJS way"), results in unexpected duplication of tags using ng-switch-when.
An in-depth description of how to reproduce the duplication is as follows. Upon creating a new tab, it appears that the previously clicked tab's content (within the div containing ng-switch-when) is duplicated. You can see this happen by loading the above JSFiddle, right-clicking the Default text, selecting Inspect element, and then pressing Analysis > Create Tab in the tabbed pane. You'll see there are now two div tags with ng-switch-when="Default" instead of just one. If you continue to create new tabs, this pattern continues for view1, view2, and so on. Alternatively, just click Analysis > Create Tab 5 times and then look at the duplicates in Tabs 1-4. Either way, you'll see the previously selected tab's contents are doubled whenever a new tab is created.
Frustrated by this unexpected behavior, I went ahead and implemented a directive-based solution using AngularUI's Bootstrap components, which creates custom directives for tab components, to gain the desired functionality.
JSFiddle: What I want to happen in the above JSFiddle -- Uses directives for DOM manipulation ("AngularJS way").
However, as I've mentioned, I want to know why my first JSFiddle is not working. See below for formal question.
Points to consider:
The issue may be related to scope, the order the scripts are loaded, or possibly a logic error.
Perhaps it's something AngularJS simply cannot do. I'm aware that compound transclusion no longer works as of v1.2, and this issue may be related.
So my question is as follows:
Why is this duplication occurring? Is there a way to prevent/workaround the duplicates from being created? Remember, I wouldn't use this in practice, I'm just curious if this issue can be solved at all.
PLEASE NOTE: I am not looking to resolve this using jQuery, such as this jQuery solution. Although I'm aware AngularJS uses a lighter version of jQuery called jqLite under the hood, I'm looking for a "pure AngularJS" solution.
HTML:
<!-- Include CDNs (see JSFiddle above) -->
<!-- Wrap in HTML tags, include DOCTYPE -->
<body ng-app="myApp">
<div class="panel panel-default" ng-controller="myCtrl">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active">
<a class="h4">My App</a>
</li>
<li class="dropdown">
<a href="" class="dropdown-toggle h4" data-toggle="dropdown">
Analysis
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>
<button id="createTabLink" class="h4" ng-click="createTabClick()">Create Tab</button>
</li>
</ul>
</li>
</ul>
</div>
<div class="panel-body">
<div class="panel panel-default">
<div class="panel-heading">
<ul id="createTabDiv" class="nav nav-tabs"></ul>
</div>
<div class="panel-body">
<div id="viewWrapper" ng-switch="" on="viewSelected">
<div id="Default" ng-switch-when="Default">Default</div>
</div>
</div>
</div>
</div>
</div>
</body>
JS:
angular.module('myApp', []);
function myCtrl($scope, $compile) {
$scope.count = 0;
$scope.viewSelected = "Default";
$scope.tabClick = function($event) {
var tab = $event.target;
var viewName = tab.getAttribute("value");
$scope.viewSelected = viewName;
};
$scope.createTabClick = function() {
$scope.count++;
var viewName = "view" + $scope.count;
var createTabDiv = angular.element(document.querySelector('#createTabDiv'));
var viewWrapper = angular.element(document.querySelector('#viewWrapper'));
var $comp1 = createTabDiv.append('<li class="active"><a class="h4" value="' + viewName + '" ng-click="tabClick($event)">Tab ' + $scope.count + '</a></li>');
$compile($comp1)($scope);
var $comp2 = viewWrapper.append('<div id="' + viewName + '" ng-switch-when="' + viewName + '">' + viewName + '</div>');
$compile($comp2)($scope);
$scope.viewSelected = viewName;
};
}

Hide a div after user selects one option

i did not found the answer i was looking for, so that why i'm creating this question. I have a div that shows another one on click but i need to hide it (the div that appears on click) after the user selects one of the options, how can i do that?
When user's click on "#showmenu" the div ".mob" appears, after clicking in one of the ".mob" li's the ".mob" div dissapears.
P.S: Sorry for my bad english.
HTML:
<div id="showmenu"><img src="images/mobile.png" /></div>
<div class="mob" style="display: none;">
<ul>
<a data-scroll href="#home"><li>INÍCIO</li></a>
<a data-scroll href="#servicos"><li>EU FAÇO</li></a>
</ul>
</div>
Code:
$(document).ready(function() {
$('#showmenu').click(function() {
$('.mob').slideToggle("fast");
});
});
I dont have any idea of the bootstrap or jquery plugins you are using but based on what is given, i'd say this should work.
$(document).ready(function() {
$('#showmenu').click(function() {
$('.mob').slideToggle("fast");
$('.mob a').click(function () {
$('.mob').slideToggle("fast");
});
});
});
Point to note there is a performance issue here , i.e code could be optimized better to search for classes or elements within a specific div than the whole document
Should those really be <a> if you don't intend to go anywhere? Can they just be <li>?
After that I think you want to give id's to the <li> that you have and then create a function much like the one you did for $('showmenu').click... that would hide the .mob.
Or if the <li> all get treated the same, maybe give them all the same class and just have one function for the class.

Where to put menu when using angular-snap?

I'm using snap.js with AngularJS using the angular-snap.js directive.
https://github.com/jtrussell/angular-snap.js
I'm also using Andy Joslin's angular-mobile-nav.
I'm wondering where I should store the code for the menu:
<snap-drawer>
<p>I'm a drawer! Where do I go in the angular code?</p>
</snap-drawer>
Because this isn't a unique page within the angular-mobile-nav, I'm currently putting the on every page and just using a directive that contains all my menu code/html.
Seems like this could be inefficient as it is loading a new directive on each page, right? Any idea on how to do this better?
Thanks!
So this is what I've done (I also use angular-mobile-nav and angular-snap.js).
This is my HTML Code
<body ng-app="MyApp">
<div snap-drawer>
<ul class="list">
<li ng-repeat="item in sidebar.items" ng-i18next="{{item.name}}" ng-tap="snapper.close();go(item.link)"></li>
</ul>
</div>
<div id="container" snap-content snap-options="snapOpts">
<div mobile-view=""></div>
</div>
</body>
please note that go() is the function to change the page and that I'm using ng-i18next to translate my items. Also ng-tap is a directive which listens for touch events instead of mouse events. With Angular >1.1.5 there's a mobile module, so my ng-tap directive won't be needed anymore.
And by using $rootScope I can put items in the sidebar:
$rootScope.sidebar = {
items: [
{
name: 'item_one',
link: 'page_one'
},
...
]
};
So if you want to change the items in the sidebar, simply override $rootScope.sidebar (not $scope.sidebar) in your controller ;)
If you don't like two animations happen at the same time, you could write a function, which waits for the sidebar to close and then change the page. It could look like this:
$rootScope.waitThenGoTo = function (page, time) {
time = time || 200;
$timeout(function () {
$navigate.go(page);
}, time);
};
If you have still question, please comment. I'll try to update this answer as soon as possible.

Resources