KendoGrid/Angular: cannot create grid columns/data dynamically - angularjs

In this plunk I have an empty grid (without columns). When I click on "Build Grid" I need to add columns (taken from an array) and also add a row to the table.
The problem is that the columns are not added to the grid, any ideas? If I try to refresh the grid, I get an undefined error.
HTML:
<button ng-click="buildGrid()">Build Grid</button>
<div kendo-grid="grid" k-options="gridOptions" k-data-source="ds"></div>
Javascript:
var app = angular.module("app", [ "kendo.directives" ]);
function MyCtrl($scope) {
$scope.ds = []
$scope.colsList = [{ name: "col1" },
{ name: "col2" },
{ name: "col3" },
{ name: "col4" }];
var gridCols = [];
$scope.gridOptions = {
columns: gridCols
};
$scope.buildGrid = function() {
$scope.data = {};
for (var x=0;x<$scope.colsList.length;x++) {
var col = {};
col.field = $scope.colsList[x].name;
col.title = $scope.colsList[x].name;
$scope.data[col.field] = "" + (1111 * (x+1));
gridCols.push(col);
}
// add one row to the table
$scope.ds.push($scope.data);
//$scope.grid.refresh();
};
}

You need to use k-rebind so that the grid reinitializes (you can't set the columns dynamically on an existing grid):
<div kendo-grid="grid"
k-options="gridOptions"
k-data-source="ds"
k-rebind="gridOptions"></div>
(demo)

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

can I assign multiple $watchCollection programmaticaly?

Imagine that I have an array called buckets. that array will contain objects called bucket where each bucket has a property called images which is an array. Example:
vm.buckets = [
{name: "bucket1", images: []},
{name: "bucket2", images: []},
{name: "bucket3", images: []}
]
I am dynamically adding new buckets to vm.buckets , but also dynamically adding new images to each bucket. Is there a way to create a new $watchCollection and assign it to every new bucket so that i can watch for new images being inserted into each bucket?
angular.module('app', []).controller('ctrl', function($scope) {
var vm = this;
vm.buckets = [
{ name: "bucket1", images: [] },
{ name: "bucket2", images: [] },
{ name: "bucket3", images: [] }
];
var counter = vm.buckets.length;
vm.Addbucket = function() {
var temp = {
name: 'bucket' + (++counter),
images: []
};
vm.buckets.push(temp);
AddWatch(temp);
}
vm.DeleteLastBucket = function(){
vm.buckets[vm.buckets.length - 1].unregisterWatch();
vm.buckets.splice(vm.buckets.length - 1, 1);
}
function AddWatch(x) {
var index = vm.buckets.indexOf(x);
var id = `buckets[${index}].images`;
x.unregisterWatch = $scope.$watchCollection(id, function() {
console.log(`Changes at ${x.name} images`);
});
}
$scope.buckets = vm.buckets;
vm.buckets.forEach(AddWatch);
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl as vm'>
<ul>
<li ng-repeat='item in vm.buckets'>
{{item.name}}: {{item.images.length}} images
<button ng-click='item.images.push("temp")'>Add image</button>
</li>
</ul>
<button ng-click='vm.Addbucket()'>Add bucket</button>
<button ng-click='vm.DeleteLastBucket()'>Delete last bucket</button>
</div>

Comparing objects from two scopes to provide a value

I'll try to simplify the problem as much as I can.
Let's say I have 2 scopes
$scope.section1 = [
{label: 'label1'},
{label: 'label2'}
];
$scope.section2 = [
{value: 'one'},
{value: 'two}
];
Those scopes are used to generate buttons with ng-repeat
<button ng-repeat="item in section1 type="button">{{item.label}}</button>
and
<button ng-repeat="item in section2 type="button">{{item.value}}</button>
Now what I would like to do it to create a third scope that would attach values to the combinations of objects from the two previous ones, say:
$scope.combo = [
{ section1.label:label1 + section2.value: one = 'result1' },
{ section1.label:label2 + section2.value: one = 'result2' },
{ section1.label:label1 + section2.value: two = 'result3' },
{ section1.label:label2 + section2.value: two = 'result4' }
];
Now here comes the tricky part. What I would need to do, is to add a function that would take the values of clicked ng-repeat buttons from each section and then display the results based on the third scope in an input field or something.
So, if you click the button with label:label1 and the one with value:two the input field would show result3.
I'm very green when it comes to Angular and I have no idea how to approach it, especially that all values are strings.
If I understand correctly you could setup your combo something like ...
$scope.combo = {
"label1": {
"one": "result1",
"two": "result2"
},
"label2": {
"one": "result3",
"two": "result4"
}
}
You can then reference the correct value as combo[valueFromButton1][valueFromButton2] where valueFromButton1 and valueFromButton2 point at a model that contains the result of the clicked buttons. Your controller function then just needs to tie everything together by updating the model when the buttons are clicked.
See this plunkr ... https://embed.plnkr.co/GgorcM/
Without changing much you can also try like below provided code snippet.Run it to check the demo.
var app = angular.module('app', []);
app.controller('Ctrl',['$scope' ,function($scope) {
var key1, key2;
$scope.click = function(type, item) {
if (type == 'label') {
key1 = item;
} else if (type == 'val') {
key2 = item;
}
$scope.key = key1 + '+' + key2;
angular.forEach($scope.combo, function(val, key) {
if(val[$scope.key]){
$scope.finalVal = val[$scope.key];
}
});
};
$scope.section1 = [{
label: 'label1'
}, {
label: 'label2'
}];
$scope.section2 = [{
value: 'one'
}, {
value: 'two'
}];
$scope.combo = [{
'label1+one': 'result1'
}, {
'label2+one': 'result2'
}, {
'label1+two': 'result3'
}, {
'label2+two': 'result4'
}];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='Ctrl'>
<button ng-repeat="item in section1" ng-click="click('label',item.label)" type="button">{{item.label}}</button>
<button ng-repeat="item in section2" ng-click="click('val',item.value)"type="button">{{item.value}}</button>
<input type="text" ng-model="finalVal"/>{{key}} {{finalVal}}
</div>

Ng-grid search bar in the grid

Right now I am using ng-grid and the grid option, showFilter: true
This places a triangle drop down in the top right corner of the grid, which when clicked pops up a search bar.
Is there a way with ng-grid to have the search bar directly visible in the grid without the need to click the triangle?
Sure, just make a input text wherever you want in your view with your ng-grid:
<input type="text" data-ng-model="outside_search_text" placeholder="Search"
class="form-control" data-ng-change="search()">
<div class="gridStyle" data-ng-grid="gridOptions">
</div>
and then in your controller:
$scope.sortOptions = {
fields : ['id'],
directions : ['DESC']
};
$scope.totalServerItems = 0;
$scope.gridOptions['sortInfo'] = $scope.sortOptions;
$scope.gridOptions['columnDefs'] = [
{field : 'name', displayName: 'Name'}
];
$scope.refresh = function() {
var p = {
searchText : {name: $scope.outside_search_text},
pageNumber : $scope.pagingOptions.currentPage,
pageSize : $scope.pagingOptions.pageSize,
sortField : $scope.sortOptions.fields,
sortDirection : $scope.sortOptions.directions
};
YourServices.getAll(p).then(function(response) {
var content = getResponseData(response);
$scope.totalServerItems = content.count;
$scope.myData = content.result_set;
if (content['result_set'].length > 0) {
//whatever
} else {
//whatever
}
});
};

Dojo ComboBox not getting populated with data

Not sure what wrong I am doing, but I have followed the apparoch explained. I think, I am also getting the objects added into store object. but the same is not getting reflected on UI. the drop down is still blank. can sombody please help.
My Select box code:
<div class="ecmSearchFormInputArea">
<table class="ecmSearchFormInputArea">
<tr>
<td class="propertyRowLabel">
<label><span class="mandatory_red"></span>Cost Center :</label>
<select data-dojo-attach-point="costCenter" id="costCenter"
data-dojo-type="dijit/form/FilteringSelect" style="width: 18em;"
data-dojo-attach-event="onChange:loadDocStacks">
</select>
</td>
</tr>
</table>
</div>
My JS
postCreate: function() {
this.logEntry("postCreate");
//Trying to populate Combobox
var storeData = {
identifier: 'costCenter',
items: []
}
var jsonObj = [{
costCenter: 'sc1'
},
{
costCenter: 'sc2'
}]
dojo.addOnLoad(function () {
var costCenterStore = new dojo.data.ItemFileWriteStore({ data: storeData });
for (var i = 0; i < jsonObj.length; i++) {
alert ("value of i = " + jsonObj[i].costCenter);
costCenterStore.newItem(jsonObj[i]);
}
var serviceFilterSelect = dijit.byId('costCenter');
alert ("serviceFilterSelect " + serviceFilterSelect);
alert ("costCenterStore " + costCenterStore);
serviceFilterSelect.attr('store', costCenterStore);
});
//Trying to populate Combobox
this.logExit("postCreate");
},
Alerts in the FOR loop shows correct data. Am I missing anything?
add label attribute along with the identifier. Because that is what will be used as label to populate.
var jsonObj = [{
costCenter: 'sc1', name: 'label1'
},
{
costCenter: 'sc2', name: 'label2'
}];
var storeData = {
identifier: 'costCenter', label: 'name',
items: []
};

Resources