AngularJS: How to initialize a $scope attribute - angularjs

I wrote the following small angular code snippet:
html file:
<body ng-controller="myCtrl">
<div class="container">
....
</div>
</body>
javascript file:
app.controller('myCtrl', function($scope) {
$scope.white_sld = get_init_white_sld();
$scope.get_soldier_style = function is_soldier(loc) {
var sld_color = get_soldier_color(loc);
if (sld_color == "") {
return ""; // no soldier in this square
} else {
return {
'width': '80%', 'height': '80%', 'border-radius': '80%', 'background-color': sld_color,
'margin': 'auto auto', 'vertical-align': 'middle'
}
}
function get_soldier_color(loc) {
for (var i = 0; i < white_sld.length; i++) {
if ((loc[0] == white_sld[i][0]) && (loc[1] == white_sld[i][1])) {
return "white"
}
}
for (var i = 0; i < black_sld.length; i++) {
if ((loc[0] == black_sld[i][0]) && (loc[1] == black_sld[i][1])) {
return "black"
}
}
return ""; // no soldier found
}
}
});
function get_init_sld_line(x_loc, y_loc, color) {
var line_sld = [];
for (var i = 0; i < 4; i++) {
line_sld.push(new Soldier(x_loc + 2 * i, y_loc, color));
}
return line_sld;
}
function get_init_white_sld() {
var init_white_sld = [];
init_white_sld.push(get_init_sld_line(0, 0, "white"));
init_white_sld.push(get_init_sld_line(1, 1, "white"));
init_white_sld.push(get_init_sld_line(0, 2, "white"));
return init_white_sld;
};
My problem with this code is that after the line "$scope.white_sld = get_init_white_sld();", $scope.white_sld gets the return value of the function get_init_white_sld(). However, when it gets inside the function 'is_soldier', then $scope.white_sld becomes undefined. My question is why and how can I resolve it?

That's because you should use $scope.white_sld within that function, not just white_sld.

I have updated the plunkr please check
Actually you forgot to add return statement in get_init_sld_line function.
https://plnkr.co/edit/kYebs1pFznaDpHlFDodK?p=preview
return line_sld;

Related

Update URL in browser while scrolling a page in gatsby [duplicate]

Is it possible to change the URL in the address bar instantly when I scroll to an id? Or have a long document with multiple id and the url changes on address bar, when I hit a new id even i scroll form bottom to top.
i use Change url when manually scrolled to an anchor?
but this is not working when scroll from bottom to top.
$(function () {
var currentHash = "#";
$(document).scroll(function () {
$('.block').each(function () {
var top = window.pageYOffset;
var distance = top - $(this).offset().top;
var hash = $(this).attr('id');
// 30 is an arbitrary padding choice,
// if you want a precise check then use distance===0
if (distance < 30 && distance > -30 && currentHash != hash) {
window.location.hash = (hash);
currentHash = hash;
}
});
});
});
body {
margin: 0px;
}
div {
height: 900px;
text-align: center;
padding: 15px;
background-color: #00f;
}
div:nth-child(even) { background: #ccc; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<div class="block" id="one">Block 1</div>
<div class="block" id="two">Block 2</div>
<div class="block" id="three">Block 3</div>
<div class="block" id="four">Block 4</div>
<div class="block" id="five">Block 5</div>
Thanks in advance.
After your comment, I understand what you are trying to achieve:
The following code is based on scroll up/ down.
Will store the current block and make you "jump" between blocks:
$(function () {
var blocksArr = $('.block');
var lastScrollTop = 0;
var currentBlock = 0;
$(document).scroll(function () {
var st = $(this).scrollTop();
var hash;
//make sure it is in the boundaries
if (st > lastScrollTop && currentBlock< blocksArr.length -1){
// downscroll code
hash = $(blocksArr[++currentBlock]).attr('id');
window.location.hash = (hash);
}
else
if (st < lastScrollTop && currentBlock > 0){
// scrollup code
hash = $(blocksArr[--currentBlock]).attr('id');
window.location.hash = (hash);
}
lastScrollTop = $(this).scrollTop();
});
});
"working" fiddle (hash wont change on fiddle)
added:
If you only want to see the URL changes:
$(function () {
var currentHash = "#";
var blocksArr = $('.block');
$(document).scroll(function () {
var currentTop = window.pageYOffset/1;
for (var i=0; blocksArr.length; i++){
var currentElementTop = $(blocksArr[i]).offset().top;
var hash = $(blocksArr[i]).attr('id');
if (currentElementTop < currentTop && currentTop < currentElementTop + $(blocksArr[i]).height() && currentHash!=hash){
if(history.pushState) {
history.pushState(null, null, '#'+hash);
}
else {
location.hash = '#'+hash;
}
currentHash = hash;
}
}
});
});

Custom angular directive controller not updating

I created my own angular directive which can simply be used as:
<basket-summary></basket-summary>
This element is used on my master template page (the index.html).
The directive is as follows:
/* Directive */
angular.module('ecommerceDirectives').directive('basketSummary', [function() {
return {
restrict : 'E',
scope: {},
replace: true,
controller : 'BasketController',
controllerAs: 'basketController',
bindToController: {
basketController : '='
},
templateUrl: function(element, attrs) {
if (typeof attrs.templateUrl == 'undefined') {
return 'app/views/basket-summary.html';
} else {
return attrs.templateUrl;
}
},
link: function (scope, element, attrs) {
console.log("test");
}
};
}]);
The templateUrl of this directive is as follows:
<div class="btn-group pull-right">
<a class="btn btn-warning" ng-click="basketController.viewBasket()"><span class="badge">{{basketController.getTotalQuantities()}}</span> <span class="hidden-xs">Shopping</span> Cart</a>
<button type="button" class="btn btn-warning dropdown-toggle" data-toggle="dropdown">
<span class="fa fa-caret-down"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu">
<div class="col-xs-12 cartItemHeader">
<h4>My Shopping Cart</h4>
</div>
<div class="quickCart" ng-repeat="cartItem in basketController.customerBasket.items">
<div class="col-xs-12 cartItemWrap">
<div class="col-xs-12 desc">{{cartItem.product.title}}</div>
<div class="col-xs-5 col-sm-8 price">{{cartItem.product.rrp_currency}} {{cartItem.product.rrp_amount}}</div>
<div class="col-xs-6 col-sm-3 units">Items: {{cartItem.quantity}}</div>
<div class="col-xs-1 trash"><a ng-click="basketController.deleteCartItem(cartItem.product.id)"><i class="fa fa-trash"></i></a></div>
</div>
</div>
</div>
And the BasketController is as follows:
/* Controller */
angular.module('ecommerceControllers').controller('BasketController', ['$rootScope', '$scope', '$route', '$location', 'CustomerBasketService', 'AppSettingService', 'StorageService', 'DEFAULT_CURRENCY_CODE', 'MERCHANT_ID_KEY', function($rootScope, $scope, $route, $location, CustomerBasketService, AppSettingService, StorageService, DEFAULT_CURRENCY_CODE, MERCHANT_ID_KEY) {
function basketProduct(product) {
this.id = product.id;
this.title = product.title;
this.categories = product.categories;
this.images = product.imaes;
this.created_on = product.created_on;
this.sku_code = product.sku_code;
this.lang = product.lang;
this.short_description = product.short_description;
this.attributes = product.attributes;
this.rrp_currency = product.rrp_currency;
this.rrp_amount = product.rrp_amount;
this.barcode_number = product.barcode_number;
this.last_modified_on = product.last_modified_on;
}
function basketItem(quantity, product) {
this.quantity = quantity;
this.product = new basketProduct(product);
}
function load() {
var basket = StorageService.get("customer_basket");
if (basket) {
for (var i = 0; i < basket.items.length; i++) {
var cartItem = basket.items[i];
cartItem = new basketItem(cartItem.quantity, cartItem.product);
basket.items[i] = cartItem;
}
}
return basket;
}
var basketController = this;
$scope.basketController = basketController;
basketController.customerBasket = load();
if (!basketController.customerBasket) {
basketController.customerBasket = {
shipping : null,
taxRate : null,
tax : null,
items : []
};
}
basketController.addCartItem = function(quantity, product) {
if (product == undefined || product == null) {
throw "No Product was specified.";
}
if (quantity == undefined || quantity == null) {
quantity = null;
}
var found = false;
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
for (var i = 0; i < basketController.customerBasket.items.length; i++) {
var cartItem = basketController.customerBasket.items[i];
if (product.id === cartItem.product.id) {
found = true;
cartItem.quantity = cartItem.quantity + quantity;
if (cartItem.quantity < 1) {
basketController.customerBasket.items.splice(i, 1);
} else {
$rootScope.$broadcast('customerBasketItemUpdated', {item: cartItem});
}
}
}
}
if (!found) {
var cartItem = new basketItem(quantity, product);
basketController.customerBasket.items.push(cartItem);
$rootScope.$broadcast('customerBasketItemAdded', {item: cartItem});
}
basketController.saveBasket();
}
basketController.updateBasket = function() {
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
for (var i = 0; i < basketController.customerBasket.items.length; i++) {
var cartItem = basketController.customerBasket.items[i];
if (cartItem.quantity < 1) {
basketController.customerBasket.items.splice(i, 1);
}
}
}
basketController.saveBasket();
}
basketController.deleteCartItem = function(productId) {
if (productId == undefined) {
throw "Product ID is required.";
}
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
for (var i = 0; i < basketController.customerBasket.items.length; i++) {
var cartItem = basketController.customerBasket.items[i];
if (productId == cartItem.product.id) {
basketController.customerBasket.items.splice(i, 1);
}
}
}
//Save
basketController.saveBasket();
}
basketController.getCurrencyCode = function() {
var code = DEFAULT_CURRENCY_CODE;
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
code = basketController.customerBasket.items[0].product.rrp_currency;
}
return code;
};
basketController.getTotalQuantities = function(id) {
var total = 0;
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
for (var i = 0; i < basketController.customerBasket.items.length; i++) {
var cartItem = basketController.customerBasket.items[i];
if (id == undefined || id == cartItem.product.id) {
total += cartItem.quantity;
}
}
}
return total;
};
basketController.getTotalAmount = function(cartItem) {
var total = 0;
if (cartItem) {
total = (cartItem.quantity * cartItem.product.rrp_amount);
}
return total.toFixed(2);
};
basketController.getFinalTotalAmount = function() {
var total = 0;
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
for (var i = 0; i < basketController.customerBasket.items.length; i++) {
var cartItem = basketController.customerBasket.items[i];
total += (cartItem.quantity * cartItem.product.rrp_amount);
}
}
return total.toFixed(2);
};
basketController.clearBasket = function() {
StorageService.set("customer_basket", null);
basketController.customerBasket = {
shipping : null,
taxRate : null,
tax : null,
items : []
};
$rootScope.$broadcast('customerBasketCleared', {});
}
basketController.saveBasket = function() {
if (basketController.customerBasket) {
StorageService.set("customer_basket", basketController.customerBasket);
$rootScope.$broadcast('customerBasketSaved', {});
}
}
basketController.checkout = function(serviceName, clearCart) {
if (serviceName == undefined || serviceName == null) {
serviceName = "PayPal";
}
switch (serviceName) {
case "PayPal":
basketController.checkoutPayPal(clearCart);
break;
default:
throw "Unknown checkout service '" + serviceName + "'.";
}
};
basketController.checkoutPayPal = function(clearCart) {
if (basketController.customerBasket && basketController.customerBasket.items && basketController.customerBasket.items.length > 0) {
// global data
var data = {
cmd: "_cart",
business: '', //parms.merchantID,
upload: "1",
rm: "2",
charset: "utf-8"
};
AppSettingService.getAppSetting(MERCHANT_ID_KEY).get().$promise
.then(function(result) {
data.business = result.value;
for (var i = 0; i < basketController.customerBasket.items.length; i++) {
var cartItem = basketController.customerBasket.items[i];
var ctr = i + 1;
data["item_number_" + ctr] = cartItem.product.sku_code;
data["item_name_" + ctr] = cartItem.product.title;
data["quantity_" + ctr] = cartItem.quantity;
data["amount_" + ctr] = cartItem.product.rrp_amount.toFixed(2);
}
// build form
var form = $('<form/></form>');
form.attr("action", "https://www.paypal.com/cgi-bin/webscr");
form.attr("method", "POST");
form.attr("style", "display:none;");
addFormFields(form, data);
$("body").append(form);
// submit form
form.submit();
form.remove();
if (clearCart) {
try {
basketController.clearBasket();
} catch (exception) {
}
}
}).catch(function(reason) {
console.log(reason);
});
}
};
basketController.viewBasket = function() {
$location.path("/basket");
$route.reload();
};
function addFormFields(form, data) {
if (data != null) {
$.each(data, function (name, value) {
if (value != null) {
var input = $("<input></input>").attr("type", "hidden").attr("name", name).val(value);
form.append(input);
}
});
}
}
return basketController;
}]);
My dilemma is as follows:
On my product page, I have product.html page (which is an angular view) and the "add to cart" button adds item to the cart but it doesn't update my cart summary button.
How do I make it that when I click the "add to cart" button that it update my cart and the number of items added to the cart?
The example image:
As you can see the orange button at the top right says that I have no items but there is 3 items in my cart already.
I tried various scoping allowed in directives but I seem to be failing to make it work.
PS: I am a newbie in AngularJS so please make your answer as simple to understand. :-)
The view product.html is linked to BasketController and this is the button that add item to cart.
<a class="btn btn-success btn-lg pull-right" role="button" ng-click="basketController.addCartItem(1, productController.selectedProduct)"><i class="fa fa-plus"></i> Add To Cart</a></div>
You have an isolated scope issue. The controller of your directive is not the same as the controller you want to use in your product page. See: scopes
I would suggest you to create a factory, which is always a singleton where you store your basket products and inject them in both, the directive and the products page

Controller function not applying to ng-include file

I am trying to use the "minisCtrlOverall" controller in my ng-include file, but none of the functionality in my controller works for the included file unless I put ng-controller in the actual overall.html file. What I am trying to do is access the "ring-fill" class that is in my overall.html file from my controller. I want to add the class "active" too all "ring-fills" but its not working. I'm guessing this isn't working because the included files comes after the controller runs? Anyone know how I can fix this?
Controller:
angular.module('ciscoImaDashboardAdmin',[])
.controller('minisCtrlOverall', function ($scope, $rootScope, dummyData) {
$scope.overallOn = true;
$scope.switchView = function(element) {
var value = element.target.attributes['value'].value;
if(value == "overall") {
$scope.overallOn = true;
$scope.swimlaneOn = false;
}
else if(value == "swimlane") {
$scope.swimlaneOn = true;
$scope.overallOn = false;
}
};
var totalRings = 9;
var maxScore = 10;
var data_overall = {
average_score: 6
}
var ringToHighlight = Math.floor((data_overall.average_score/maxScore)*totalRings); //round down
var i = 0;
var rings = [];
var ringClass = 'path.ring-fill:not(.ring-border)';
$(ringClass).each(function(){
rings.push($(this)[0]);
});
while( i < ringToHighlight) {
fillPath(i);
i = i + 1;
}
function fillPath(i) {
if(i < ringToHighlight) {
var selectedRing = $(rings[i]);
selectedRing.attr("class", "ring-fill active");
}
}
});
HTML:
<div class="row mini" ng-show="overallOn" ng-controller="minisCtrlOverall">
<div class="col-sm-3">
<div ng-include="'svgs/overall.html'"></div>
</div>
</div>

Truncate ng-bind-html in AngularJS

I'm using truncate.js https://github.com/sparkalow/angular-truncate and it works great for codes like this:
{{announcement.content | characters:25}}
However, i can't seem to setup for the following and i can't get it to work:
<p ng-bind-html="parseTrustedHtml(announcement.content | characters : 25)"></p>
I encountered a similar issue, the issue is that angular-truncate is meant for strings, not HTML. Here is my solution:
Markup:
<div class="container" ng-controller="parentCtrl">
<div ng-bind-html="text | limitHtml : maxNumberOfChar:'...' | trustAsHtml"></div>
</div>
Code:
.filter('trustAsHtml', ['$sce', function($sce) {
return $sce.trustAsHtml;
}])
.filter('limitHtml', function() {
return function(text, limit, ellipsis) {
var _getClosedTagsString = function(_tagArray) {
var _returnArray = [],
_getTagType = function(_string) {
return _string.replace(/<[\/]?([^>]*)>/,"$1");
};
angular.forEach(_tagArray,function(_tag,_i) {
if(/<\//.test(_tag)) {
if(_i === 0) {
_returnArray.push(_tag);
} else if(_getTagType(_tag) !== _getTagType(_tagArray[_i - 1])) {
_returnArray.push(_tag);
}
}
});
return _returnArray.join('');
},
_countNonHtmlCharToLimit = function(_text,_limit) {
var _isMarkup = false,
_isSpecialChar = false,
_break = false,
_underLimit = false,
_totalText = 0,
_totalChar = 0,
_element,
_return = {
textCounter : 0,
offsetCounter : 0,
setEllipsis : false,
overElementArray : []
};
angular.forEach(_text,function(_c) {
_underLimit = _return.textCounter < _limit;
if(_c === '<' && !_isMarkup && !_isSpecialChar) {
(!_underLimit) && (_element = '<');
_isMarkup = true;
} else if(_c === '&' && !_isMarkup && !_isSpecialChar) {
_isSpecialChar = true;
} else if(_isMarkup) {
//tracking html elements that are beyond the text limit
(!_underLimit) && (_element = _element + _c);
if(_c === '>') {
//push element in array if it is complete, and we are
//beyond text limit, to close any html that is unclosed
(!_underLimit) && (_return.overElementArray.push(_element));
_break = true;
_isMarkup = false;
}
} else if(_c === ';' && _isSpecialChar) {
_isSpecialChar = false;
//count as one character
_return.textCounter++;
_break = true;
}
if(_underLimit) {
if(!_isMarkup && !_isSpecialChar && !_break) {
//counting number of characters in non html string
_return.textCounter++;
}
_return.offsetCounter++;
} else {
_return.setEllipsis = true
}
_break = false;
});
//returns offset within html of number of non html characters found
return _return;
},
_charToLimitOutput = _countNonHtmlCharToLimit(text.toString(),limit);
return text.toString().substr(0, _charToLimitOutput.offsetCounter) +
ellipsis + _getClosedTagsString(_charToLimitOutput.overElementArray);
}
})
.controller('parentCtrl', function($scope,$timeout) {
$scope.text = "<span><h1>Example </h1><p>Special Text</p><div>other stuff</div></span>";
$scope.maxNumberOfChar = 10;
});
No need to use truncate.js
You can solve this using custom directives and filters.
try this one: https://stackoverflow.com/a/45076560/6816707
Need more information, however can you check if parseTrustedHtml method/function is available at $scope or $rootscope.

Angular 3 filters (inputs and checkboxes)

I need some help.
screenshot: https://monosnap.com/file/VxAdq975FVT6QHkECfxlFyHgGd3sAn
I have 3 filters on top: rooms, size and price. How to filter table results, when something typing in the filter fields?
UPDATE
$scope.$watch( '[min_size, max_size]', function(val) {
$scope.filterBySizeRange();
});
$scope.filterBySizeRange = function() {
$scope.filteredSizes = [];
angular.forEach($scope.apps, function(items) {
if (items.size >= $scope.min_size
&& items.size <= $scope.max_size) {
$scope.filteredSizes.push(items);
}
if (!$scope.min_size
&& !$scope.max_size) {
$scope.filteredSizes.push(items);
};
});
};
UPDATE 3
Here is my solution, that works with single or multiple range input fields
fiddle
I think you want to use $watchGroup.
$scope.$watchGroup(['min_size', 'max_size'], function(val) {
$scope.filterBySizeRange();
});
$scope.filterBySizeRange = function() {
$scope.filteredSizes = [];
angular.forEach($scope.apps, function(items) {
if (items.size >= $scope.min_size
&& items.size <= $scope.max_size) {
$scope.filteredSizes.push(items);
}
if (!$scope.min_size
&& !$scope.max_size) {
$scope.filteredSizes.push(items);
};
});
};
Anyway I believe that it would be better to create your own filter function
// template
<div ng-repeat="item in apps|sizefilter:min_size:max_size">
// filter
app.filter('sizefilter', function() {
return function(collection, minSize, maxSize) {
var items = collection.slice(0, collection.length -1);
var i =0, len = items.length
for (; i < len;) {
if (items.size < minSize && items.size > maxSize) {
items.splice(i, 1);
} else {
i++;
}
}
return items;
});
};
});
// Min/max size filter
$scope.sizeFilter = function(app){
if (!$scope.min_size && !$scope.max_size) {
return app;
} else if(!$scope.max_size){
return (app.size >= $scope.min_size);
} else if(!$scope.min_size){
return (app.size <= $scope.max_size);
} else {
return (app.size >= $scope.min_size && app.size <= $scope.max_size);
}
}

Resources