AngularJS delete default empty option in directive - angularjs-directive

I have this directive that brings sub-categories by the categoryId in this application the categories are called services and the sub-categories are called services-child( just for you to know ).
ok
The File that contain the directive service-detail.php has:
<services-child serviceid="{{id}}"></services-child>
The directive :
'use strict';
app.directive('servicesChild', function ($window,$state,servChildService) {
return {
require: '^form',
restrict: 'EA',
scope: {
serviceid: '#',
},
templateUrl:'assets/views/partials/service-child.php',
link: function ($scope, $element, $attributes) {
var serviceId = $attributes.serviceid;
$scope.childs = servChildService;
servChildService.loadServiceChilds(serviceId);
$scope.serviceChilds = servChildService.serviceCH;
}
};
});
app.factory('serviceChildResource', ['$resource', function($resource) {
return $resource("/services/serviceChild/:id", {id: '#id'}, {
getChilds: {
method: 'GET'
}
});
}]);
app.service('servChildService', function(serviceChildResource) {
var self = {
'isLoading': false,
'showBlock': true,
'serviceCH': [],
'loadServiceChilds': function(serviceId){
if (!self.isLoading) {
self.isLoading = true;
var params = {
'id': serviceId,
};
self.serviceCH = [];
serviceChildResource.getChilds(params, function(data){
if(data.childs.length > 0){
angular.forEach(data.childs, function(value, key){
self.serviceCH.push(new serviceChildResource(value));
self.isLoading = false;
self.showBlock = true;
});
}else{
self.showBlock = false;
self.isLoading = false; //show the loading.
}
});
}
}
};
return self;
});
Now the services-child view is this service-child.php:
<div class="panel panel-white" ng-show="childs.showBlock">
<div class="panel-heading border-light">
<h4 class="panel-title"><span class="text-bold">Add More Services</span>
</h4>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover" id="sample-table-1">
<thead>
<tr>
<th>Service</th>
<th>Description</th>
<th>Price</th>
<th>Qty.</th>
<th>Select</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="service in serviceChilds">
<td>{{service.name}}</td>
<td ng-bind-html="service.description">
{{service.description}}
</td>
<td>${{service.price}}</td>
<td>
<qty-select ng-hide="{{service.single_service}}"
price="{{service.price}}"
id="{{service.id}}"
indexid = {{$index}}>
</qty-select>
</td>
<td><input type="checkbox" value="{{service.id}}"
ng-model="$root.servCheck[service.id]"
name="servCheckN"/></td>
</tr>
</tbody>
</table>
<div ng-show="childs.isLoading">
<span us-spinner="{radius:10, width:5, length:4, lines:8}"></span>
</div>
</div>
</div>
Ok Now in this directive I have another Directive that has the Dynamic DropDown:
as you see this is the directive:
<qty-select ng-hide="{{service.single_service}}"
price="{{service.price}}"
id="{{service.id}}"
indexid = {{$index}}>
</qty-select>
Now this directive code is this qty-select.js:
'use strict';
app.directive('qtySelect', function ($window,$state,servChildService,$localStorage,$rootScope,$timeout) {
return {
require: '^form',
restrict: 'EA',
scope: {
indexid: '#',
},
templateUrl:'assets/views/partials/qty-select.php',
link: function ($scope, $element, $attributes) {
var i = 1;
while (i < 16) {
$scope.selectObj.push({'value' : $attributes.price * i,
'label' : i + ' (' + $attributes.price * i + ')',
'price' : $attributes.price * i,
'qty' : i,
});
i += 1;
};
console.log($rootScope.priceQty);
}
};
});
So basically what this directive does is creating a select option element
but it shows the price multiply by the index Ex. 1($10) which increment one by one
Ex. 2($20) so the result is something like this: if you pass 10 to the directive in the price attribute it will show:
<select ng-init="$parent.priceQty[indexid] = selectObj[0]"
ng-model="$parent.priceQty[indexid]" ng-change="updatePrice()"
ng-options="option.label for option in selectObj"
class="ng-valid ng-not-empty ng-dirty ng-valid-parse ng-touched" style="">
<option value="?"></option>
<option label="1 (15)" value="object:77">1 (15)</option>
<option label="2 (30)" value="object:78">2 (30)</option>
<option label="3 (45)" value="object:79">3 (45)</option>
<option label="4 (60)" value="object:80">4 (60)</option>
<option label="5 (75)" value="object:81">5 (75)</option>
<option label="6 (90)" value="object:82">6 (90)</option>
<option label="7 (105)" value="object:83">7 (105)</option>
<option label="8 (120)" value="object:84">8 (120)</option>
<option label="9 (135)" value="object:85">9 (135)</option>
</select>
Now this all works fine but I don't know how to get ride of the first empty option that angular append to the dropDown.
This is the View directive for select qty-select.php
<select ng-init="$root.priceQty[indexid] = selectObj[0]"
ng-model="$root.priceQty[indexid]" ng-change="updatePrice()"
ng-options="option.label for option in selectObj" >
</select>
<!-- <select ng-model="$root.priceQty[indexid]" ng-change="updatePrice(indexid)">
<option ng-repeat="value in selectObj" value="{{value.value}}"
ng-selected="$index == 1">{{value.label}}</option>
</select> -->
As you see in the I am trying with ng-repeat and with ng-options but
i am having problems with both.
Now if I work with $scope it works but when I am trying to use root
it does not work.
I notice that in my view select i have set
<select ng-init="$root.priceQty[indexid] = selectObj[0]"
but in the html that gets populated I see
<select ng-init="$parent.priceQty[indexid] = selectObj[0]"
$parent why ? is changing it.
and I need to set these input in the rootScope because I have to submit the form and I can no access to those values if I don't set the ng-model to be sent to the rootScope.
Also I tried to do this in the qty-select.js directive
after I build the select element i added this:
$scope.priceQty = $scope.selectObj[0];
and it worked but when I use $rooScope it doesn't work it says undefined.
$rootScope.priceQty = $scope.selectObj[0];
Any Idea?
Thank you

Hi thank you very much for your feedback...
Im not using $rootScope and a fixed the problem with the empty.
I had a lot architectural mistakes and thank you for your recommendations.
I totally change the way of using the directive.
Ok the way I solved the empty option was
'use strict';
app.directive('qtySelect', function ($window,$state,servChildService,$timeout,cartService,$cookies,$rootScope) {
return {
require: '^form',
restrict: 'EA',
scope: {
indexid: '#',
serviId: '#id',
servobj: '=',
},
templateUrl:'assets/views/partials/qty-select.php',
link: function ($scope, $element, $attributes) {
var i = 1;
var index = 0;
$scope.selectObj = [];
$scope.cartArray = [];
while (i < 16) {
$scope.selectObj.push({'value' : $attributes.price * i, //the value of the option tag (obligatory)
'label' : i + ' (' + $attributes.price * i + ')', //the copy (obligatory)
'price' : $attributes.price * i,
'qty' : i,
'serviceId' : $attributes.id,
'index' : index,
});
i += 1;
index += 1;
};
$timeout(function() {
document.getElementById("qty-"+$scope.indexid)[1].selected = true
//add the attribute ng-checked to filter them later.
document.getElementById("qty-"+$scope.indexid)[1].setAttribute("selected", 'selected');
}, 10);
}
};
});
I'm targeting the element by id and I am adding timeout because
the first load return undefined it seems the DOM is not ready
at that point so adding a timeout it give it enough time to the DOM to be ready.
Also I am adding the attribute selected="selected" manually since the selected true does not add the attribute selected and I need it to select later the whole element.
$timeout(function() {
document.getElementById("qty-"+$scope.indexid)[1].selected = true
//addinf manyally the attribute selected
document.getElementById("qty-"+$scope.indexid)[1].setAttribute("selected", 'selected');
}, 10);
Thank you
If you have recommendations are welcome.
This is the html directive:
<select ng-model="priceQty" ng-change="updatePrice(indexid,serviId,this)"
name="priceQty" id="qty-{{indexid}}">
<option ng-repeat="(key, value) in selectObj" value="{{value.price}}"
qty="{{$index+1}}" service-id="{{serviId}}">
{{$index+1}} ({{value.price}})
</option>
</select>

Related

Setting 'select' as default value in dropdown using angularjs

I am trying to make a simple dropdown using angularjs and want to invoke a function when the dropdown value is changed. Only when the dropdown selection is 'A', i want to display some other elements(say 'hello') in my page.
Here is the jsfiddle http://jsfiddle.net/gkJve/932/.
Html
<div ng-controller="Ctrl">
<select id="sel" class="input-block-level" ng-model="list_category" ng-options="obj.name for obj in list_categories" ng-change="DropDownChange()">
<option value="">Select</option>
</select>
<div ng-show="showdiv">
Hello
</div>
<div>
Angular
var app = angular.module('app', []);
$scope.showdiv=false;
function Ctrl($scope) {
$scope.list_categories = [{
id: 'id1',
name: 'A'
}, {
id: 'id2',
name: 'B'
}];
$scope.DropDownChange = function() {
$scope.showdiv = angular.equals($scope.list_category.name,'A');
}
}
Result - 'Hello' is displayed when 'A' is selected and hidden when 'B' is selected. But the problem is, if I select 'A' and then change my selection to 'Select', 'hello' is still displayed. I think its because 'select' is not one of my model data value the comparison is failing. Is there any other way to make this work without adding 'select' to my model data?
use like this
<div ng-show="list_category.name == 'A'">
var app = angular.module('app', []);
$scope.showdiv=false;
function Ctrl($scope) {
$scope.list_categories = [{
id: 'id1',
name: 'A'
}, {
id: 'id2',
name: 'B'
}];
$scope.DropDownChange = function() {
if($scope.list_category == null || $scope.list_category == "")
{
$scope.showdiv = false;
}
else{
$scope.showdiv = angular.equals($scope.list_category.name,'A');
}
}
}
Fiddle

unable to call click event in template in angularjs directive

In have one common directive which will display in each and every page. Already visited page displaying as a done, So i want click event on already visited page. I added ng-click and wrote function in controller. Can anybody help why it's not working.
html
<div class="row">
<div class="col-sm-12">
<wizard-menu currentPage="searchOffering"></wizard-menu>
</div>
</div>
js
function generateMenuHtml(displayMenuItems, currentPage, businessType) {
var htmlOutput = '';
var indexOfCurrentPage = getIndexOf(displayMenuItems, currentPage, 'pageName');
if (businessType) {
htmlOutput += '<ol class="wizard wizard-5-steps">';
} else {
htmlOutput += '<ol class="wizard wizard-6-steps">';
}
angular.forEach(displayMenuItems, function (value, key) {
var htmlClass = '';
if (indexOfCurrentPage > key) {
htmlClass = 'class="done" ng-click="goToFirstPage()"';
} else if (key === indexOfCurrentPage) {
htmlClass = 'class="current"';
} else {
htmlClass = '';
}
if (key!==1){
htmlOutput += '<li ' + htmlClass + '><span translate="' + value.title + '">' + value.title + '</span></li>';
}
});
htmlOutput += '</ol>';
return htmlOutput;
}
.directive('wizardMenu',['store','WIZARD_MENU', 'sfSelect', function(store, WIZARD_MENU, Select) {
function assignPageTemplate(currentPageValue){
var storage = store.getNamespacedStore(WIZARD_MENU.LOCAL_STORAGE_NS);
var data=storage.get(WIZARD_MENU.LOCAL_STORAGE_MODEL);
var businessTypePath='offeringFilter.businessType.masterCode';
var businessTypeValue = Select(businessTypePath, data);
if(businessTypeValue!=='' && businessTypeValue==='Prepaid'){
template = generateMenuHtml(businessTypePrepaid, currentPageValue, true);
}
else{
template = generateMenuHtml(commonMenu, currentPageValue, true);
}
return template;
}
return {
require: '?ngModel',
restrict: 'E',
replace: true,
transclude: false,
scope: {
currentPage: '='
},
controller: ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
$scope.goToFirstPage = function() {
console.log('inside First Page');
};
}],
link: function(scope,element,attrs){
element.html(assignPageTemplate(attrs.currentpage));
},
template: template
};
}])
I'm unable to call goToFirstPage(). Can anybody tell what is wrong here.
Thanks in advance....
You need to compile the template. If you use Angular directive such as ng-click and you simply append them to the DOM, they won't work out of the box.
You need to do something like this in your link function:
link: function(scope,element,attrs){
element.append($compile(assignPageTemplate(attrs.currentpage))(scope));
},
And don't forget to include the $compile service in your directive.
Hope this helps, let me know!
Documentation on $compile: https://docs.angularjs.org/api/ng/service/$compile

Angularjs does not update the data retrieved from the $http service

I'm trying to retrieve some data from a service using Angularjs. Initially, I want to just show the first 10 elements. Then, when the user clicks on a button (with ng-click="next()"), I want the same function to be triggered again in order to get the next 10 elements.
Here's my controller:
Admin.controller('orders', function ($scope, $http) {
var startIndex = 0;
const count = 10;
function retrieveData(startIndex, count) {
$http({
method: 'POST',
url: '/Admin/order/GetOrders',
data: { "startIndex": startIndex, "count": count }
})
.success(function (data) {
$scope.orders = data;
startIndex = startIndex + count;
$scope.$apply();
});
};
retrieveData(startIndex, count);
$scope.next = retrieveData(startIndex, count);
};
Now, what happens is that the function retrieveData() works perfectly the first time, but when I click the button nothing happens. I know for sure that the "click" event triggers the function, because I tried to replace the code with an alert, but I don't understand why the function retrieveData() itself only works the first time.
What am I missing?
<div class="container admin" ng-controller="orders">
<table class="table">
<tbody>
<tr ng-repeat="order in orders| filter:{OrderStatus: 'Hto'} | filter:query | filter:'!'+showCancelled | orderBy:predicate:reverse">
<td>
{{order.UserName}}
</td>
<td>
<span ng-class="{ 'label label-danger' : isLate }">
{{order.OrderDate}}
</span>
</td>
<td>
{{order.Country}}
</td>
<td>
<span ng-class="{ 'label label-warning' : order.OrderStatus == 'HtoPreAuth' || order.OrderStatus == 'SalePreAuth'}">
{{order.OrderStatus}}
</span>
</td>
</tr>
</tbody>
</table>
<a href="" ng-click="retrieveData()">
next
</a>
</div>
You are supposed to make the changes inside the $apply method:
Admin.controller('orders', function ($scope, $http) {
var startIndex = 0;
const count = 10;
$scope.retrieveData() { // No parameters
$http({
method: 'POST',
url: '/Admin/order/GetOrders',
data: { "startIndex": startIndex, "count": count }
})
.success(function (data) {
startIndex = startIndex + count;
$scope.orders = data;
if(!$scope.$$phase) {
$scope.$digest();
}
});
}
$scope.retrieveData();
};
// And the view
<a ng-click="retrieveData()"></a>
Edit:
Remove the parameters from the $scope.retrieveData function since they will be undefined
if you call the function with ng-click="retrieveData()"

watch changes on JSON object properties

I'm trying to implement a directive for typing money values.
var myApp = angular.module('myApp', []);
var ctrl = function($scope) {
$scope.amount = '0.00';
$scope.values = {
amount: 0.00
};
};
myApp.directive('currency', function($filter) {
return {
restrict: "A",
require: "ngModel",
scope: {
separator: "=",
fractionSize: "=",
ngModel: "="
},
link: function(scope, element, attrs) {
if (typeof attrs.separator === 'undefined' ||
attrs.separator === 'point') {
scope.separator = ".";
} else {
scope.separator = ",";
};
if (typeof attrs.fractionSize === 'undefined') {
scope.fractionSize = "2";
};
scope[attrs.ngModel] = "0" + scope.separator;
for(var i = 0; i < scope.fractionSize; i++) {
scope[attrs.ngModel] += "0";
};
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue === oldValue) {
return;
};
var pattern = /^\s*(\-|\+)?(\d*[\.,])$/;
if (pattern.test(newValue)) {
scope[attrs.ngModel] += "00";
return;
};
}, true);
}
};
});
HTML template:
<div ng-app="myApp">
<div ng-controller="ctrl">
{{amount}}<br>
<input type="text" style="text-align: right;" ng-model="amount" currency separator="point" fraction-size="2"></input>
</div>
</div>
I want to bind the value in my input element to values.amount item in controller but the watch instruction of my directive doesn't work.
How do I leverage two-way-data-binding to watch JSON objects?
To understand problem more precise I've created a jsfiddle.
The task is the following: Add extra zeros to the input element if user put a point. I mean if the value in input element say "42" and user put there a point, so the value now is "42." two extra zeros have to be aded like this "42.00".
My problems:
If I use ng-model="amount" the logic in input element works, but amount value of outer controller doesn't update.
If I use ng-model="values.amount" for binding, neither amount of outer controller nor input element logic works.
I really have to use ng-model="values.amount" instruction, but it doesn't work and I don't know why.
Any ideas?

DropDown Menu Changing OnChange

I have 3 dropdown menu. and all 3 are interlinked. ie, if i select the values of 1st dropdown, depending on that the second dropdown should display values. depending on the selection of 2nd dropdown, 3rd dropdown should populate the values. have done for 1st and 2nd. but depending on the values of 2nd drop down am not able to populate the values of 3rd dropdown. can anyone plaz help me. i know something like this will be in JFIDDLE.com. but not able to fine the exact name to search that!
You have to use AJAX if you want that. it will be easy.
<select name="ID"
id="ID"
onchange="DoYourTaskHere(this);">
<option value="select" selected="selected">Select</option>
<c:forEach items="${A.List}" var="Variable">
<option value="${ID}">
<c:out value="${ID}" />
</option>
</c:forEach>
</select>
And in the script you write the code as follows.
function loadValue(ID) {
if (ID.value != "select") {
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera,
// Safari
ValueXmlHttpReq = new XMLHttpRequest();
} else {// code for IE6, IE5
ValueXmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}
ValueXmlHttpReq.onreadystatechange = processLoadValues;
ValueXmlHttpReq.open("POST", "getValue.htm?ID="
+ ID.value, true);
ValueXmlHttpReq.send();
} else {
var objSelect = document.getElementById("ValueId");
var currentValueListLength = objSelect.options.length;
while (currentValueListLength > 0) {
objSelect.remove(1);
currentValueListLength--;
}
var objSelect = document.getElementById("2ndDropDownWhereYouWantToPopulate");
var currentSecondValueListLength = objSelect.options.length;
while (currentSecondValueListLength > 0) {
objSelect.remove(1);
currentSecondValueListLength--;
}
}
}
Allright , here something to get you started :
<form name='cars'>
<select name='brand'></select>
<select name='model'></select>
</form>
​
and the javascript (i'm using jQuery ) :
var application_model = [
{
name: "General motors",
models: [
"model1", "model2", "model3"
]},
{
name: "Mercedes",
models: [
"model4", "model5", "model6"
]},
{
name: "Fiat",
models: [
"model7", "model8", "model9"
]}
];
var selectedBrandIndex = 0
var selectedModelIndex = 0
function render() {
// render the first combo
$('select[name=brand]').empty();
$.each(application_model, function(index, object) {
var selected = "";
if (index == selectedBrandIndex) {
selected = "selected";
}
console.log(this);
$('select[name=brand]').append("<option value='" + index + "' " + selected + ">" + object.name + "</option>");
})
// render the second combo
$('select[name=model]').empty();
$.each(application_model[selectedBrandIndex].models, function(index, object) {
var selected = "";
if (index == selectedModelIndex) {
selected = "selected";
}
console.log(this);
$('select[name=model]').append("<option value='" + index + "' " + selected + ">" + object + "</option>");
});
}
function main() {
$("select[name=brand]").bind("change", function(event) {
console.log(event.currentTarget.value);
selectedBrandIndex = event.currentTarget.value;
render();
});
render();
}
main();​
check the fiddle here :
http://jsfiddle.net/camus/MAgza/2/
cheers
you can try related combobox
You can easily setup any number of combobox instances on a single page. They can interact with each other based on certain client or server side events.
This example shows how the comboboxes can interact with each other using client-side methods and requesting the items on demand. To request the items on demand at the client-side, the requestItems() method is used.
The ViewState of the dependent comboboxes is disabled because the data required for their proper operation in this example is maintained in their ClientState.
refer http://demos.telerik.com/aspnet-ajax/combobox/examples/functionality/multiplecomboboxes/defaultcs.aspx