Custom angular directive controller not updating - angularjs

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

Related

Angular filter multiple columns with an array of filters

I am trying to be able to filter multiple columns on multiple values. So far I can filter multiple columns on 1 single value:
myApp.controller('MyCtrl', ['$scope', '$http', function($scope, $http) {
$scope.empList = [];
$http.get('getAllOnline.php')
.success(function(data) {
$scope.empList = data;
});
$scope.column1List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column1"
}
})
.success(function(data) {
$scope.column1 = data;
});
$scope.column2List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column2"
}
})
.success(function(data) {
$scope.column2 = data;
});
$scope.column3List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column3"
}
})
.success(function(data) {
$scope.column3 = data;
});
$scope.setColumn1Value = function(val) {
if ($scope.zelectedColumn1 == val) {
$scope.zelectedColumn1 = undefined;
} else {
$scope.zelectedColumn1 = val;
}
}
$scope.setColumn2Value = function(val) {
if ($scope.zelectedColumn2 == val) {
$scope.zelectedColumn2 = undefined;
} else {
$scope.zelectedColumn2 = val;
}
}
$scope.setColumn3Value = function(val) {
if ($scope.zelectedColumn3 == val) {
$scope.zelectedColumn3 = undefined;
} else {
$scope.zelectedColumn3 = val;
}
}
Then i use this to set my single value filters:
<ul id="Column1" class="collapse">
<li ng-repeat="emp in empList | unique:'Column1'">
<a ng-click="setColumn1Value(emp.Column1);" ng-class="{selected: emp.Column1 === zelectedColumn1}">
<div ng-repeat="someone in Column1List | filter:{Column1_id:emp.Column1}">
{{someone.value}}
</div>
</a>
</li>
</ul>
This works perfect! But now I want to be able to filter on multiple values in 1 column. So I changed my setter functions to:
$scope.zelectedColumn1=[];
$scope.setColumn1Value = function(val) {
var found = jQuery.inArray(val, $scope.zelectedColumn1);
if (found >= 0) {
// Element was found, remove it.
$scope.zelectedColumn1.splice(found, 1);
} else {
// Element was not found, add it.
$scope.zelectedColumn1.push(val);
}
console.log($scope.zelectedColumn1);
}
So now I add or remove indexes to an array instead of storing a single value. This also works, but how do I filter my columns on the contents of an array instead of on a single value as I do now:
<div class='row'>
<div class='col-lg-2 col-md-3 col-sm-6' ng-repeat="emp in empList | filter:{column1:zelectedColumn1,column2:zelectedColumn2,column3:zelectedColumn3} as res">
{{emp.column1}}
{{emp.column2}} <br>
{{emp.column3}} <br>
</div>
</div>
I have been struggling with this al day and hope someone can help me out here!

Sort objects by property AngularJS

I have a collection of avatars with a property 'orderNumber' in model. I need my collection of avatars to sort by 'orderNumber' field. Each object has it's orderNumber and if you change orderNumber of Object in robomongo for example, objects swaps due to its 'orderNumber' number.
Here is my model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CollectionSchema = new Schema({
name: {
type: String,
required: 'Name is required!'
},
description: {
type: String
},
orderNumber:{
type: Number
},
defaultAvatar: [{
type: Schema.Types.ObjectId,
ref: 'Part'
}],
designer_id: {
type: Schema.Types.ObjectId,
ref: 'Designer'
},
pipeline: [{
name: {
type: String,
required: 'Pipeline step name is required!'
},
zoom: Boolean,
templates: [{
name: {
type: String,
required: 'Template name is required!'
},
parts: [{
type: Schema.Types.ObjectId,
ref: 'Part'
}]
}]
}]
});
module.exports = mongoose.model('Collection', CollectionSchema);>
Here is my controller
(function () {
'use strict';
angular
.module( 'mainApp' )
.controller( 'simpleACController', simpleACController );
function simpleACController( $scope, $location, $compile, $localStorage, CanvasRendererFactory, WrapUnwrapFactory,
PremadeAvatarManager, PartsFactory, AvatarConstructorFactory, UserAvatarsFactory,
JqueryRenderFactory, UserFactory ) {
var vm = this; // ViewModel, you are free of controller's name in html
var canvas = new fabric.StaticCanvas( '' );
vm.showPreviousButton = false;
vm.showNextButton = true;
var savedAvatarMiniatures = [];
var savedPipelineNumber;
// var canvas = new fabric.StaticCanvas( 'simpleAvatarCanvas' );
var pipelineNumber; // -1
var previousPipeline;
var selectedType;
var selectedIndex = 0;
var previousIndex = 0;
var miniatureCarousel;
var avatarCarousel;
var transitionInProgress = false;
vm.avatars = [];
var initialAvatars = [];
var pipelineNames = {};
var pipelineZooms;
vm.select = function ( index ) {
savedAvatarMiniatures = vm.avatars;
selectedIndex = index;
if (pipelineNumber == -1) {
selectedType = index;
}
var wasSelected = vm.avatars[index].selected;
for (var i = 0; i < vm.avatars.length; i++) {
vm.avatars[i].selected = false;
}
vm.avatars[index].selected = !wasSelected;
if (pipelineNumber == -1) {
vm.avatarName = vm.avatars[index].name;
}
vm.showPreviousButton = ( index != 0 );
vm.showNextButton = ( index != (vm.avatars.length - 1) );
};
vm.selectNext = function () {
previousIndex = selectedIndex;
if (selectedIndex < vm.avatars.length - 1) {
changeMiniatureCarousel( ++selectedIndex );
}
};
vm.selectPrevious = function () {
previousIndex = selectedIndex;
if (selectedIndex > 0) {
changeMiniatureCarousel( --selectedIndex );
}
};
vm.onMiniatureClick = function ( index ) {
if (!transitionInProgress) {
vm.select( index );
}
};
vm.prevPipeLine = function () {
var avatar = getSelectedAvatar();
if (avatar != null) {
if (pipelineNumber < getPipelineCount( avatar )) {
previousPipeline = pipelineNumber;
pipelineNumber--;
updatePipeline();
} else {
pipelineNumber--;
}
vm.switchCarousel( true );
}
updateText();
};
vm.nextPipeLine = function () {
if (getSelectedAvatar() != null) {
previousPipeline = pipelineNumber;
pipelineNumber++;
updatePipeline();
}
updateText();
};
vm.showCarousel = true;
vm.switchCarousel = function ( flag ) {
vm.showCarousel = flag;
};
vm.saveAvatar = function () {
if (!UserFactory.IsLoggedIn()) {
$localStorage.anonymousAvatar = getSelectedAvatar();
UserFactory.showLoginModal( function () {
save();
}, function ( err ) {
console.log( 'saveAvatar Error' );
console.log( err );
} );
} else {
save();
}
function save() {
var avatar = getSelectedAvatar();
var partIds = WrapUnwrapFactory.unwrapPartIds( avatar );
UserAvatarsFactory.saveAvatar( avatar.orderNumber, vm.avatarName, partIds, function () {
$location.path( '/avatarmanager' );
$scope.$apply();
} );
}
};
vm.toFullAC = function () {
if (!UserFactory.IsLoggedIn()) {
UserFactory.showLoginModal( function () {
toFullAC( getSelectedAvatar() );
}, function ( err ) {
console.log( 'toFullAC Error' );
console.log( err );
} );
} else {
toFullAC( getSelectedAvatar() );
}
};
function changeMiniatureCarousel( index ) {
refresh( index );
miniatureCarousel.trigger( 'to.owl.carousel', [index] );
}
function selectMiniatureCarousel() {
var index = ( pipelineNumber == -1 && selectedType != undefined ) ? selectedType : 0;
changeMiniatureCarousel( index )
}
function getSelectedAvatar() {
var avatar = null;
if (vm.avatars.length > 0) {
for (var i = 0; i < vm.avatars.length; i++) {
if (vm.avatars[i].selected == true) {
avatar = vm.avatars[i];
}
}
}
return avatar;
}
function updatePipeline() {
var avatar = getSelectedAvatar();
if (avatar) {
if (pipelineNumber < getPipelineCount( avatar )) {
updateMiniatures( avatar ).then( function () {
return true;
} );
} else {
if (!UserFactory.IsLoggedIn()) {
updateViewModel();
vm.switchCarousel( false );
} else {
toFullAC( avatar );
}
}
}
return false;
}
function updateText() {
var avatar = getSelectedAvatar();
if (pipelineNames.length != 0) {
var names = ['type'];
for (var i = 1; i < pipelineNames[avatar.orderNumber].length; i++) {
names.push( pipelineNames[avatar.orderNumber][i] );
}
if (pipelineNumber > -2) {
vm.heading = 'Select avatar ' + names[pipelineNumber + 1];
}
if (pipelineNumber >= getPipelineCount( avatar )) {
vm.heading = "Your avatar is ready! Save?"
}
}
}
function getPipelineCount( avatar ) {
return PremadeAvatarManager.getPipelineCount( avatar );
}
function updateCarousels() {
renderAvatars();
renderMiniatures();
bindCarousels();
}
function renderMiniatures() {
var slideCount = 6;
slideCount = ( vm.avatars.length < slideCount ) ? vm.avatars.length : slideCount;
var settings = {
array: vm.avatars,
imageProperty: 'premade',
type: 'canvas',
width: 150,
height: 100,
vmArray: 'simpleAC.avatars',
oldCarousel: '#simpleACcarousel',
carouselRoot: '#miniatures-carousel-root',
containerClass: "as-slider",
containerID: "simpleACcarousel",
divClick: ' ng-click="simpleAC.onMiniatureClick',
slideCount: slideCount,
centerMode: false,
needsText: true,
selectable: true
};
var divNroot = JqueryRenderFactory.render( settings, $scope, $compile );
miniatureCarousel = divNroot.div;
//divNroot.root.append( miniatureCarousel );
$compile( divNroot.root )( $scope );
CanvasRendererFactory.renderAvatarsToCanvasesInCarousel( vm.avatars, miniatureCarousel );
miniatureCarousel.owlCarousel( {
responsive: {
0: {
items: 5,
},
1439: {
items: 7,
}
},
animateIn: 'fadeIn',
animateOut: 'fadeOut',
center: true,
loop: false,
margin: 10,
mouseDrag: false,
touchDrag: false,
nav: false
} );
}
function renderAvatars() {
var slideCount = 3;
slideCount = ( vm.avatars.length - 1 < slideCount ) ? 1 : slideCount;
var settings = {
array: vm.avatars,
imageProperty: 'premade',
type: 'canvas',
width: 480,
height: 320,
vmArray: 'simpleAC.avatars',
oldCarousel: '#simpleACavatarCarousel',
carouselRoot: '#avatars-carousel-root',
containerClass: "ag-slider",
containerID: "simpleACavatarCarousel",
divClick: ' ',
slideCount: slideCount,
centerMode: true,
needsText: false,
selectable: false
};
var divNroot = JqueryRenderFactory.render( settings, $scope, $compile );
avatarCarousel = divNroot.div;
//divNroot.root.append( avatarCarousel );
$compile( divNroot.root )( $scope );
CanvasRendererFactory.renderAvatarsToCanvasesInCarousel( vm.avatars, avatarCarousel );
avatarCarousel.owlCarousel( {
animateIn: 'fadeIn',
animateOut: 'fadeOut',
items: 5,
center: true,
loop: false,
margin: 10,
mouseDrag: false,
touchDrag: false,
nav: false
} );
var items = $( '.owl-item' );
items.removeClass( 'medium' );
items.eq( 1 ).addClass( 'medium' );
avatarCarousel.on( 'translate.owl.carousel', function ( e ) {
var index = e.item.index;
var items = $( '.owl-item' );
items.removeClass( 'medium' );
items.eq( index - 1 ).addClass( 'medium' );
items.eq( index + 1 ).addClass( 'medium' );
} );
}
function renderCanvasesInCarousel( carousel ) {
var canvasArray = carousel.find( "canvas" );
for (var i = 0; i < vm.avatars.length; i++) {
var canvas = new fabric.StaticCanvas( canvasArray[i] );
canvasArray[i].removeAttribute( 'style' );
WrapUnwrapFactory.unwrapAvatar( vm.avatars[i], {x: canvas.width, y: canvas.height},
function ( canvas ) {
return function ( array ) {
CanvasRendererFactory.drawAvatar( canvas, array );
}
}( canvas )
);
}
}
function bindCarousels() {
var changing = false;
var changed;
var duration = 300;
avatarCarousel.on( 'changed.owl.carousel', function ( e ) {
if (!changing) {
changing = true;
miniatureCarousel.trigger( 'to.owl.carousel', [e.item.index, 0] );
changing = false;
}
} );
miniatureCarousel.on( 'click', '.owl-item', function () {
if (!transitionInProgress) {
var index = $( this ).index();
avatarCarousel.trigger( 'to.owl.carousel', [index, 0] );
}
} );
miniatureCarousel.on( 'translate.owl.carousel', function ( e ) {
transitionInProgress = true;
} );
miniatureCarousel.on( 'translated.owl.carousel', function ( e ) {
transitionInProgress = false;
} );
miniatureCarousel.on( 'changed.owl.carousel', function ( e ) {
if (!changing) {
changing = true;
avatarCarousel.trigger( 'to.owl.carousel', [e.item.index, 0] );
refresh( e.item.index );
if (!$scope.$$phase) {
$scope.$apply();
}
changing = false;
}
} );
}
function initpipelineNames() {
vm.avatars.forEach(function(avatar) {
var resultArray = [];
PremadeAvatarManager.getPipeline(avatar).forEach(function(pipeline) {
resultArray.push(pipeline.name);
});
pipelineNames[avatar.orderNumber] = resultArray;
});
AvatarConstructorFactory.SimpleACpipelineNames( pipelineNames );
}
function updateViewModel() {
vm.backButtonHidden = ( pipelineNumber == -1 );
}
function updateMiniatures( avatar ) {
return new Promise( function ( resolve ) {
PremadeAvatarManager.getAvatarArray( avatar, pipelineNumber ).then( function ( collection ) {
vm.avatars = collection;
console.log(avatars);
if (pipelineNumber == -1) {
initialAvatars = collection;
}
// var index = ( pipelineNumber == -1 && selectedType != undefined ) ? selectedType : 0;
updateViewModel();
updateAvatarConstructorFactory();
updateCarousels();
selectMiniatureCarousel();
switchZoom();
if (!$scope.$$phase) {
$scope.$apply();
}
resolve()
} );
} );
//return (vm.avatars != undefined);
}
function changeLocationAfterLoaded( avatar ) {
AvatarConstructorFactory.SelectedAvatarToFullAC( avatar );
$location.path( '/fullac' );
if (!$scope.$$phase) {
$scope.$apply();
}
}
function toFullAC( avatar ) {
var defaultTypes = PartsFactory.getDefaultPartTypes( avatar.orderNumber );
var avatarTypes = WrapUnwrapFactory.unwrapTypes( avatar );
var filtered = _.filter( defaultTypes, function ( type ) {
return avatarTypes.indexOf( type ) >= 0; // contains
} );
var typesStack = PartsFactory.getPartsByTypeArray( avatar.orderNumber, filtered );
AvatarConstructorFactory.TypesStack( typesStack );
changeLocationAfterLoaded( avatar );
}
function refresh( index ) {
index = ( index == undefined ) ? 0 : index;
if (vm.avatars[index].selected) {
vm.select( index );
}
vm.select( index );
}
function updateAvatarConstructorFactory() {
AvatarConstructorFactory.SimpleACavatarMiniatures( vm.avatars );
AvatarConstructorFactory.SimpleACpipelineNumber( pipelineNumber );
}
function switchZoom() {
var items = $( '.avatar-gallery .owl-item' );
if (pipelineNumber > -1 && pipelineZooms == undefined) {
var avatar = getSelectedAvatar();
var collection = PartsFactory.findCollection( avatar.orderNumber);
pipelineZooms = [];
collection.pipeline.forEach( function ( pipeline ) {
if (pipeline.zoom) {
pipelineZooms.push( true );
} else {
pipelineZooms.push( false );
}
} );
setZoom();
} else if (pipelineNumber > -1) {
setZoom();
}
function setZoom() {
if (pipelineZooms[pipelineNumber + 1]) {
items.addClass( 'zoom' );
} else {
items.removeClass( 'zoom' );
}
}
}
function activate() {
PartsFactory.registerCollectionObserverCallback( internalActivate );
if (PartsFactory.areCollectionsInitialized()) {
internalActivate();
}
function internalActivate() {
if (!AvatarConstructorFactory.isInit()) {
pipelineNumber = -1;
updateMiniatures().then( function () {
initpipelineNames();
updateText();
if (!$scope.$$phase) {
$scope.$apply();
}
vm.selectNext()
} )
} else {
updateViewModel();
pipelineNumber = AvatarConstructorFactory.SimpleACpipelineNumber();
vm.avatars = AvatarConstructorFactory.SimpleACavatarMiniatures();
pipelineNames = AvatarConstructorFactory.SimpleACpipelineNames();
updateCarousels();
selectMiniatureCarousel();
switchZoom();
updateText();
refresh();
if (!$scope.$$phase) {
$scope.$apply();
}
vm.selectNext()
}
}
}
activate();
}
})();
and the view
<div ng-controller="simpleACController as simpleAC" class="inner-bg clearfix">
<div class="inner avatar-generator">
<div class="character-bg">
<div class="layer-one"></div>
<div class="layer-two"></div>
</div>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="avatar-gallery">
<div class="controls">
<div class="add-name">
<form action="">
<input type="text"
class="cell"
ng-hide="simpleAC.showCarousel"
ng-model="simpleAC.avatarName">
</form>
</div>
</div>
<div class="ag-controls">
<i class="ag-prev" ng-show="simpleAC.showCarousel && simpleAC.showPreviousButton"
ng-click="simpleAC.selectPrevious()">prev</i>
<i class="ag-next" ng-show="simpleAC.showCarousel && simpleAC.showNextButton"
ng-click="simpleAC.selectNext()">next</i>
</div>
<div id="avatars-carousel-root"></div>
</div>
</div>
</div>
<div class="avatar-details">
<div class="container">
<div class="title">
<span class="back button"
ng-hide="simpleAC.backButtonHidden"
ng-click="simpleAC.prevPipeLine()">back</span>
<h2 ng-bind="simpleAC.heading">Select avatar type</h2>
<span class="next button"
ng-show="simpleAC.showCarousel"
ng-click="simpleAC.nextPipeLine()">next</span>
</div>
<div class="avatar-styles">
<div ng-show="simpleAC.showCarousel">
<div class="as-controls">
<i class="as-prev" ng-show="simpleAC.showPreviousButton"
ng-click="simpleAC.selectPrevious()">prev</i>
<i class="as-next" ng-show="simpleAC.showNextButton" ng-click="simpleAC.selectNext()">next</i>
</div>
<div id="miniatures-carousel-root">
</div>
</div>
<div ng-show="!simpleAC.showCarousel">
<div class="done-controls">
<a class="btn fakelink" ng-click="simpleAC.toFullAC()">Edit details</a>
<a class="btn button-blue fakelink" ng-click="simpleAC.saveAvatar()">Save</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
You can sort your collection with the angular's filter "orderBy".
Ref : http://justinklemm.com/angularjs-filter-ordering-objects-ngrepeat/
If you want use this filter in javascript and not on ng-repeat
$filter('orderBy')(array, expression, reverse)
https://docs.angularjs.org/api/ng/filter/orderBy

Angular sets input field to ng-invalid-min when trying to update it dynamically

I am trying to update an <input type="number"> field depending on the chosen option in a <select>, as well as its min="" and max="", but the the input field gets red and empty : ionic puts these classes in the markup :
class="ng-valid-max ng-valid-number ng-dirty ng-invalid ng-invalid-min ng-invalid-required"
The console.logs on the typeof the input value indicates that they are indeed numbers.
Edit: If I comment the snippet of code under : //initialise the values in the weight and unit fields
then the bug disappears...
template.html
<div id="weightdata" class="row">
<div id="weighttext"> <!-- class="col-20 enteryourweighttext"> -->
My weight:
</div>
<input id="weightinput" type="number" name="userweight" min="{{data.minWeight}}" max="{{data.maxWeight}}" ng-model="data.userweight" ng-change="saveUserWeight()" required></input>
<div id="weightunitradios">
<ion-checkbox class="checkboxes" ng-model="data.weightunit" ng-true-value="kg" ng-false-value="lbs" ng-change="saveWeightUnit(); convertWeightInput();">kg</ion-checkbox>
<ion-checkbox class="checkboxes" ng-model="data.weightunit" ng-true-value="lbs" ng-false-value="kg" ng-change="saveWeightUnit(); convertWeightInput();">lbs</ion-checkbox>
</div>
</div>
controllers.js:
.controller('WeightlevelCtrl', function($scope, $ionicPopup, $timeout, sessionService) {
//initialise the values in the weight and unit fields
console.log(sessionService.get('weightunit'))
if (sessionService.get('weightunit')) {
$scope.data.weightunit = sessionService.get('weightunit');
console.log(sessionService.get('weightunit') );
} else {
$scope.data.weightunit = 'kg';
};
if ($scope.data.weightunit === 'kg'){
$scope.data.minWeight="30";
$scope.data.maxWeight="140";
} else {
$scope.data.minWeight="65";
$scope.data.maxWeight="310";
}
if (sessionService.get('userWeight')) {$scope.data.userweight = sessionService.get('userWeight') } else {$scope.data.userweight = 70};
if (sessionService.get('userLevel')) {$scope.data.levelvalue = sessionService.get('userLevel') } else {$scope.data.levelvalue = 5};
if (sessionService.get('weightunit')) {
$scope.data.weightunit = sessionService.get('weightunit');
console.log(sessionService.get('weightunit') );
} else {
$scope.data.weightunit = 'kg';
};
if ($scope.data.weightunit === 'kg'){
$scope.data.minWeight="30";
$scope.data.maxWeight="140";
} else {
$scope.data.minWeight="65";
$scope.data.maxWeight="310";
}
if (sessionService.get('userWeight')) {$scope.data.userweight = sessionService.get('userWeight') } else {$scope.data.userweight = 70};
if (sessionService.get('userLevel')) {$scope.data.levelvalue = sessionService.get('userLevel') } else {$scope.data.levelvalue = 5};
$scope.convertWeightInput = function () {
if ($scope.data.weightunit === 'kg'){
$scope.data.minWeight="30";
$scope.data.maxWeight="140";
$scope.data.userweight = parseFloat(Math.round(lbs2kg($scope.data.userweight)));
console.log(typeof $scope.data.userweight);
console.log($scope.data.userweight);
} else {
$scope.data.minWeight="65";
$scope.data.maxWeight="310";
$scope.data.userweight = parseFloat(Math.round(kg2lbs($scope.data.userweight)));
console.log(typeof $scope.data.userweight);
console.log($scope.data.userweight);
}
}
$scope.saveWeightUnit = function() {
sessionService.persist('weightunit', $scope.data.weightunit);
}
$scope.saveUserWeight = function() {
sessionService.persist('userWeight', $scope.data.userweight);
}
$scope.saveUserLevel = function() {
sessionService.persist('userLevel', $scope.data.levelvalue);
}
})
Screenshot:
as seen here https://github.com/angular/angular.js/issues/2404
we need to use ng-min and ng-max instead of min and max.
Actually I had to split the process into two functions like this :
if ($scope.data.weightunit === 'kg'){
$scope.data.minWeight=30;
$scope.data.maxWeight=140;
} else {
$scope.data.minWeight=65;
$scope.data.maxWeight=310;
}
if (sessionService.get('userWeight')) {$scope.data.userweight = parseInt(sessionService.get('userWeight')) } else {$scope.data.userweight = 70};
if (sessionService.get('userLevel')) {$scope.data.levelvalue = parseInt(sessionService.get('userLevel')) } else {$scope.data.levelvalue = 5};
$scope.changeMinMax = function () {
if ($scope.data.weightunit === 'kg'){
$scope.data.minWeight=30;
$scope.data.maxWeight=140;
} else {
$scope.data.minWeight=65;
$scope.data.maxWeight=310;
}
}
$scope.convertWeightInput = function () {
if ($scope.data.weightunit === 'kg'){
$scope.data.userweight = parseFloat(Math.round(lbs2kg($scope.data.userweight)));
console.log(typeof $scope.data.userweight);
console.log($scope.data.userweight);
} else {
$scope.data.userweight = parseFloat(Math.round(kg2lbs($scope.data.userweight)));
console.log(typeof $scope.data.userweight);
console.log($scope.data.userweight);
}
}

Converting CSS Sticky Plugin Into Angular Directive

I am trying to use this Sticky CSS plugin with an Angular Directive. I tried wrapping this code into a Directive but no luck yet getting it to work.
Here is the CodePen of the plugin without Angular - http://codepen.io/chrissp26/pen/gBrdo
and this is what I have so far.
Any help or guidance would be greatly appreciated.
app.directive('sticky', function() {
return function stickyTitles(stickies) {
this.load = function() {
stickies.each(function(){
var thisSticky = jQuery(this).wrap('<div class="followWrap" />');
thisSticky.parent().height(thisSticky.outerHeight());
jQuery.data(thisSticky[0], 'pos', thisSticky.offset().top);
});
}
this.scroll = function() {
stickies.each(function(i){
var thisSticky = jQuery(this),
nextSticky = stickies.eq(i+1),
prevSticky = stickies.eq(i-1),
pos = jQuery.data(thisSticky[0], 'pos');
if (pos <= jQuery(window).scrollTop()) {
thisSticky.addClass("fixed");
if (nextSticky.length > 0 && thisSticky.offset().top >= jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight()) {
thisSticky.addClass("absolute").css("top", jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight());
}
} else {
thisSticky.removeClass("fixed");
if (prevSticky.length > 0 && jQuery(window).scrollTop() <= jQuery.data(thisSticky[0], 'pos') - prevSticky.outerHeight()) {
prevSticky.removeClass("absolute").removeAttr("style");
}
}
});
}
}
return function(){
var newStickies = new stickyTitles(jQuery(".followMeBar"));
newStickies.load();
jQuery(window).on("scroll", function() {
newStickies.scroll();
});
};
});
try this:
<sticky>
<div class="followMeBar">a</div>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<div class="followMeBar">b</div>
</sticky>
directive:
app.directive('sticky', function() {
var stickyTitles = function (stickies) {
this.load = function() {
stickies.each(function() {
var thisSticky = jQuery(this).wrap('<div class="followWrap" />');
thisSticky.parent().height(thisSticky.outerHeight());
jQuery.data(thisSticky[0], 'pos', thisSticky.offset().top);
});
}
this.scroll = function() {
stickies.each(function(i) {
var thisSticky = jQuery(this),
nextSticky = stickies.eq(i + 1),
prevSticky = stickies.eq(i - 1),
pos = jQuery.data(thisSticky[0], 'pos');
if (pos <= jQuery(window).scrollTop()) {
thisSticky.addClass("fixed");
if (nextSticky.length > 0 && thisSticky.offset().top >= jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight()) {
thisSticky.addClass("absolute").css("top", jQuery.data(nextSticky[0], 'pos') - thisSticky.outerHeight());
}
} else {
thisSticky.removeClass("fixed");
if (prevSticky.length > 0 && jQuery(window).scrollTop() <= jQuery.data(thisSticky[0], 'pos') - prevSticky.outerHeight()) {
prevSticky.removeClass("absolute").removeAttr("style");
}
}
});
}
}
return {
restrict: 'E',
link: function(scope, element, attrs) {
var newStickies = new stickyTitles($(element).find(".followMeBar"));
newStickies.load();
jQuery(window).on("scroll", function() {
newStickies.scroll();
});
}
};
http://plnkr.co/edit/13w5e7n0ReWoaO8513K5?p=preview

How to do the Logic behind the next button in angularjs wizard

I have a customers.create.html partial bound to the WizardController.
Then I have 3 customers.create1,2,3.html partial files bound to WizardController1,2,3
Each WizardController1,2 or 3 has an isValid() function. This function determines wether the user can proceed to the next step.
The next button at the bottom of the pasted html should be disabed if ALL ? isValid() functions are false...
Thats my question but the same time that seems not correct to me.
I guess I am not doing the Wizard correctly...
Can someone please guide me how I should proceed with the architecture that the bottom next button is disabled when the current step isValid function returns false, please.
How can I make a connection from the WizardController to any of the WizardController1,2 or 3 ?
Is Firing an event like broadcast a good direction?
<div class="btn-group">
<button class="btn" ng-class="{'btn-primary':isCurrentStep(0)}" ng-click="setCurrentStep(0)">One</button>
<button class="btn" ng-class="{'btn-primary':isCurrentStep(1)}" ng-click="setCurrentStep(1)">Two</button>
<button class="btn" ng-class="{'btn-primary':isCurrentStep(2)}" ng-click="setCurrentStep(2)">Three</button>
</div>
<div ng-switch="getCurrentStep()" ng-animate="'slide'" class="slide-frame">
<div ng-switch-when="one">
<div ng-controller="WizardController1" ng-include src="'../views/customers.create1.html'"></div>
</div>
<div ng-switch-when="two">
<div ng-controller="WizardController2" ng-include src="'../views/customers.create2.html'"></div>
</div>
<div ng-switch-when="three">
<div ng-controller="WizardController3" ng-include src="'../views/customers.create3.html'"></div>
</div>
</div>
<a class="btn" ng-click="handlePrevious()" ng-show="!isFirstStep()">Back</a>
<a class="btn btn-primary" ng-disabled="" ng-click="handleNext(dismiss)">{{getNextLabel()}}</a>
'use strict';
angular.module('myApp').controller('WizardController', function($scope) {
$scope.steps = ['one', 'two', 'three'];
$scope.step = 0;
$scope.wizard = { tacos: 2 };
$scope.isFirstStep = function() {
return $scope.step === 0;
};
$scope.isLastStep = function() {
return $scope.step === ($scope.steps.length - 1);
};
$scope.isCurrentStep = function(step) {
return $scope.step === step;
};
$scope.setCurrentStep = function(step) {
$scope.step = step;
};
$scope.getCurrentStep = function() {
return $scope.steps[$scope.step];
};
$scope.getNextLabel = function() {
return ($scope.isLastStep()) ? 'Submit' : 'Next';
};
$scope.handlePrevious = function() {
$scope.step -= ($scope.isFirstStep()) ? 0 : 1;
};
$scope.handleNext = function(dismiss) {
if($scope.isLastStep()) {
dismiss();
} else {
$scope.step += 1;
}
};
});
durandalJS wizard sample code which could be used to rewrite a wizard for angularJS:
define(['durandal/activator', 'viewmodels/step1', 'viewmodels/step2', 'knockout', 'plugins/dialog', 'durandal/app', 'services/dataservice'],
function (activator, Step1, Step2, ko, dialog, app, service) {
var ctor = function (viewMode, schoolyearId) {
debugger;
if (viewMode === 'edit') {
service.editSchoolyear(schoolyearId);
}
else if (viewMode === 'create') {
service.createSchoolyear();
}
var self = this;
var steps = [new Step1(), new Step2()];
var step = ko.observable(0); // Start with first step
self.activeStep = activator.create();
var stepsLength = steps.length;
this.hasPrevious = ko.computed(function () {
return step() > 0;
});
self.caption = ko.observable();
this.activeStep(steps[step()]);
this.hasNext = ko.computed(function () {
if ((step() === stepsLength - 1) && self.activeStep().isValid()) {
// save
self.caption('save');
return true;
} else if ((step() < stepsLength - 1) && self.activeStep().isValid()) {
self.caption('next');
return true;
}
});
this.isLastStep = function() {
return step() === stepsLength - 1;
}
this.next = function() {
if (this.isLastStep()) {
$.when(service.createTimeTable())
.done(function () {
app.trigger('savedTimeTable', { isSuccess: true });
})
.fail(function () {
app.trigger('savedTimeTable', { isSuccess: false });
});
}
else if (step() < stepsLength) {
step(step() + 1);
self.activeStep(steps[step()]);
}
}
this.previous = function() {
if (step() > 0) {
step(step() - 1);
self.activeStep(steps[step()]);
}
}
}
return ctor;
});

Resources