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
Related
I used the google charts in my angular project dashboard.
By reading the document: https://github.com/FERNman/angular-google-charts , I used the below code for getting the event(which should contain the elements of the chart which I selected)
As per the document, the select event is emitted when an element in the chart gets selected.
<google-chart (select)="onSelect($event)"></google-chart>
I used the same in my code.
Html:`
<google-chart #chart [title]="Bartitle" [type]="Bartype" [data]="Bardata" [columnNames]="BarcolumnNames"
[options]="Baroptions" [width]="Barwidth" [height]="Barheight"
(select)="onSelect($event)">
</google-chart>`
Component.Ts
this.Bartitle = 'Current and Target';
this.Bartype = 'BarChart';
this.Bardata = [
["2012", 900, 390],
["2013", 1000, 400],
["2014", 1170, 440],
["2015", 1250, 480],
["2016", 1530, 540]
];
this.BarcolumnNames = ["Year", "Current", "Target"];
this.Baroptions = {
hAxis: {
title: 'Maturity'
},
vAxis: {
title: 'Month'
},
};
this.Barwidth = 200;
this.Barheight = 200;
onSelect(event) {
console.log(event);
}
But I dont get the values which I selected..
I need the values of maturity and the year... How i get that?? Did I made any changes??
Select
The select event is emitted when an element in the chart gets selected.
<google-chart (select)="onSelect($event)"></google-chart>
The event of type ChartSelectionChangedEvent containing an array of selected values.
in component
EDIT : Based on comments
onSelect(event) {
const { row, column } = event[0];
const year = this.Bardata[row][0];
let selectedItem;
if (column === 1) {
selectedItem = "current";
}
if (column === 2) {
selectedItem = "target";
}
console.log("year", year, "SelectedItem" ,selectedItem, this.Bardata[row][column]);
}
for more info read the documentation :
https://github.com/FERNman/angular-google-charts
I have a html view that's connected to Knockout viewmodel, and displays a list of items.
Each item in the list contains a textual name field, and a numeric order field.
A user can perform a "drag and drop" action to items in the UL list.
The "drag and drop" event changes the order of the items as follows:
<div id="wrapper">
<ul data-bind="foreach:Items">
<li draggable="true"
ondragover="event.preventDefault();"
data-bind="event:{dragstart:$root.dragItem,drop:$root.dropItem}">
<label data-bind="text:name"></label>
<label data-bind="text:orderNo"></label>
<input type="text" data-bind="value:name" />
</li>
</ul>
<script type="text/javascript">
var list = [{ name: 'Red', orderNo: 0 }
, { name: 'Green', orderNo: 1 }
, { name: 'Blue', orderNo: 2 }];
function viewmodel() {
var self = this;
self.Items = ko.mapping.fromJS(list);
self.ItemToDrag = ko.observable();
self.dragItem = function (item, event) {
self.ItemToDrag(item);
return true;
}
self.dropItem = function (item, event) {
event.preventDefault();
var up = self.ItemToDrag().orderNo() > item.orderNo();
self.ItemToDrag().orderNo(up ? item.orderNo() - 0.5 : item.orderNo() + 0.5);
//order this list
self.Items.sort(function (left, right) {
return left.orderNo() == right.orderNo() ? 0 : (left.orderNo() < right.orderNo() ? -1 : 1);
});
//set integer number
for (var i = 0; i < self.Items().length; i++) {
self.Items()[i].orderNo(i);
}
}
}
var vm;
$(document).ready(function () {
vm = new viewmodel();
ko.applyBindings(vm, $("#wrapper")[0]);
});
My question is, if it is possible with Knockout to change the contents of the order field automatically when the items of the list change their order through the UI.
Something like
<ul data-bind="foreach:Items,orderKey:orderNo"></ul>
Where orderKey indicates the order of the items, and which field to update in case of order change.
I'm not sure this is exactly what you need. This is custom binding, that sorts an array from foreach binding before:
ko.bindingHandlers.foreach["after"] = ["orderKey"];
ko.bindingHandlers.orderKey = {
update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
var key = ko.unwrap(valueAccessor());
var allBindings = allBindingsAccessor();
if("foreach" in allBindings) {
var array = ko.unwrap(allBindings.foreach);
array.sort(function(a, b) { return a[key] > b[key]; });
allBindings.foreach = array;
}
}
};
// The model
var model = { Items: ko.observableArray([{text: 3}, {text: 1}, {text: 2}]) };
// Apply
ko.applyBindings(model);
// This simulate changes in observableArray
setTimeout(function() { model.Items.push({text: 0}) }, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<ul data-bind="foreach: Items, orderKey: 'text'">
<li data-bind="text: text"></li>
</ul>
No, there is no specific binding for that use case. In knockout, however, it is simple to write a custom binding. See the documentation. In the company I'm working for, we're using a knockout-based framework (developed by us) with tons of custom bindings, some of them really complex.
I just started to create such a binding for your use case. But I realized, it won't fit the purpose unless you have dozens of such lists.
What you can do, however, is to sort put the actual sorting into a knockout computed and just do the updating of the sort index in your drop function. See example below and don't hesitate to ask if something is not clear.
var list = [{ name: 'Red', orderNo: 0 }
, { name: 'Green', orderNo: 1 }
, { name: 'Blue', orderNo: 2 }];
function viewmodel() {
var self = this;
self._items = ko.mapping.fromJS(list);
self.Items = ko.pureComputed(function () {
return self._items().sort(function (a, b) {
return a.orderNo() < b.orderNo() ? -1 : 1;
});
});
self.ItemToDrag = ko.observable();
self.dragItem = function (item, event) {
self.ItemToDrag(item);
return true;
}
self.dropItem = function (item, event) {
event.preventDefault();
var up = self.ItemToDrag().orderNo() > item.orderNo();
self.ItemToDrag().orderNo(up ? item.orderNo() - 0.5 : item.orderNo() + 0.5);
}
}
var vm;
$(document).ready(function () {
vm = new viewmodel();
ko.applyBindings(vm, $("#wrapper")[0]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div id="wrapper">
<ul data-bind="foreach:Items">
<li draggable="true"
ondragover="event.preventDefault();"
data-bind="event:{dragstart:$root.dragItem,drop:$root.dropItem}">
<label data-bind="text:name"></label>
<label data-bind="text:orderNo"></label>
<input type="text" data-bind="value:name" />
</li>
</ul>
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>
I want to show spaces in the div options which I am appending multiple properties.
The spaces are not shown.
Below is my code
<select id="sel" class="input-block-level" ng-model="list_category" ng-options="obj.id as (obj.name + ' ' + obj.id) for obj in list_categories.data">
<option value="">Other</option>
</select>
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.list_categories = {
data: [{
id: 'id1',
name: 'name1'
}, {
id: 'id2',
name: 'name2'
}]
};
$scope.list_category = 'id2';
}
The spaces are not showing up if i append + ' ' + Should i add any CSS for this ??
use instead of '' or set css white-space:pre
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?