How to edit a table row using angluarJS? - angularjs

I'm searching for a solution to edit in-line table rows. Pretty much like this fiddle, but with populated comboboxes too.
function Ctrl($scope) {
$scope.model = {
contacts: [{
id: 1,
name: "Ben",
age: 28
}, {
id: 2,
name: "Sally",
age: 24
}, {
id: 3,
name: "John",
age: 32
}, {
id: 4,
name: "Jane",
age: 40
}],
selected: {}
};
// gets the template to ng-include for a table row / item
$scope.getTemplate = function (contact) {
if (contact.id === $scope.model.selected.id) return 'edit';
else return 'display';
};
$scope.editContact = function (contact) {
$scope.model.selected = angular.copy(contact);
};
$scope.saveContact = function (idx) {
console.log("Saving contact");
$scope.model.contacts[idx] = angular.copy($scope.model.selected);
$scope.reset();
};
$scope.reset = function () {
$scope.model.selected = {};
};
});
How can I make inline editable comboboxes ? Each line should have a name, age and a group of options.

It would be easier, and I would venture to say nicer, if you use ng-grid instead of html tables. The grid has built-in editing, and you can customize the kind of editor, such that your cell could display as plain text, but use a combo box for editing.
Here is a plunker that shows an editable combo box on the Gender column:
http://plnkr.co/edit/zsxqZNQCnpFySjSWcf1D?p=preview
{field:'gender', displayName: 'Gender', enableCellEdit: true,editableCellTemplate: '<span id="gender"><select ng-class="\'colt\' + col.index" + ng-input="COL_FIELD" ng-model="COL_FIELD" ng-options="c.value as c.name for c in genders"></select></span>'}]
And here is the documentation for ng-grid:
http://angular-ui.github.io/ng-grid/

Related

How can i always call a function on Master - Detail Kendo grid row expansion?

I am using Master detail in Kendo Grid in AngularJS.
Here is the setup of the code:
In cshtml is:
<script type="text/x-kendo-template" id="template">
<div>
<div class="orders"></div>
</div>
</script>
In the AngularJS controller is
The MasterRowId is the id of the Column in the Master row. So the Master grid is a grid with
columns Id and name
$scope.getorders = function (masterRowId)
{
myservice.getorders(masterRowId)
.then(function (result) {
for (var j = 0; j < result.data.length; j++)
$scope.ordergroupdata.push(result.data[j]);
});
}
In the master grid definition i have
......
detailTemplate: kendo.template($("#template").html()),
detailInit: $scope.detailInit,
The definition of $scope.detailInit is
$scope.detailInit = function (e)
{
$scope.MasterRowId = e.data.Id;
$scope.getorders ($scope.MasterRowId);
var orderdata = $scope.ordergroupdata;
var orderdatasource = new kendo.data.DataSource({
transport: {
read: function (e) {
e.success(orderdata);
},
update: function (e) {
e.success();
},
create: function (e) {
var item = e.orderdata;
item.Id = orderdata.length + 1;
e.success(item);
}
},
schema: {
model: {
id: "Id",
fields: {
OrderId: { type: 'string'},
}
}
},
});
e.detailRow.find(".orders").kendoGrid({
dataSource: orderdatasource,
columns: [
{ field: "OrderId", title: "OrderId" },
]
});
}
The issue is that if i click on the first row , i can retrieve the data according to the MasterRowId from my MVC action method. So If i click on the first row and the MasterRowId is for example 10 , then i get an OrderId of "1234" .
I can click on the second row with MasterRowID of 15 and it will retrieve the OrderId of "8231", BUT if i go back to the first row the data (OrderId) in the details grid is actually the data from the second row, so its "8321" NOT "1234".
How can i always call the $scope.detailInit so that i can go back to the MVC Action method and always retrieve the correct data for that particular row with that MasterRowId ?
Once i expand the row and move on to another row , the detailsInit doesnt get called any more for that row ?
detailInit is only called on first expand, but you can use detailExpand that is called every time you expand the detail table.
Offical example:
<script>
$("#grid").kendoGrid({
columns: [
{ field: "name" },
{ field: "age" }
],
dataSource: [
{ name: "Jane Doe", age: 30 },
{ name: "John Doe", age: 33 }
],
detailTemplate: "<div>Name: #: name #</div><div>Age: #: age #</div>",
detailExpand: function(e) {
console.log(e.masterRow, e.detailRow);
}
});
</script>
Docs: detailExpand
Official example: detailExpand example

How to select a value for option dropdown in angularJS UnitTest

My html code is something like this and I want to select a value from dropdown and verify the same in AngularJS UnitTestCase using Jasmine
I tried using option.value but it's not working out..
<select id="company" ng-model="company" ng-change="companychange(company)" ng-options="company.ID as company.Name for company in companies"></select>
My AngularJS UnitTestCase is something like this
//somecode has been left out
$templateCache.put('selectcompany.html', directiveTemplate);
templateHtml = $templateCache.get('selectcompany.html');
formElem = angular.element('<div>' + templateHtml + '</div>');
//console.log(directiveTemplate);
$compile(formElem)($scope);
form = $scope.companyform;
companyform = form;
form2 = $compile(directiveTemplate)($scope);
$scope.$apply('companies=[{ Name: "Gold", value:0, ID: 0 },{ Name: "Silver", value:1, ID: 1 }, { Name: "Bronze", value: 2, ID: 2 }]');
}));
describe(' company ', function () {
it('selecting a dropdown ', function () {
var options = form2.find('[id="company"]')[0];
angular.element(options["value='2'"]).trigger('click');
$scope.$digest();
console.log(options.innerHTML);
expect($scope.company).toBe(2);
});
});

Vue.js filtering on array

I am trying to filter an array using a computed property in vue.js. I would like to search on on multiple fields, name, state, tags etc.
My data:
events: [
{
id: 1,
name: 'Name of event',
url: '#',
datetime: '2017-05-10T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'ordinary',
'advanced'
]
},
{
id: 2,
name: 'Another event',
url: '#',
datetime: '2017-05-12T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'beginner'
]
},
{
id: 3,
name: 'Great event',
url: '#',
datetime: '2017-05-18T00:00:00Z',
description: 'The full text of the event',
state: 'NSW',
tags: [
'beginner'
]
}
]
},
The following function works as expected, however I cant work out how to have it search the items in 'tags' (commented out).
searchevents: function(){
let result = this.events
if (this.filterValue){
result = result.filter(event =>
event.name.toLowerCase().includes(this.filterValue.toLowerCase()) ||
event.state.toLowerCase().includes(this.filterValue.toLowerCase())
// event.tags.toLowerCase().values().includes(this.filterValue.toLowerCase())
)
}
return result
}
The following returns a blank array, this method works ok when i have done it in angular but not in vue.
searchevents2: function(){
var searchRegex = new RegExp(this.filterValue,'i')
this.events.filter(function(event){
return !self.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state)
})
}
Ideally I would either like to be able to list array items to filter by or just filter by the entire array.
Appreciate any help, first post here so be gentle. I have a lot more experience with Python than Javascript so i may also use incorrect terminology at times.
You weren't too far off.
For your searchEvents filter, you just needed to add the tag filter. Here's how you might do that.
searchevents: function(){
let result = this.events
if (!this.filterValue)
return result
const filterValue = this.filterValue.toLowerCase()
const filter = event =>
event.name.toLowerCase().includes(filterValue) ||
event.state.toLowerCase().includes(filterValue) ||
event.tags.some(tag => tag.toLowerCase().includes(filterValue))
return result.filter(filter)
}
Array.some() is a standard array method that returns true if any element of the array passes your test.
searchevents2: function(){
const searchRegex = new RegExp(this.filterValue,'i')
return this.events.filter(event =>
!this.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state))
}
With searchEvents2 you really only left an errant self in there. Either you needed to set self before you executed the filter, or, as I have done here, turned it into an arrow function.
Example.
const app = new Vue ({
el: '#app',
data: {
search: '',
userList: [
{
id: 1,
name: "Prem"
},
{
id: 1,
name: "Chandu"
},
{
id: 1,
name: "Shravya"
}
]
},
computed: {
filteredAndSorted(){
// function to compare names
function compare(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
return this.userList.filter(user => {
return user.name.toLowerCase().includes(this.search.toLowerCase())
}).sort(compare)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<ul>
<li v-for="user in filteredAndSorted">{{user.name}}</li>
</ul>
</div>

Populate Kendo Grid with Angular model list

I have an Angular app that retrieves my data from the server and would like to use the results to populate a kendo grid. I have tried to create a kendo.data.DataSource but can not get the grid to populate. Below is what I am trying.
$scope.surchargeGridOptions = {
dataSource: {
pageSize: 15,
autoSync: true,
autoBind: false,
data: $scope.model.dataSource,
}
$scope.getWaivers = function () {
waiverService.getCustomers($scope.model.customer.CustomerID).then(function (result) {
$scope.model.waivers = result.data;
$scope.model.dataSource = new kendo.data.DataSource({
data: $scope.model.waivers,
});
$scope.model.dataSource.read();
});
};
Is it possible to do this and how should I go about it?
The data source object in your options has a data property that only requires a reference to a plain array, not an entire kendo data source.
You should use k-data-source to reference your data...
<kendo-grid k-data-source="myData"></kendo-grid>
... and you don't strictly need a kendo data source to get it working...
$scope.myData = [
{ name: 'a', number: 1 },
{ name: 'b', number: 1 },
{ name: 'c', number: 1 },
{ name: 'd', number: 1 }
];
.. If you have dynamic data then a kendo observable array would be best practice.
Here is a code pen example.
Front Html Page having Grid option
<div kendo-grid="ListGrid" options="ListOptions" k-rebind="ListOptions" class="k-grid-content-border"></div>
function GridColumn() {
return [{
field: 'name',
template: "<a ng-click='ToList(this.dataItem)' class='cursor-pointer'>{{this.dataItem.name}}</a>",
title: "",
footerTemplate: "Total",
width: 200,
locked: true,
}]}
$scope.ToGeo = function (item) {
$scope.dataLoded = false;
GetResults(function (res) {
$scope.ListOptions.dataSource = new kendo.data.DataSource({
data: res,
});
$scope.ListOptions.columns = GridColumn();
$scope.ListGrid.refresh();
$scope.dataLoded = true;
})
}
where GetResults is for API call and fetching data

AngularJS matching data between json arrays and setting a selected option

$scope.opts =
{
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"}
]
}
The above is my options list array and now I set my default option.
$scope.user.unit = $scope.opts.unit[0];
The above creates the following in my html
<select class="unit ng-pristine ng-valid" data-ng-options="a.name for a in opts.unit" data-ng-model="user.unit">
<option value="0" selected="selected">px</option>
<option value="1">%</option>
</select>
When I use the below I am pulling the data that was stored in a db from the options selected in the above example.
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
console.log($scope.user.unit);
});
This console.logs me the following Object { id=1, val="px", name="px"}
I may be wrong but the <select> box is binded to $scope.opts
How would I be able to link the retrieved data from $scope.user.unit to $scope.opts.unit so that when the data is retrieved it will then mark the correct option as :selected?
I'm not 100% sure but you can try this (or create JSFiddle):
JS:
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
$scope.selected = {};
angular.forEach($scope.opts.unit, function (value)
{
if (value.val == $scope.user.unit.val) {
$scope.selected = value
}
});
console.log($scope.user.unit);
});
and in View:
<select class="unit ng-pristine ng-valid" data-ng-options="a.name for a in opts.unit" data-ng-model="user.unit">
<option value="{{selected.val}}">{{selected.name}}</option>
</select>
Your ng-model for the select element is an object, and not a primitive type, which is fine, but then you reassign $scope.user to a brand new object (returned from $http.get), so user.unit is a new object too, so it's not identical to any of your ng-options. I can think of two ways which should fix the problem:
bind the select to the 'id' property of the unit object:
<select ng-options="a.id as a.name for a in opts.unit" ng-model="user.unit.id">
or leave the select bound to user.unit, but use the track by feature of ng-options:
<select ng-options="a.name for a in opts.unit track by a.id" ng-model="user.unit">
One of the things in Angular is that you rarely need to do is explicitly create <option> elements manually as the framework will generate this for you. Therefore, the following will work: (Working jsfiddle at http://jsfiddle.net/LMHLq/12/)
HTML:
<select data-ng-model='user.unit' data-ng-options="o.id as o.name for o in opts.unit"/>
JavaScript:
$scope.opts ={
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"},
{ id: 3, val: "pt", name: "pt"}
]
}
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
console.log($scope.user.unit);
});
$scope.opts ={
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"},
{ id: 3, val: "pt", name: "pt"}
]
}
I noticed that the $scope.opts builds my select element and populates it but when the data is retrieved via db it needs to go into $scope.user.unit but this is binded to $scope.opts so what I have done is sought out the ID for the item that was retrieved and then added -1 to it so it will select from the array of $scope.opts.unit
var testUnit = $scope.user.unit.id-1; //gets the ID of the unit thats been retrieved
$scope.user.unit = $scope.opts.unit[testUnit]; //sets the selected option in the dom

Resources