angular.js app with linked panels - javascript

I am learning Angular and i am ok with the basics however i have a situation where i have 3 interlinking panels or panes. You select the category from the first pane, it populates a list of products in the second pane then choosing one of these populates the third pane with the product details.
I have mocked up the structure here: https://jsfiddle.net/pbpxexsa/1/
Whats the best approach for constructing this?
I added some routing to have a meaningfull url, but i can only have one ng-view.
I have looked at ui.router and this looks like it might fit
Could i just have a separate controller on each pane and an observer to watch for changes but i have read this is to be avoided.
I like the aspect of directives and understand i could provide <product-pane></product-pane> and <product-details></product-details> directives but again not sure how to link them.
The books i am reading don't seem to cover this kind of architecture, am i missing something obvious?
Thanks in advance.

I think you might be over-thinking this slightly. You have 3 panels which are all viewing a single data group, but you are trying to engineer a solution that would only be necessary when each panel is showing different data. You can easily do this design with a single controller.
Just a generic mockup (not working or tested, but should give you an idea):
<div class="category-pane">
<ul>
<li ng-repeat="category in categories"
ng-click="setSelectedCategory(category)">{{category.name}}</li>
</ul>
</div>
<div class="products-pane">
<ul>
<li ng-repeat="product in selectedCategory.products"
ng-click="setSelectedProduct(product)">{{product.name}}</li>
</ul>
</div>
<div class="product-details-pane>
{{selectedProduct.name}}
{{selectedProduct.info}}
....
</div>
In controller:
$scope.setSelectedCategory= function(category){
$scope.selectedCategory = category;
$scope.selectedProduct = null; //remove whatever was in the product pane
};
$scope.setSelectedProduct= function(product){
$scope.selectedProduct = product;
};

Related

When is transclusion appropriate?

Case in point, I am developing a multi-select control in Angular2, similar in functionality to Select2 or a multitude of other controls.
I started by defining what I want the user interface to look like in terms of defining what's included in the dropdown, and came up with two options.
One is to use #Input()s for the options:
<my-multi-select [options]="options"></my-multi-select>
...and then within the template for my-multi-select:
<div class=option" *ngFor="let option of options">{{option.display}}</div>
Ahother is to use transclusion, which is how material2 appears to do it:
<my-multi-select>
<my-option *ngFor="let option of options" [value]="option"></my-option>
</my-multi-select>
...and then within the template for my-multi-select:
<div class=select-container">
<ng-content select="my-option"></ng-content>
</div>
I was content with the transclusion option, but then when I started to actually implement it rand in to difficulty binding the events coming from my-option to my-multi-select. I could try to figure out a way to notify my-select of things that are happening in my-option, like using an Observable, or digging deeper in to using an #Output event -- but that feels like trying to jam a square peg in to a round hole when #Input variables might just be simpler.
This led me to the question, is transclusion even appropriate here? And the bigger question, when is transclusion appropriate, and when is using transclusion jamming a square peg in to a round hole?
For your example, it's simple to compare the two aproaches as you are only including one text item as part of the transclusion. This makes using #Input() trivial and may well be the best solution.
However, imagine a scenario where you have multiple elements you want to include in your child component, each with custom HTML tags along for the ride. Using trasnclusion this is trivial, but using #Input() it requires a few "hacks" to get right and isn't very maintainable or extendable.
Explanation
Building on from this Todd Motto blog about transclusion as a reference, we can utilise transclusion to have more complex HTML for our title and content sections without issue.
Transculsion Parent
<my-component>
<my-title>
<h1>This is the Component title!</h1>
</my-title>
<my-content>
And here's some awesome content.
<ul>
<li>First</li>
<li>Second</li>
</ul>
</my-content>
</my-component>
Transclusion Child
<div class="my-component">
<div>
Title:
<ng-content select="my-title"></ng-content>
</div>
<div>
Content:
<ng-content select="my-content"></ng-content>
</div>
</div>
Now imagine this same scenario using only #Input() to declare our elements.
Our binding from the parent isn't very friendly, we definitely don't want to do this for more complex scenarios.
In our child component we have to use [innerHTML] to get around interpolation encoding in Angular. Again, this isn't very easy to extend or maintain. This is where in my opinion transclusion really excels.
Input Parent
<my-component
[my-title]="<h1>This is the Component title!</h1>"
[my-content]="And here's some awesome content.<ul><li>First</li><li>Second</li></ul>">
</my-component>
Input Child
<div class="my-component">
<div [innerhtml]="'Title:' + my-title"></div>
<div [innerhtml]="'Content:' + my-content"></div>
</div>
This led me to the question, is transclusion even appropriate here?
If want html to look like:
<my-multi-select>
<my-option *ngFor="let option of options" [value]="option"></my-option>
</my-multi-select>
Where my-multi-select and my-option are components, then transclusion is the way to do it.

AngularJS: Add Focus behavior to list items similar to `select-option`

I have a html list with some text items. I need to bring the list item into focus when user type the initial letters of it's text similar to select-option when user type initial letter of it's options; matching option is brought into focus. I need to have same behavior for list. I am rendering my list using ng-repeat directive of angular:
<ul class="list-group m-3 col">
<li ng-repeat="song in ctrl.songs | orderBy" class="list-group-item list-group-item-action" tabindex="{{$index}}">
<span>{{song}}</span>
</li>
</ul>
I googled for a plugin/library but unfortunately couldn't find any. I can write a directive for this but time does not permit it. So can anyone please tell me if there's any library/plugin available? Here's the plunkr.
After spending quality time with jQuery and tabindex of bootstrap I finally made it working.
function bringToFocus(text) {
var item = element.children().filter(function(child) {
return angular.element(this).text().trim().toLowerCase().startsWith(text.toLowerCase());
}).first();
item.focus();
}
Here's the working plunkr. Hope it might be helpful for someone.

Bootstrap collapse not working when using dynamic id

I have an issue when creating dynamic id for bootstrap collapse.
First when I click on any topic which is in ng-repeat it should collapse and give me the question list.
The problem here is when I click on the first topic it collapse but when i click on second topic the first topic collapse is getting the second topic question list data
Html:
<div class="topic-div">
<p class="topic-heading">Topics</p>
</div>
<div class="arrow-down"></div>
<ul>
<li ng-repeat="topics in oJdDetails.topics" class="topic-li" ng-click="fngetQList(topics,$index)">
<p class="topics-p"> {{topics}}</p>
<ul uib-collapse="isCollapsed">
<li ng-repeat="value in aQuestionList">{{value.quesList.quesListName}}</li>
</ul>
</li>
</ul>
Js:
$scope.fngetQList = function(topics, index) {
debugger;
$scope.isCollapsed = true;
$scope.displayQList = true;
$scope.sTopics = topics;
$scope.index = index;
getCandidateInterviewListService.fnGetQList(topics).then(function(response) {
$scope.aQuestionList = response;
console.log($scope.aQuestionList);
});
};
I don't understand how to make it work. Any help would be highly appreciated.
It seems you are using plain bootstrap library which will not work with angularjs.
Try with https://angular-ui.github.io/bootstrap/#/collapse
Try avoid using jQuery with Angular especially for Bootstrap. Angular's UI team has written the Angular wrapper for Bootstrap.
http://angular-ui.github.io/bootstrap/
Use this library instead. You don't need the jQuery for this. http://angular-ui.github.io/bootstrap/#/collapse
This uses the Bootstrap's CSS file and the Javascript part is provided by the Angular so you don't need to use jQuery for it.
Angular in itself is a big enough Javascript library so don't bloat your application by installing jQuery as well.

Clicking Links to Change Displayed Content

General Layout
I'm working on a tool for work to help streamline some things, but I've never been good at best practices and wanted to make sure I did this right.
I want the site to load with only column A visible. Click a link in column A, column B is generated with proper links. Click a link in column B, column C is generated. Click a different link in column A, column B is replaced with the new proper links (and if column C was already visible, get rid of it completely).
My current idea is having a clever div structure so I can add or remove a "hidden" class using jQuery on all children. Each column is a floated div with styled links. I'm not sure if the cleanest way to do this would just be having a parent for each potential section, then labeling each possibility with a class for a selector. That way I could use the jQuery children().addClass("Hidden") on the parent container for each section that I've defined to come after the one the link was clicked in.
I'm not sure I have access at work to a MySQL database, and I can't put company information on my personal sandbox website, so I think I'm stuck with javascript for the most part. Can anyone guide me in the right direction?
The code below is incomplete, just wanted to give an idea of what I was already thinking. i.e.
//This would be part of function modemBrands when Arris is passed, currently using if else statements for each option.
$('.ModemMods').children().addClass("Hidden");
$(".Arris").removeClass("Hidden");
~
<div class="NavCon">
<nav>
<!-- Brand options -->
<div class="LinkCon">
Arris
Motorola
SMC
Ubee
</div>
<div class="ModemMods">
<div class="LinkCon Arris">
DG860A
DG1670
DG860A
DG1670
</div>
<div class="LinkCon Motorola Hidden">
DG860A
DG1670
DG860A
DG1670
</div>
<div class="LinkCon SMC Hidden">
DG860A
DG1670
</div>
<div class="LinkCon Ubee Hidden">
DG860A
DG1670
</div>
</div>
<div class="ModemRegion">
</div>
</nav>
</div>
Am I on the right track? Should I just have the function tied to links in column A add the Hidden class to all of the parent containers for columns B or C each time I click it? Or is there a more elegant way to do this?
You should not use onclick. Use addEventListener (vanilla JS) or .click() (jQuery).
Why? Separation of concerns. Your HTML is for content and layout. Your CSS is for styling. Your JS is for scripting.
MySQL is not a programming language. It is a database system. You can either hard code in the values, store it in a JSON file somewhere, or use a database and a server side scripting language to load it from the DB.
Try separating data from your layout. For example:
.
var motorola = {name: "Motorola", modems: ["ABC123", "BCD123", "XYZ123"]};
var cisco = {name: "Cisco", modems: ["MEOW123", "LOL123", "STACKOVERFLOW"]};
var vendors = [motorola, cisco];
for (var i in vendors) {
$link = $("<a>").text(vendors[i].name);
$link.attr("data-name", i);
$link.click(vendorClickHandler);
}
function vendorClickHandler(e) {
var vendorName = $(this).attr("data-name");
var vendor = vendors.filter(function(t) { return t.name === vendorName })[0]; // basically, search vendors for an elem with this name
for (var i in vendor.modems) {
// ...
}
}
This would be a good jQuery-level architecture. The "best" architecture? A templating library (but that's not necessary at this level).

Trying to use the same string properties in angular to do multiple things

I have a folder in my angular apps directory called demos that I want to load into an iframe and a navigation that I want populated based off of the folder name. Then I want it to list only 10 of the demos and it should be a random selection.
Here is my code but I am missing some steps obv.
app/demos/democara
app/demos/randomeDemo
app/demos/anotherDemo
//UL generated from app/demos/ structure
<ul class="randomDemoNav">
<li ngClick="{{cPDemo}}"><span class="pseudo">/democara</span></li>
<li ngClick="{{cPDemo}}"><span class="pseudo">/randomeDemo</span></li>
<li ngClick="{{cPDemo}}"><span class="pseudo">/anotherDemo</span></li>
</ul>
<iframe class="demo-loader" ng-src="{{cPDemo}}"></iframe>
<script>
and that's about as far as I get.
</script>
Any thoughts on what I should search in API to accomplish this? I would also be ok using just jquery for this.

Categories