Binding function to element after adding it to DOM - AngularJS - javascript

I'm building small shop app in AngularJS.
I'm adding products to the basket in directive like this:
app.directive('cart', function () {
return {
restrict: 'E',
controller: 'CartController',
templateUrl: '/templates/cart.html',
link: function (scope, element, attrs) {
scope.add_product_to_cart = function (id) {
//show cart element
if (document.getElementsByClassName('cart')[0].style.display == "")
document.getElementsByClassName('cart')[0].style.display = "block";
//find products that match id
for (var i = 0; i < products.length; i++) {
if (products[i].productId == id) {
//check if product is already in cart
var product_in_cart = false;
for (var j = 0; j < scope.products.length; j++) {
if (scope.products[j].productId == id) {
product_in_cart = j;
break;
}
}
if (product_in_cart === false) {
scope.products.push({
productId: products[i].productId,
title: products[i].title,
price: products[i].price,
imageUrl: products[i].imageUrl,
categoryId: products[i].categoryId,
count: 1
});
} else
scope.products[j].count++;
}
}
}
scope.delete_product = function (id) {
for (var i = 0; i < scope.products.length; i++) {
if (id == scope.products[i].productId) {
if (scope.products[i].count == 1) {
scope.products.splice(i, 1);
} else {
scope.products.count -= 1;
}
}
}
}
}
};
});
I can't execute scope.delete_product function, because it gives me "Error: [$parse:syntax]" error.
My template looks like this:
<span class="cart-delete-product" ng-click="delete_product({{ product.productId }})">{{ delete_product }}</span>
How can I bind scope.delete_product to element after DOM updates?

Related

update priority of queue in javascript

in hasValue class, why return is not working? when i try with console.log and alert, it worked.
want to implement function like priorityQueue.changePriority("Sheru", 1); changePriority class is not working.
commented code is code i tried to implement the changes i.e. i want to change the priority of existing item present in queue. Could anyone please help?
class QElement {
constructor(element, priority) {
this.element = element;
this.priority = priority;
}
}
class PriorityQueue {
constructor() {
this.items = [];
}
isEmpty() {
return this.items.length == 0;
}
add(element, priority) {
var qElement = new QElement(element, priority);
var contain = false;
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].priority > qElement.priority) {
this.items.splice(i, 0, qElement);
contain = true;
break;
}
}
if (!contain) {
this.items.push(qElement);
}
}
peek() {
if (this.isEmpty())
return "No elements in Queue";
return this.items[0];
}
poll() {
if (this.isEmpty())
return "Underflow";
return this.items.shift();
}
/*changePriority(firstTerm, secondTerm)
{
let xxx = new QElement(firstTerm, secondTerm);
for (let i = 0; i < this.items.length; i++){
if (this.items[i].element === firstTerm){
this.items[i].priority = secondTerm;
this.items.splice(i, 0, xxx);
}
}
this.items.push(xxx);
}*/
hasValue(args) {
let status = false;
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].element === args) {
status = true;
}
}
console.log(status);
}
size() {
if (this.isEmpty())
return "Underflow";
return this.items.length;
}
printPQueue() {
var str = "";
for (var i = 0; i < this.items.length; i++)
str += this.items[i].element + " ";
return str;
}
}
var priorityQueue = new PriorityQueue();
console.log(priorityQueue.isEmpty());
console.log(priorityQueue.peek());
priorityQueue.add("Sumit", 2);
priorityQueue.add("Gourav", 1);
priorityQueue.add("Piyush", 1);
priorityQueue.add("Sunny", 2);
priorityQueue.add("Sheru", 3);
console.log(priorityQueue.printPQueue());
console.log(priorityQueue.peek().element);
console.log(priorityQueue.poll().element);
priorityQueue.add("Sunil", 2);
console.log(priorityQueue.size());
priorityQueue.hasValue('Sumit');
console.log(priorityQueue.printPQueue());
priorityQueue.changePriority("Sheru", 1);
console.log(priorityQueue.printPQueue());
You missing return keyword. This just works:
hasValue(args) {
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].element === args) {
return true;
}
}
return false;
}
I did not understand the idea how your changePriority function should work. Just find the element and move it up or down based on priority change:
swap(a, b) {
let tmp = this.items[a];
this.items[a] = this.items[b];
this.items[b] = tmp;
}
changePriority(firstTerm, secondTerm) {
let i = 0;
while (i < this.items.length) {
if (this.items[i].element === firstTerm) {
if (secondTerm < this.items[i].priority) {
// move up
this.items[i].priority = secondTerm;
while (i > 0 && this.items[i - 1].priority > secondTerm) {
this.swap(i - 1, i);
i--;
}
} else if (secondTerm > this.items[i].priority) {
// move down
this.items[i].priority = secondTerm;
while (i < this.items.length - 1 && this.items[i + 1].priority < secondTerm) {
this.swap(i + 1, i);
i++;
}
}
break;
}
i++;
}
}

Javascript: 'click' button doesn't work when pressed?

I'm in practical JS by Gordon Zhu (wwww.watchandcode.com). I don't understand why the buttons appear in the browser/UI/client side, but for some reason, when I go to click it the button makes an animation and does nothing. You know, kind of like one of those old, old software programs some companies and job boards use.
I'm getting an
VM3950:1 Uncaught ReferenceError: todoList is not defined
at :1:1
when defining todoList by displayTodos.addTodo('first');
var todoList = {
todos: [],
displayTodos: function() {
if (this.todos.length === 0) {
console.log('Your todo list is empty!');
} else {
console.log('My todos:');
for (var i = 0; i < this.todos.length; i++) {
if (this.todos[i].completed === true) {
console.log('(x)', this.todos[i].todoText);
} else {
console.log('( )', this.todos[i].todoText);
}
}
}
},
addTodo: function(todoText) {
this.todos.push({
todoText: todoText,
completed: false
});
this.displayTodos();
},
changeTodo: function(position, todoText) {
this.todos[position].todoText = todoText;
this.displayTodos();
},
deleteTodo: function(position) {
this.todos.splice(position, 1)
this.displayTodos();
},
toggleCompleted: function(position) {
var todo = this.todos[position];
todo.completed = !todo.completed;
this.displayTodos();
},
toggleAll: function() {
var totalTodos = this.todos.length;
var completedTodos = 0;
for (var i = 0; i < totalTodos; i++) {
if (this.todos[i].completed === true) {
completedTodos++;
}
}
}
if (completedTodos === totalTodos) {
for (var i = 0; i < totalTodos; i++) {
this.todos[i].completed = false;
}
} else {
for (var i = 0; i < totalTodos; i++); {
this.todos[i].completed = true;
}
}
this.displayTodos();
}
};
var displayTodosButton = document.getElementById('displayTodosButton');
displayTodosButton.addEventListener('click', function() {
todoList.displayTodos();
});
<h1>Todo List</h1>
<button id = "displayTodosButton">Display Todos</button>
<button id = "toggleAllButton">Toggle All</button>
this keyword works different in Javascript compared in other languages, this is determined by how a function is called (runtime binding). In your handlers you may called todoList.displayTodos() or you can also update to use arrow functions, because in arrow functions, this retains the value of the enclosing lexical context's this see your code below:
var todoList = {
todos: [],
displayTodos: function() {
if (this.todos.length === 0) {
console.log('Your todo list is empty!');
} else {
console.log('My todos:');
for (var i = 0; i < this.todos.length; i++) {
if (this.todos[i].completed === true) {
console.log('(x)', this.todos[i].todoText);
} else {
console.log('( )', this.todos[i].todoText);
}
}
}
},
addTodo: function(todoText) {
this.todos.push({
todoText: todoText,
completed: false
});
this.displayTodos();
},
changeTodo: function(position, todoText) {
this.todos[position].todoText = todoText;
this.displayTodos();
},
deleteTodo: function(position) {
this.todos.splice(position, 1)
this.displayTodos();
},
toggleCompleted: function(position) {
var todo = this.todos[position];
todo.completed = !todo.completed;
this.displayTodos();
},
toggleAll: function() {
var totalTodos = this.todos.length;
var completedTodos = 0;
for (var i = 0; i < totalTodos; i++) {
if (this.todos[i].completed === true) {
completedTodos++;
}
}
if (completedTodos === totalTodos) {
for (var i = 0; i < totalTodos; i++) {
this.todos[i].completed = false;
}
} else {
for (var i = 0; i < totalTodos; i++); {
this.todos[i].completed = true;
}
}
this.displayTodos();
}
};
var displayTodosButton = document.getElementById('displayTodosButton');
var toggleAllButton = document.getElementById('toggleAllButton');
displayTodosButton.addEventListener('click', () => {
this.todoList.displayTodos();
});
toggleAllButton.addEventListener('click', () => {
this.todoList.toggleAll();
});
<h1>Todo List</h1>
<button id = "displayTodosButton">Display Todos</button>
<button id = "toggleAllButton">Toggle All</button>
The error display is : Uncaught ReferenceError: todoList is not defined
Which means exactly that when referencing the todoList object from the this context it won't be defined because the this here is your html element not the window object thus if you want to refer to your object you have to remove the this keyword that's all (The javascript this keyword has a different behavior from many other languages).
Finally if you want to read more about the this behavior in java-script you can visit : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

How to use function outside $scope ( error: function isn't defined)

I have a rzslider which takes in true or false for disabled. I want disable to be true based on a function. So I want to make it disabled:$scope.truthy
I have a function called checkDupsName() checkDupsName should return true if there is a duplicate, false otherwise. I set my $scope.truthy variable to be true if the function returns true but the issue is, when I call it outside this function ( in my slider), it's ALWAYS false.
$scope.checkDupsName = function(Mylist) {
var i, j, n;
n = Mylist.length;
for (i = 0; i < n; i++) {
for (j = i + 1; j < n; j++) {
if (Mylist[i] === Mylist[j]) {
return true;
}
}
}
return false;
};
$scope.truthy=false;
$scope.nameList = [];
var Types = [];
$scope.loadRuleList = function() {
PrioritizeService.getData($scope.prioritizeURL).
then(function(response) {
if (response) {
Types = response;
}
for (var k = 0; k < Types.length; k++) {
$scope.nameList.push(Types[k].name);
}
if($scope.checkDupsName($scope.nameList)) {
$scope.truthy=true;
}
};
$scope.slider = {
value: 1,
options: {
floor: 0,
ceil: 3,
showTicks: true,
showTicksValues: true,
disabled:$scope.truthy
}
};
You are defining it inside of your function that is being called by then. You should move it outside and make it a function defined/declared on the scope instead and have it take the data it uses as a parameter.
// initialize it so your code does not blow up in the case of forgotten undefined or null check
$scope.nameList = [];
$scope.loadRuleList = function() {
var me = this;
PrioritizeService.getData($scope.MyURL).
then(function(response) {
if (response) {
Types = response;
}
// re-init the nameList field
me.nameList = [];
for (var k = 0; k < Types.length; k++) {
me.nameList.push(Types[k].name)
}
//check for duplicates
var areDups = me.checkDupsName(me.nameList);
}
}
$scope.checkDupsName = function(listToCheck) {
var i, j, n;
n = listToCheck.length;
for (i = 0; i < n; i++) {
for (j = i + 1; j < n; j++) {
if (listToCheck[i] === listToCheck[j]) {
return true;
}
}
}
return false;
}

Polymers this.$$(selector) (querySelector) for elements inside template repeat

I am simply trying to get an element inside of a template repeat by it's custom attribute. Here is the code:
<dom-module id="order-customize-sauces">
<link rel="stylesheet" href="order-customize-sauces.css"/>
<template>
<center>
<div style="width:100%; height:80px;">
<input class="customize-search" title="Filter The List By A Search Term" placeholder="Search List..." value="{{searchValue::input}}" type="text" on-keyup="filterSelections"></input>
<iron-icon icon="search" style="float:right;margin-right:5px;"></iron-icon>
</div>
<div style="height:300px;width:100%;">
<template id="saucesRepeat" is="dom-repeat" items="{{repeat}}" filter="{{filterSelections(searchValue)}}">
<paper-button data-sauce$="{{item.code}}" class$="button-{{item.SELECTED}}" raised on-click="buttonClick">{{item.name}}</paper-button>
</template>
</div>
</center>
<div style="margin-top:300px"> </div>
</template>
<script src="order-customize-sauces.js"></script>
As you can see I have "data-sauce" attribute that I want to select by. So here is what I have tried with no luck:
Try1:
var repeaters = document.querySelector('template[is="dom-repeat"]')
console.log(repeaters.querySelector("[data-sauce]"));
Try 2:
var items = Polymer.dom(this.$.saucesRepeat).node.__data__.items;
console.log(this.$$("[data-sauce]"));
Try 3:
this.$$("[data-sauce]");
Am I going about this wrong? How can I select an element inside of a template repeat? Also I am not using JQuery so if this could be javascript or polymer specific that would be great!
EDIT:
I have added the full JS code to help with the debugging.
The main function where this is the question is in the "changeButtonColor" function. This is called after a series of functions but it all starts with the "passCurrentItem" function. Also this element is a child of another element which is calling this elements function like the following:
this.$.allSaucesOptions.passCurrentItem(mynewarray);
The "allSaucesOptions" is the id given to the "order-customize-sauces" element.
Full JS Code:
Polymer({
is:"order-customize-sauces",
behaviors: [
Polymer.NeonSharedElementAnimatableBehavior
],
properties:{
animationConfig: {
value: function() {
return {
'entry':[
{
name:'fade-in-animation',
node:this
}],
'exit':[
{
name:'fade-out-animation',
node:this
}]
}
}
},
},
//--------------------------------------------------
ready:function(){
this.repeat = [];
this.currentItem = [];
this.originalArray = [];
},
//--------------------------------------------------
passCurrentList:function(list){
this.originalArray = list;
},
//--------------------------------------------------
passCurrentItem:function(array) {
var i;
this.currentItem.length = 0;
for(var i=0; i < array.length; i++) {
this.currentItem.push({name: array[i].name, code: array[i].code, price: array[i].price, SELECTED: array[i].SELECTED});
}
this.resetRepeatArray(); //everything above works!!
this.updateArray(); //update visually
},
//--------------------------------------------------
resetRepeatArray:function(){ //testing working!!!!
var i;
for(i=0; i < this.originalArray.length; i++) {
this.originalArray[i].SELECTED = 0;
}
this.repeat = this.originalArray;
},
//--------------------------------------------------
updateArray:function() {
var i;
//for(i =0; i < this.repeat.length; i++) {
//this.repeat[i] = {name: this.repeat[i].name, code: this.repeat[i].code, price: this.repeat[i].price, SELECTED: this.repeat[i].SELECTED};
//this.changeButtonColor(0,this.repeat[i].code);
//}
for(i = 0; i < this.currentItem.length; i++) {
//index = this.repeat.map(function(d) { return d['code']; }).indexOf(this.currentItem[i].code);
//this.set("repeat.index",{name: this.currentItem[i].name, code: this.currentItem[i].code,
// price: this.currentItem[i].price, SELECTED: this.currentItem[i].SELECTED});
console.log("set color "+this.currentItem[i].name);
this.changeButtonColor(this.currentItem[i].SELECTED,this.currentItem[i].code); //this isn't working
}
},
//--------------------------------------------------
changeButtonColor:function(number, id) {
var defaultClassValues = " style-scope order-customize-sauces x-scope paper-button-0";
var defaultClass ="button-";
// var items = Polymer.dom(this.$.saucesRepeat).node.__data__.items;
// console.log(this.$$("[data-sauce]"));
// var repeaters = document.querySelector('template[is="dom-repeat"]')
var element = this.$$("[data-sauce]");
console.log(element);
// for(var i=0; i < items.length; i++) {
// if(items[i].code === id) {
// Polymer.dom(this.$.saucesRepeat).node.__data__.items[i].SELECTED = number;
// }
// }
// var element = this.getElementByAttributeValue("data-sauce",id);
// if(element != null){
// element.className = defaultClass+number+defaultClassValues;
// console.log(element.className);
// }
},
//--------------------------------------------------
getElementByAttributeValue: function(attribute, value){
var matchingElements = [];
var allElements = document.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++) {
if (allElements[i].getAttribute(attribute) === value){
// Element exists with attribute. Add to array.
matchingElements.push(allElements[i]);
}
}
return matchingElements[0];
},
//--------------------------------------------------
isNumber:function(selected, number) {
selected = (selected == null)?0:selected;
if(selected == number) {
return false;
}
else {
return true;
}
},
//--------------------------------------------------
removeFromArray:function(itemName) {
var index, i;
for(i = 0; i < this.currentItem.length; i++) {
if(this.currentItem[i].name == itemName || this.currentItem[i].code == itemName) {
index = i;
break;
}
}
if(index > -1) {
this.currentItem.splice(index, 1);
}
},
//--------------------------------------------------
buttonClick:function(e){
var item = e.model.__data__.item;
item.SELECTED = this.setSelectedNumber(item.SELECTED);
var number = item.SELECTED;
if(number > 3) {//original item
this.currentItem[0] = {name: item.name, code: item.code, price: item.price, SELECTED: 4};//change original item to new values
if(this.currentItem[1] != null) { //is there another item selected?
this.removeFromArray(this.currentItem[1].code); //make original item the only thing in array
}
}
else if(number < 4) {//not original item
this.currentItem[0] = {name: this.currentItem[0].name, code: this.currentItem[0].code, price: this.currentItem[0].price, SELECTED: 4};//unselect original item -- don't remove from array
if(this.currentItem[1] == null) { //is this the first time selecting a new item?
this.currentItem.push({name: item.name, code: item.code, price: item.price, SELECTED: item.SELECTED}); //add item to a new slot in array
}
else{
this.currentItem[1] = {name: item.name, code: item.code, price: item.price, SELECTED: item.SELECTED}; //Update array slot with new information
}
}
this.updateArray();
},
//--------------------------------------------------
setSelectedNumber:function(number) {
if(number < 8 && number > 3) {
number += 1;
if(number > 7) {
number = 4;
}
}
if(number < 4 || number == null) {
number = (number == null)?0:number;
number += 1;
if(number > 3) {
number = 0;
}
}
return number;
},
//---------------------------------------------------
filterSelections:function(search) {
this.resetSauceArray();
this.changeButtonColor(this.currentItem[0].SELECTED, this.currentItem[0].code);
if(this.currentItem[1] != null){
this.changeButtonColor(this.currentItem[1].SELECTED, this.currentItem[1].code);
}
return function(item){
var string;
if (!search) return true;
if (!item) return false;
search = search.toUpperCase();
if(item.name && ~item.name.toUpperCase().indexOf(search)) {
return true;
}
else {
return false;
}
}
},
});

AngularJS directive for a multi-select

I am having trouble populating my multi-select. I am using this version of the multi-select http://davidstutz.github.io/bootstrap-multiselect/
I have looked at this stack overflow page (How can I use Bootstrap Multiselect Dropdown in AngularJS) but I am still having problems. I am trying to populate my multi-select with data that I grab from a database which gets stored in provData.
Here is my html:
<div class="col-sm-8">
<select class="form-control" multiple ht-multi-select ng-model="patient.provid" ng-options="prov.id as prov.code for prov in provData">
<option value="{{prov.id}}">{{prov.code}}</option>
</select>
</div>
Here is my directive:
templates.directive('htMultiSelect', function() {
return {
replace: true,
require: 'ngModel',
scope: {},
link:function(scope, element, attrs) {
console.log($(element));
console.log('hit');
// Below setup the dropdown:
$(element).multiselect({
enableClickableOptGroups: true
});
// Below maybe some additional setup
scope.$watch(attrs.ngModel, function () {
$(element).multiselect('refresh');
});
}
};
});
My main issue is I can not populate my multi-select with data from provData and I can not get it to set the ng-model. Any help will be greatly appreciated.
Based on Rashim's blog, this is what I tried in Plunke.
https://rashimuddin.wordpress.com/2014/07/08/multi-select-drop-down-in-angularjs/
Check this out
http://plnkr.co/edit/y8VOlgeSMOqHjFxoZU1L?p=previewenter code here
HTML
<div dropdown-multiselect="" items="FirstItems" on-close="closeAlert(val)" on-Validation="addAlert(validationMsg)" max-Selection="3" selected-items="FirstSelectedItems"></div>
Angular directive would be
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('AppCtrl', ["$scope", function($scope) {
$scope.status = {
isopen: false
};
$scope.FirstItems = [];
$scope.alerts = [{
type: 'danger',
msg: 'Oh snap! Change a few things up and try submitting again.'
}, {
type: 'success',
msg: 'Well done! You successfully read this important alert message.'
}];
$scope.addAlert = function(validationMsg) {
if ($scope.validateFire === true) {
$scope.alerts.push({
msg: validationMsg
});
}
$scope.validateFire = true;
};
$scope.closeAlert = function(val) {
$scope.alerts = [];
}
for (var i = 0; i <= 20; i++) {
var obj = [];
obj["Id"] = i;
obj["Name"] = "Name" + i;
obj["IsSelected"] = false;
$scope.FirstItems.push(obj);
}
$scope.FirstSelectedItems = [];
var removeItem = function(items, item) {
for (var index = 0; index < items.length; index++) {
if (item.Id == items[index].Id) {
item.IsSelected = false;
items.splice(index, 1);
break;
}
}
};
$scope.removeFirstItem = function(item) {
removeItem($scope.FirstSelectedItems, item);
};
$scope.removeSecondItem = function(item) {
removeItem($scope.SecondSelectedItems, item);
};}]);
Directive goes like this
app.directive('dropdownMultiselect', function() {
return {
restrict: 'A',
scope: {
items: "=",
selectedItems: "=",
maxSelection: "=",
validateFire: "=",
onValidation: '&',
onClose: '&'
},
template: "" +
"" +
"Add " +
"" +
"" +
"" +
"" +
"All " +
"None" +
"" +
"" +
"" +
" " +
"{{item.Name}}" +
"" +
"" +
"",
controller: function($scope) {
$scope.handleClick = function($event) {
$event.stopPropagation();
};
$scope.status = {
isopen: false
};
$scope.status = {
isopen: false
};
$scope.openDropdown = function($event) {
if ($scope.items !== undefined && $scope.items.length > 0) {
if ($scope.items.length > $scope.maxSelection)
$scope.showAll = false;
else
$scope.showAll = true;
for (var index = 0; index < $scope.items.length; index++) {
$scope.items[index].IsSelected = false;
}
if ($scope.selectedItems !== undefined && $scope.selectedItems.length > 0) {
for (var selectedItemIndex = 0; selectedItemIndex < $scope.selectedItems.length; selectedItemIndex++) {
for (var itemIndex = 0; itemIndex < $scope.items.length; itemIndex++) {
if ($scope.selectedItems[selectedItemIndex].Id == $scope.items[itemIndex].Id) {
$scope.items[itemIndex].IsSelected = true;
break;
}
}
}
}
}
$event.stopPropagation();
$scope.status.isopen = true;
};
$scope.selectAll = function($event) {
$scope.selectedItems = [];
angular.forEach($scope.items, function(item) {
item.IsSelected = true;
$scope.selectedItems.push(item);
});
$event.stopPropagation();
};
$scope.deselectAll = function($event) {
angular.forEach($scope.items, function(item) {
item.IsSelected = false;
});
$scope.selectedItems = [];
$event.stopPropagation();
};
$scope.selectItem = function(item) {
if (item.IsSelected === false) {
for (var index = 0; index < $scope.selectedItems.length; index++) {
if (item.Id == $scope.selectedItems[index].Id) {
item.IsSelected = false;
$scope.selectedItems.splice(index, 1);
$scope.onClose({
val: "1"
});
break;
}
}
} else {
if ($scope.selectedItems.length > $scope.maxSelection) {
item.IsSelected = false;
$scope.validationMsg = "MAX_REACHED";
$scope.validateFire = true;
$scope.onValidation({
validationMsg: "Max_Reached"
});
return;
}
$scope.selectedItems.push(item);
}
};
$scope.clickItem = function($event) {
$event.stopPropagation();
};
$scope.closeDropDown = function() {
$scope.status.isopen = false;
$event.stopPropagation();
};
}
};
});`

Categories

Resources