Cleaning up Angular Partials - javascript

I am making a menu that has a few different types of buttons with AngularJS. Based on the type of button, the html needs to have different characteristics. Also, the currently selected menu item needs to have a different color than the available buttons. Currently, I am using this mess of code:
<div id="navbar" ng-show="navbar.show" ng-mouseenter="navbar.keep()" ng-mouseleave="navbar.release()">
<div ng-repeat="navSection in navbar.navSections" ng-init="sectionIndex = $index" class="navblock">
<div ng-repeat="navItem in navSection.navigationItems">
<div ng-switch on="navItem.function">
<div ng-switch-when='CategoryNav', ng-click='navbar.navClick(sectionIndex, $index)'>
<div info="navItem", ng-if='sectionIndex == navbar.section && $index == navbar.item', id="selected">
<a ui-sref="{{navItem.function}}" ng-href="{{navItem.function}}">
<div class="navbutton">{{navItem.label}}</div>
</a>
</div>
<div info="navItem", ng-if='sectionIndex != navbar.section || $index != navbar.item', id="notselected">
<a ui-sref="{{navItem.function}}" ng-href="{{navItem.function}}">
<div class="navbutton">{{navItem.label}}</div>
</a>
</div>
</div>
<div ng-switch-when='StorefrontNav', ng-click='navbar.changeStorefront(navItem.type)'>
<div info="navItem", id="notselected">
<a ui-sref="{{navItem.function}}" ng-href="{{navItem.function}}">
<div class="navbutton">{{navItem.label}}</div>
</a>
</div>
</div>
<div ng-switch-default>
<div info="navItem", ng-if='sectionIndex == navbar.section && $index == navbar.item', id="selected">
<a ui-sref="{{navItem.function}}" ng-href="{{navItem.function}}">
<div class="navbutton">{{navItem.label}}</div>
</a>
</div>
<div info="navItem", ng-if='sectionIndex != navbar.section || $index != navbar.item', id="notselected">
<a ui-sref="{{navItem.function}}" ng-href="{{navItem.function}}">
<div class="navbutton">{{navItem.label}}</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
It works fine but I want to clean it up a little bit. I have thought about making a directive and passing in the functions the different links need to work (for example, the navbar.changeStoreFront()) but that seems like a lot of extra code just to clean the format up a bit. Does anyone know any better ways of cleaning this up?

I noticed that the simplest way to make the code more readable is to put the attributes on separate lines. Take this for example:
<ui-gmap-google-map center='{expression}'
control='{Object}'
zoom='{expression}'
dragging='{expression}'
refresh='{expression}'
options='{expression}'
events='{expression}'
bounds='{expression}'
pan='{string or boolean}'
>
<!-- other ui-gmap-directives here -->
</ui-gmap-google-map>

Related

Avoid JavaScript Replication in partial views

This code is used to remove a cart-item from a partial view.
$(document).on('click', '.RemoveLink', (function (e) {
e.preventDefault();
var recordToDelete = $(this).attr("data-id");
var itemID = $(this).attr("data-itemid");
if (recordToDelete != '') {
$.post("/ShoppingCart/RemoveFromCart", { "id": recordToDelete, "itemID": itemID },
function () {
$('.container-cart').load('#Url.Action("cartDropDown","ShoppingCart")', function () {
$('.cart-dropdown').css('display', 'inline-block');
}
);
});
}
}));
This works well for the first iteration but from the second iteration on-wards, every click of a remove of an item is resulting in deletion of 2 items of a kind. Suppose we had 4 items of pencils and 8 items of pens. Clicking delete pencil button once will result in deletion of 2 pencils and vice versa.
This is probably because of the logic used. Following is the html that is rendered when $('.container-cart').load('#Url.Action("cartDropDown","ShoppingCart")' executes:
#model OnlineStore.ViewModels.ShoppingCartViewModel
<div class="container-cart">
#if (Model.ItemCount == 0)
{
<div>
<span>
There are no items in your cart. Continue shopping.
</span>
</div>
}
else
{
<ul class="cart-dropdown">
<li>
<div class="cart-items cart-caption">
<ul>
#foreach (var i in Model.CartItems)
{
<li id="list-item-#i.item.ItemID">
<div class="container-fluid item-wrap" style="position: relative">
<div class="item-remove">
<a href="#" class="RemoveLink"
data-id="#i.RecordID" data-itemid="#i.item.ItemID">
x
</a>
</div>
<div class="col-md-2 item-img">
<div class="row-cart">
<img alt="" id="cartImg" height="71" width="75" src="#i.item.ImageUrl">
</div>
</div>
<div class="col-md-5 item-info">
<div class="row-cart">
<div class="brand-name">
<a href="#" class="brandName">
#i.item.BrandName
</a>
</div>
<div class="product-name">
<a href="#" class="productName">
#i.item.ItemName
</a>
</div>
<div class="product-qty">
<p class="productQTY" id="item-count-#i.item.ItemID">
#i.Count x #i.item.ItemPrice
</p>
</div>
</div>
</div>
<div class="col-md-5 price-info">
<div class="row-cart" style="margin-top: 10px">
<div class="col-md-6">
<div class="row-mrp">
<span class="cartItemPrice" id="item-total-#i.item.ItemID">
Rs #(#i.Count * #i.item.ItemPrice)
</span>
</div>
</div>
</div>
</div>
</div>
</li>
}
</ul>
</div>
</li>
<li class="clearfix">
<div class="col-md-6">
<div class="row-cart sub-cost" style="background: #fff; margin-left: -10px; margin-right: 0">
<p>
Sub Total :
<span style="float: right">
Rs
<span class="ng-binding"></span>
</span>
</p>
<p>
Delivery Charge :
<span qa="delChargeMB" style="float: right">Free</span>
</p>
</div>
<div class="row-cart cart-chkout-btn">
<button type="button">View Basket & Checkout</button>
</div>
</div>
</li>
</ul>
}
</div>
This html is the partial view that is initially rendered when user clicks a button to view the cart-items. So when user clicks on 'remove an item' button on this partial view, an ajax call is sent to server to remove an item from the cart-items and on success, load the UI again by rendering this partial view once again with new values from the database.
All this is working fine for the first iteration of the deletion of an item from the cart-item list. But when I'm deleting an item again as a second deletion, code is running twice. I'm guessing this is because <div class="container-cart"> is rendered twice on the page as after the first deletion, I can see it on the live DOM inside the browser that <div class="container-cart"> is encolsed inside another <div class="container-cart"> and then the normal elements are rendered in sequence. I'm guessing maybe that's why javaScript is rendered twice or running twice.
Please suggest what you think about it and help me resolve it.
Thanks in advance
After deletion of an item try to use location.reload(); instead of hitting the MVC action method again!

$scope variable not updating in view

I have the following snippet of code,
<div ng-controller="GraphCtrl" ng-if="errorTracerouteResultsLength!=0">
<h5>Erroneous Traceroute Paths
<small>Sub-heading</small>
</h5>
<a href="#" ng-repeat="(key, val) in errorTracerouteResults" ng-click="loadTraceroutePath(val.metadata);">
S: {{val.source.ip}}
D: {{val.destination.ip}}
</a>
</div>
It works fine, loadTraceroutePath belongs to another controller,(lets call it X) but somehow ng-click works and console.log gets printed out with the correct metadata value.
However, in controller X, I have,
$scope.loadIndividualTraceroutePath = function (metadataKey) {
$scope.noOfResults = 1;
}
In the html, I have {{noOfResults}} all over the place. Some of it are able to display 1 while some can't. I have already attached the ng-controller directives and point to controller X, but {{noOfResults}} does not display.
How can I make {{noOfResults}} display in any section of the HTML?
Edit: I have added the HTML.
<div class="row">
<div class="col-lg-9">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bar-chart-o fa-fw"></i> Visualisation
<div class="pull-right">
Layout
</div>
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div class="container" ng-controller="X">
<!--This does not work-->
{{noOfResults}}
</div>
<div>
<div ng-controller="IndividualTraceroutePathGraphCtrl" id="individual_traceroute_path_graph"></div>
</div>
</div>
<!-- /.panel-body -->
</div>
<!-- /.panel -->
</div>
The ng click in the first part of this question is way below.
You have a extra ; at the end, also you are not using the object what you are passing as a parameter to the function, hence Change
From:
<a href="#" ng-repeat="(key, val) in errorTracerouteResults" ng-click="loadTraceroutePath(val.metadata);">
To:
<a href="#" ng-repeat="(key, val) in errorTracerouteResults" ng-click="loadTraceroutePath(val)">
Then,
$scope.loadIndividualTraceroutePath = function (metadataKey) {
$scope.noOfResults = 1;
}
EDIT
You don't need to mention so many controllers in the view, have one controller where the function is defined and remove the rest, here, Update like this,
<div class="container" >
<!--This does not work-->
{{noOfResults}}
</div>
<div>
<div id="individual_traceroute_path_graph"></div>
</div>
</div>

Editing function inside ng repeat

I have an edit control inside of an ng-repeat that is correctly changing boolean values onclick, however, the elements that are hooked to these values via ng-show are not showing/hiding. What is going wrong?
Controller:
$scope.editedAnswers = {};
$scope.toggleEditAnswer = function(answer) {
$scope.editedAnswers[answer._id] = !$scope.editedAnswers[answer._id];
console.log($scope.editedAnswers[answer._id]);
};
$scope.updateAnswer = function(answer) {
answer.$update(function() {
$location.path('answers/' + answer._id);
});
$scope.toggleEditAnswer(answer);
};
HTML:
<div class="answers" ng-show="showAnswers">
<div ng-repeat="answer in answers">
<div class="answer-info">
<a ng-show="!editedAnswer[answer._id]" ng-click="toggleEditAnswer(answer)">edit</a>
<a ng-show="editedAnswer[answer._id]" ng-click="updateAnswer()">save</a>
</div>
<div ng-hide="editedAnswer[answer._id]"></div>
<textarea ng-show="editedAnswer[answer._id]">
{{answer.content}}
</textarea>
</div>
</div>
May be the problem is that in controller you define editedAnswers property, but in HTML you refer to the editedAnswer property (without -s)
try changing this:
<div class="answers" ng-show="showAnswers">
<div ng-repeat="answer in answers">
<div class="answer-info">
<a ng-show="!editedAnswer[answer._id]" ng-click="toggleEditAnswer(answer)">edit</a>
<a ng-show="editedAnswer[answer._id]" ng-click="updateAnswer()">save</a>
</div>
<div ng-hide="editedAnswer[answer._id]"></div>
<textarea ng-show="editedAnswer[answer._id]">
{{answer.content}}
</textarea>
</div>
</div>
to this:
<div class="answers" ng-show="showAnswers">
<div ng-repeat="answer in answers">
<div class="answer-info">
<a ng-show="answer._id != editedAnswer[answer._id]" ng-click="toggleEditAnswer(answer)">edit</a>
<a ng-show="answer._id == editedAnswer[answer._id]" ng-click="updateAnswer()">save</a>
</div>
<div ng-hide="answer._id == editedAnswer[answer._id]"></div>
<textarea ng-show="answer._id == editedAnswer[answer._id]">
{{answer.content}}
</textarea>
</div>
</div>
In your version you're simply showing if editedAnswer[answer._id] exists.
In my edit (which may not be evaluating what you actually want - you may need to change != and == around) I am checking if the current answer equals (or not) then editedAnswer[answer._id] - I.E answer._id == editedAnswer[answer._id]

Toggle between multiple divs

I have an issue. I want to toggle between many divs, while one shows the rest hide.
This is what I have so far.
Thanks in advance!
<a href="#n" onclick="toggle_visibility('box1');">
<div class="square img_1-1"></div>
</a>
<a href="#n" onclick="toggle_visibility('box2');">
<div class="square img_1-2"></div>
</a>
<a href="#n" onclick="toggle_visibility('box3');">
<div class="square img_1-3"></div>
</a>
<div id="box1" style='display:none;'>
<div class="trabajo">
<p>box1</p>
</div>
</div>
<div id="box2" style='display:none;'>
<div class="trabajo">
<p>box2</p>
</div>
</div>
<div id="box3" style='display:none;'>
<div class="trabajo">
<p>box3</p>
</div>
</div>
<a href="#n" onclick="toggle_visibility('box4');">
<div class="square img_2-1"></div>
</a>
<a href="#n" onclick="toggle_visibility('box5');">
<div class="square img_2-2"></div>
</a>
<a href="#n" onclick="toggle_visibility('box6');">
<div class="square img_2-3"></div>
</a>
<div id="box4" style='display:none;'>
<div class="trabajo">
<p>box4</p>
</div>
</div>
<div id="box5" style='display:none;'>
<div class="trabajo">
<p>box5</p>
</div>
</div>
<div id="box6" style='display:none;'>
<div class="trabajo">
<p>box6</p>
</div>
</div>
Also, here is the Javascript. I'm using toggle_visibility(id) The problem is that it start to get weird when I hide one box and open another it opens both then it just gets weird. It leaves both open then closes one.
var prevId;
function toggle_visibility(id) {
if(prevId && id !== prevId){
$("#"+prevId).toggle();
}
var e = document.getElementById(id);
$(e).toggle();
prevId = id;
}
There's also another javascript code I tried before, this one works sorta fine, the only thing it doesn't do is toggle it just shows the work and doesn't hide it, although it does toggle between different work.
top.visible_div_id = 'box1';
function toggle_visibility(id) {
var old_e = document.getElementById(top.visible_div_id);
var new_e = document.getElementById(id);
if(old_e) {
console.log('old', old_e, 'none');
old_e.style.display = 'none';
}
console.log('new', new_e, 'block');
new_e.style.display = 'block';
top.visible_div_id = id;
}
Instead of the onclick, I used the eventhandler on() (you should not inline-javascript). I coombine this with the data-attribute:
<a data-toggles="#box2">box 2</a>
And some changes to your javascript:
$('a.triggeringAnchor').on('click', function(e){
e.preventDefault() // stop the anchor
$('a.triggeringAnchor').hide(); // hide them all
$( $(this).data('toggles') ).show(); // bring the one back in the one back
})
You could use the :not() selectors, but this is a bit more obvious :)
The trick here is the 'bring back' line. I use the value of the data-attribute as selector for the actual element we want back.
This code has a big advantage over yours. If you add 1 more element, your counter would be to know about this (could be done via .length()), my code doesn't care. Everything gets hidden, and just that 1 will come back.
I think this jQuery Widget is exactly what you are looking for. Easy to implement and very usable. Plus, the jQuery API instructions make it a breeze to get onto your page.

AngularJS - Using ng-switch in ng-repeat to conditionally apply HTML

I have a paginated list of video thumbnails I want to display in a Bootstrap fluid grid. I'm using a nested ng-repeat to first iterate over each list, for pagination, and an inner ng-repeat to iterate over each video thumbnail in the subarray. The grid is 3x6 so when the inner ng-repeat's index is a multiple of 5 (assuming $index starts at 0), I want to close a fluid row and start a new one.
<div ng-repeat="thumbList in thumbLists">
<div ng-repeat="thumb in thumbList" class="row-fluid">
<div class="span2">
<a href="{{ thumb.URL }}">
<img src="{{ thumb.thumbnailURL }}" width="160" height="85" alt="" class="img-responsive">
</a>
</div>
<!-- ng-switch? -->
</div>
</div>
The lists are populated by a REST service, such that when a user clicks on a 'next' button, a new array list' is pushed onto the 'lists' and an animation will slide over each 3x6 grid. The structure looks like:
$scope.thumbLists = [
[{URL: url, thumbnailURL: thumburl}, ...], // init
[{URL: url, thumbnailURL: thumburl}, ...], // pushed when user clicks 'next'
... etc
];
Everything I have is working properly, I'm just not sure how to conditionally add in the HTML to close a row and start a new one once that row hits 6 since the thumbnails are given in a single array.
For example, where I have the commented ng-switch, could I do something like:
<div ng-switch="$index">
<div ng-switch-when="$index % 6 == 5">
</div>
Using 1.0.8 stable release. Must support IE8.
Temporary solution - removing the left margin for each element that is the first in it's row:
<div ng-repeat="thumbList in thumbLists">
<div class="row-fluid>
<div ng-repeat="thumb in thumbList" class="span2" ng-class="{nomargin: $index % 6 == 0}">
<a href="{{ thumb.URL }}">
<img src="{{ thumb.thumbnailURL }}" width="160" height="85" alt="" class="img-responsive">
</a>
</div>
</div>
</div>
Would still like to find a solution that where I can conditionally insert/remove HTML, perhaps using a directive.
Would ng-grid work for you? Might save you having to deal with the details.
http://angular-ui.github.io/ng-grid/
This works with ng-switch -- but may need some more tweaking to avoid the extra hidden span markup...
<div ng-repeat="thumb in thumbList">
<span ng-switch="$index % 5">
<div class="row" ng-switch-when="0">
<div class="col-sm-2"><img src="{{thumbList[$index+0].url}}" class="img-responsive"></div>
<div class="col-sm-2"><img src="{{thumbList[$index+1].url}}" class="img-responsive"></div>
<div class="col-sm-2"><img src="{{thumbList[$index+2].url}}" class="img-responsive"></div>
<div class="col-sm-2"><img src="{{thumbList[$index+1].url}}" class="img-responsive"></div>
<div class="col-sm-2"><img src="{{thumbList[$index+2].url}}" class="img-responsive"></div>
</div>
</span>
</div>
http://bootply.com/86985

Categories