Angular JS selectize error - javascript

This is driving me nuts, as I can find no real reason for it.
I have an Angular 1.3.2 project. On one view, with one controller, one factory, and 2 json files as sources. I added 2 selectize menus, both work perfectly.
I did a save as to create a new file, new controller, new factory, new json file.
The html I kept the same but adjusted the selectize to match the new data:
<input type="text" selectize="partnerSelectMenu.options" options="partnerList" ng-model="partner.selected" />
The factory is
app.factory('DatagroupsFactory', ['$http',
function($http) {
var datagroupsData;
return {
getDataGroups: function() {
if (!datagroupsData) {
datagroupsData = $http.get('scripts/data/datagroups.json').then(function(response) {
return response.data;
});
}
return datagroupsData;
}
};
}
]);
the controller is
app.controller('AddDatagroupCtrl', function($scope, $log, PartnerFactory) {
var partnerList = [];
PartnerFactory.getPartners().then(function(response) {
$scope.partnerList = response.partners;
});
$scope.partnerSelectMenu = {
options: {
valueField: 'name',
labelField: 'name',
searchField: ['name'],
plugins: ['remove_button']
}
};
});
And a fragment of the json:
{
"partners" : [
{
"value" : "CPPRT0002706",
"name" : "Axis Promotions"
},
{
"value" : "CPPRT0005006",
"name" : "Band of Outsiders"
}
]
}
Here's the kicker: the menu works fine, as expected, and displays my data. The problem is I also get this error:
I have tried commenting out every part of this path of files. Nothing. Log debug statements show the data is correctly being passed; heck, the menu even works.
The other pages on my site with selectize menus -- with the same options -- do not throw this error. I am at a loss on how to track down.

I think you need to change:
var partnerList = [];
to
$scope.partnerList = [];
in your controller.
app.controller('AddDatagroupCtrl', function($scope, $log, PartnerFactory) {
$scope.partnerList = [];
PartnerFactory.getPartners().then(function(response) {
$scope.partnerList = response.partners;
});
$scope.partnerSelectMenu = {
options: {
valueField: 'name',
labelField: 'name',
searchField: ['name'],
plugins: ['remove_button']
}
};
});
I think before your promise returns, partnerList is undefined and your selectize is trying to get a name property from that.

Related

Extjs 4.2 autosync dynamic grid store

I have a grid with dynamic columns:
MODEL
Ext.define('App.mdlCriteriosConcurso', {
extend: 'Ext.data.Model',
fields: [
]
});
STORE
Ext.define('App.strCriteriosConcurso', {
extend: 'Ext.data.Store',
model: 'App.mdlCriteriosConcurso',
autoLoad: false,
proxy: {
type: 'ajax',
api: {
read: 'some url',
update: 'some url',
},
reader: {
type: 'json',
root: 'data',
totalProperty: 'total'
},
writer: {
root: 'records',
encode: true,
writeAllFields: true
}
}
});
GRID
var almacenCriteriosConcurso = Ext.create('App.strCriteriosConcurso');
//Some code ...
{
xtype:'grid',
itemId:'gridCriteriosConcursoV4',
store:almacenCriteriosConcurso,
plugins: [Ext.create('Ext.grid.plugin.CellEditing', {clicksToEdit: 2})],
columns:[]
}
//Some code...
CONTROLLER
Here in the controller I have the next piece of code:
Ext.ComponentQuery.query('viewFichaDetalle #tabpanelsecundario4_1 #gridCriteriosConcursoV4')[0].getStore().addListener('metachange',function(store,meta){
var columnas=0;
var renderer1 = function(v,params,data){
if(v==''){
return '<div style="background-color:#F5FAC3;color:blue;">'+Ext.util.Format.number(0,'0.000,00/i')+'</div>';
}
else{
return '<div style="background-color:#F5FAC3;color:blue;">'+Ext.util.Format.number(v,'0.000,00/i')+'</div>';
}
};
var renderer2 = function(v,params,data){
if(v=='' || v==0){
return '<div style="background-color:#F5FAC3;color:green;">'+Ext.util.Format.number(0,'0.000,00/i')+'</div>';
//return '';
}
else{
return '<div style="background-color:#F5FAC3;color:green;">'+Ext.util.Format.number(v,'0.000,00/i')+'</div>';
}
};
Ext.each(meta.columns,function(col){
if(columnas==2){
meta.columns[columnas].renderer = renderer1;
}
if(columnas>=3){
meta.columns[columnas].renderer = renderer2;
}
columnas++;
},this);
Ext.suspendLayouts();
Ext.ComponentQuery.query('viewFichaDetalle #tabpanelsecundario4_1 #gridCriteriosConcursoV4')[0].reconfigure(store, meta.columns);
Ext.ComponentQuery.query('viewFichaDetalle #tabpanelsecundario4_1 #gridCriteriosConcursoV4')[0].setTitle("<span style='color:red;font-weight:bold;font-size: 12pt'>Criterios del Concurso con ID:</span> "+"<span style='color:black;font-weight:bold;font-size: 12pt'>"+this.IdConcurso+"</span>");
Ext.resumeLayouts(true);
},this);
I create the columns in the php, using the metadata.
With this code I add some renderers to the grid columns. And I see all the data perfect, and can edit the data.
In the php y generate the column and the field like this:
$array_metadata['columns'][]=array("header"=>"<span style='color:blue;'>Resultado</span>","dataIndex"=>"puntos_resultado","width"=>82,"align"=>"right","editor"=>"textfield");
$array_metadata['fields'][]=array("name"=>"puntos_resultado","type"=>"float");
And then pass $array_metadata to 'metaData' response.
But when I try to sync or autosync the store I get this error:
Uncaught TypeError: Cannot read property 'name' of undefined
at constructor.getRecordData (ext-all-dev.js:62247)
at constructor.write (ext-all-dev.js:62192)
at constructor.doRequest (ext-all-dev.js:102306)
at constructor.update (ext-all-dev.js:101753)
at constructor.runOperation (ext-all-dev.js:106842)
at constructor.start (ext-all-dev.js:106769)
at constructor.batch (ext-all-dev.js:62869)
at constructor.sync (ext-all-dev.js:64066)
at constructor.afterEdit (ext-all-dev.js:64162)
at constructor.callStore (ext-all-dev.js:101428)
UPDATE 1
I have fount this thread in Sencha Forums link , and I have tried all posibles solutions and Im getting allways the same error.
The error tells us that you don't fill the model's fields array properly, because that is where name is a required config. In ExtJS 4, you have to add all fields to the model for the sync to work properly.
To be exact, the Model prototype has to be filled with the correct fields before the instances are created.
This means that you will have to override the reader's getResponseData method, because between Ext.decode and readRecords you will have to prepare the model prototype by setting the fields as returned from the server; something like this:
App.mdlCriteriosConcurso.prototype.fields = data.fields;

Bind knockoutjs to javascript object property

I'm new to Knockoutjs, so please bear with me.
I want to knocoutjs bind a DxForm (DevExpress) to an javascript object property, but I get an error ... "Cannot read property 'items' of undefined".
I am uncertain if this is a knockout problem, DevExpress problem or just incufficient coding skills from my part.
Here's my code...
HTML:
<div data-bind="dxForm: frm.options"></div>
Javascript:
var viewModel = function() {
var that = this;
// -----------------------------------------------------------------
// Faste...
// -----------------------------------------------------------------
that.frm = {
items: ko.observable(undefined),
data: ko.observable(undefined),
instance: ko.observable({}),
options: {
items: that.frm.items,
formData: that.frm.data,
onInitialized: function(e) {
that.frm.instance(e.component);
},
},
};
return {
frm: that.frm,
};
};
var vm = new viewModel();
ko.applyBindings(vm);
var items = [{
"dataField": "test",
"editorOptions": {
"type": "date",
"pickerType": "calendar",
},
"editorType": "dxDateBox",
"name": "Test",
"visible": true
}];
var data = {
test: 10,
};
vm.frm.data(data);
vm.frm.items(items);
JSFiddle: https://jsfiddle.net/MojoDK/ke395v2c/3/
I want to bind to objects since I'm going to use several DxForm objects and I like to keep the code to each DxForm in an object (easier to read).
Any idea why it fails?
Thanks.
You just have a problem with closure in your frm.
The that property in frm object do not exist you should use this...
But in your onInitialized function, this and that will not target your viewModel object...
So this way, the easiest is to declare options object later :
that.frm = {
items: ko.observable(undefined),
data: ko.observable(undefined),
instance: ko.observable({})
};
that.frm.options = {
items: that.frm.items,
formData: that.frm.data,
onInitialized: function(e) {
that.frm.instance(e.component);
},
};
Updated jsfiddle

Kendo UI: One data source, two widgets

UPDATE: Here is a link to reproduce the problem
RELATED: This is another question of mine where similar problems are happening with Kendo UI Map, maybe it could help someone figure this one out! It has one failing and one working version.
I use Kendo UI's DataSource, DropDownList and Map in an Angular single-page application.
I want to use the same DataSource object for both the DropDownList and the Map. However, the Map behaves in a very unpredictable manner.
When I put the DropDownList before the Map in the template, only the DropDownList gets populated. Inspecting the network traffic reveals that indeed only one request is being made. When I put the Map first, both of them get populated and two requests are made.
When I don't use any promises in transport.read, but just call options.success immediately with a static value, everything works as expected. Two calls are being made.
I've been pulling my hair over this the entire work day, so any help is highly appreciated.
The data source service:
m.factory('ourDataSource', function(foo, bar, baz) {
return new kendo.data.DataSource({
transport: {
read: function(options) {
foo().then(function (result) {
return bar(result);
}).then(function (result) {
return baz(result);
}).then(function (result) {
options.success(result);
}).catch(function (err) {
options.error(err);
});
}
}
});
});
The controller:
m.controller('ourController', ['ourDataSource', function(ourDataSource) {
// set the data source of the dropdownlist
this.ourDataSource = ourDataSource;
// set up the map layers
this.mapLayers = [{
type: 'tile',
urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/#= zoom #/#= y #/#= x #',
}, {
type: 'marker',
dataSource: ourDataSource, // the same data source as before
locationField: 'Position',
titleField: 'Title'
}];
}]);
The view:
<div ng-controller="ourController as ctrl">
<select kendo-drop-down-list
k-data-text-field="'Title'"
k-data-value-field="'Title'"
k-data-source="ctrl.ourDataSource"></select>
<div kendo-map
k-zoom="2"
k-center="[1, 1]"
k-layers="ctrl.mapLayers">
</div>
</div>
What am I missing here?
I believe that this might be a bug in the Kendo UI Map widget, since the behavior occurring here isn't at all what one would expect. However, I do have a workaround solution. Rather than return the data source as a singleton object, return it as a function. This is probably not ideal, but it works.
angular.module('ourModule', ['kendo.directives'])
.factory('getDataSource', function ($q) {
return function() { // return a function that creates a new data source
return new kendo.data.DataSource({
transport: {
read: function (options) {
$q.when([
{Position: [1, 1], Title: 'First place'},
{Position: [10, 10], Title: 'Second place'}
]).then(function (result) {
options.success(result);
});
}
}
});
};
})
.controller('ourController', function (getDataSource) {
this.ourDataSource = getDataSource();
this.mapLayers = [{
type: 'tile',
urlTemplate: '...removed for brevity...'
}, {
type: 'marker',
dataSource: getDataSource(),
locationField: 'Position',
titleField: 'Title'
}];
});
Factory mostly used to create instances on demand. See this example
var app = angular.module('ourModule', ['kendo.directives']);
app.factory('dataSourceFactory', function($q) {
function dataSourceFactory() {}
dataSourceFactory.prototype = {
contentTypes: function() {
return new kendo.data.DataSource({
transport: {
read: function(options) {
$q.when(
[{
Position: [1, 1],
Title: 'First place'
}, {
Position: [10, 10],
Title: 'Second place'
}])
.then(function(result) {
options.success(result);
});
}
}
})
}
};
return dataSourceFactory;
});
app.controller('ourController', ['$scope', 'dataSourceFactory',
function($scope, dataSourceFactory) {
var dataSourceFactory = new dataSourceFactory();
$scope.mapLayers = [{
type: 'tile',
urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/#= zoom #/#= y #/#= x #',
}, {
type: 'marker',
dataSource: dataSourceFactory.contentTypes(), // the same data source as before
locationField: 'Position',
titleField: 'Title'
}];
$scope.ourDataSource = dataSourceFactory.contentTypes();
}
]);
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.rtl.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.default.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.mobile.all.min.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/angular.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/jszip.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2015.3.930/js/kendo.all.min.js"></script>
<div ng-app="ourModule">
<div ng-controller="ourController">
<kendo-drop-down-list k-data-source="ourDataSource"
k-data-text-field="'Title'"
k-data-value-field="'Title'">
</kendo-drop-down-list>
<kendo-map k-zoom="2"
k-layers="mapLayers">
</kendo-map>
</div>
</div>
See this JSFiddle demo

Returning data from service dependancy using promises causes $digest iteration error

I have two services where one needs to query the other by an id for example.
This is what I currently have but I'm getting $digest iteration limits.
"Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
Controller
angular.module('exampleApp')
.controller('ExampleCtrl', function ($scope, PeopleService, PlanService) {
$scope.plan = new Plan();
// or
$scope.plan = Plan.get({ id: 1 });
// Used to populate a <select> menu in the view
PeopleService.all().then(function(data) {
$scope.people = data;
});
});
PlanService
angular.module('exampleApp')
.factory('PlanService', function($resource, PeopleService){
// Resource example
// { id: 1, title: 'Example', assignee_id: 1 }
var Plan = $resource('/plans/:id', { id: '#id' }, {
'query': { method:'GET', isArray: true },
'get': { method:'GET', isArray: false },
'update': { method:'PUT' }
});
// Return the matching person from PeopleService
Plan.prototype.assignee = function() {
if(this.assignee_id) {
return PeopleService.find_by_id(this.assignee_id)
} else {
return null;
}
}
return Plan;
});
PeopleService
angular.module('exampleApp')
.service('PeopleService', function($http, $q){
var people_array = $q.defer();
// This list is reasonably static hence why I feel no need to make it it's own resource
// Contains an array of people objects for example
// { id: 1, name: 'Paul Smith', hourly_rate: 5.0, max_hours: 8 }
$http.get('/people/json').then(function(result) {
people_array.resolve(result.data);
});
this.all = function() {
return people_array.promise;
};
this.find_by_id = function(id) {
var def = $q.defer();
people_array.promise.then(function(data) {
// underscore.js find
var person = _.find(data, function(person) {
return parseInt(person.id) === parseInt(id);
})
def.resolve(person);
});
return def.promise;
};
});
View
<select name="person_id" class="form-control" ng-model="plan.assignee_id" ng-options="assignee.id as assignee.name for person in people" required>
</select>
{{ plan.assignee().name }}
Now the select populates no problem, however the plan.assignee.name causes the digest errors when using the select menu to select an option. I'm sure this is a misunderstanding I have with how promises work so I'd appreciate a nudge in the right direction.

Controls in Angular break when configuration options come from service

I have a service that will return my some config options for an ng-grid. The getGridOptions function takes the name of the controller it's used on and returns the correct set of options (only one shown here for brevity).
service for ng-grid options:
angular.module('services').service('GridOptionsService',function(){
var documents = {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: {
pageSizes: [50,100,200],
pageSize: 50,
currentPage: 1
},
filterOptions: {
filterText: '',
useExternalFilter: false
},
enableCellEdit: false,
enableColumnReordering: true,
enablePinning: false,
showGroupPanel: false,
groupsCollapsedByDefault: true,
enableColumnResize: true,
showSelectionCheckbox: true,
selectWithCheckboxOnly: true,
columnDefs: [
{field:'docId', displayName:'Document ID', cellTemplate: NgGridDomUtil.toLink('#/documents/{{row.getProperty(col.field)}}')},
{field:'docTags', displayName:'Tags'},
{field:'lastSaveDate', displayName:'Last saved'},
{field:'documentVersion', displayName:'Version', width: 120},
{field:'busDocId', displayName:'Customer Doc ID'},
{field:'markedForDelete', displayName:'Deleted', width: 120, cellTemplate: NgGridDomUtil.toCheckbox('{{row.getProperty(col.field)}}')}]
};
var gridOptionManager = {
documents: documents
}
return {
getGridOptions: function(controllerName){
return gridOptionManager[controllerName];
}
}
})
The NgGridDomUtil class just makes it easier to style things on the grid:
var NgGridDomUtil = (function(){
var toLink = function(href){
var html = '<div class="ngCellText" ng-class="col.colIndex()"><a ng-href= "'+href+'" class="ngCellLink"><span ng-cell-text>{{row.getProperty(col.field)}}</span></a></div>'
return html;
}
var toCheckbox = function(_selected){
var html = '<div class="ngCellText" ng-class="col.colIndex()"><input type="checkbox" ng-change="console.log('+"TEST"+')" ng-model="COL_FIELD" ng-input="COL_FIELD"' + (_selected ? 'selected' : '') + ' /></div>'
return html
}
return {
toLink: toLink,
toCheckbox: toCheckbox
}
})();
My problem is what when I use the GridOptionsService to retrieve the data, the data is still presented to the grid correctly, but the text filtering no longer works and the paging is broken. However, the selectedFilterOption still works.
controller:
angular.module('controllers').controller('Repository', ['$scope', 'DataContext','GridOptionsService','$http', function($scope, DataContext,GridOptionsService,$http) {
$scope.filterOptions = {
filterText: '',
useExternalFilter: false
};
$scope.totalServerItems =0;
$scope.pagingOptions ={
pageSizes: [5,10,100],
pageSize: 5,
currentPage: 1
}
//filter!
$scope.dropdownOptions = [{
name: 'Show all'
},{
name: 'Show active'
},{
name: 'Show trash'
}];
//default choice for filtering is 'show active'
$scope.selectedFilterOption = $scope.dropdownOptions[1];
//three stage bool filter
$scope.customFilter = function(data){
var tempData = [];
angular.forEach(data,function(item){
if($scope.selectedFilterOption.name === 'Show all'){
tempData.push(item);
}
else if($scope.selectedFilterOption.name ==='Show active' && !item.markedForDelete){
tempData.push(item);
}
else if($scope.selectedFilterOption.name ==='Show trash' && item.markedForDelete){
tempData.push(item);
}
});
return tempData;
}
//grabbing data
$scope.getPagedDataAsync = function(pageSize, page, searchText){
var data;
if(searchText){
var ft = searchText.toLowerCase();
DataContext.getDocuments().success(function(largeLoad){
//filter the data when searching
data = $scope.customFilter(largeLoad).filter(function(item){
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
})
$scope.setPagingData($scope.customFilter(data),page,pageSize);
})
}
else{
DataContext.getDocuments().success(function(largeLoad){
var testLargeLoad = $scope.customFilter(largeLoad);
//filter the data on initial page load when no search text has been entered
$scope.setPagingData(testLargeLoad,page,pageSize);
})
}
};
//paging
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page -1) * pageSize, page * pageSize);
//filter the data for paging
$scope.myData = $scope.customFilter(pagedData);
$scope.myData = pagedData;
$scope.totalServerItems = data.length;
// if(!$scope.$$phase){
// $scope.$apply();
// }
}
//watch for filter option change, set the data property of gridOptions to the newly filtered data
$scope.$watch('selectedFilterOption',function(){
var data = $scope.customFilter($scope.myData);
$scope.myData = data;
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
})
$scope.$watch('pagingOptions',function(newVal, oldVal){
$scope.getPagedDataAsync($scope.pagingOptions.pageSize,$scope.pagingOptions.currentPage,$scope.filterOptions.filterText);
$scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
},true)
$scope.message ="This is a message";
$scope.gridOptions = {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enableCellEdit: true,
enableColumnReordering: true,
enablePinning: true,
showGroupPanel: true,
groupsCollapsedByDefault: true,
enableColumnResize: true
}
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
//get the data on page load
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
}]);
The grid options that I have hard coded into the controller are the same as the ones returned from the service. What I don't understand is why the grid renders, the dropdown filter works, but the paging is broken, only when the options for the grid come from a service? But it works as expected if it's hard coded into the controller.
EDIT: If someone can more eloquently state my problem, feel free to edit the title.
I don't really know how the ngGrid is implemented, but a common pitfall I do know that exist in many directives, is they expect their configurations to be ready as soon as they're initialized. Meaning that instead of watching the configuration object, they assume it exists and use it directly in the link\controller functions which runs as soon as they're created.
If this is indeed the case, a quick workaround to the problem is initializing the directive only when you have the configuration object. Let's say you pass on the configuration object through the variable 'options' on your scope, you'll then write something like:
<!-- If options exists on your scope, it means you fetched it from the server -->
<div ng-if="options">
<div ng-grid ng-grid-options="options"></div>
</div>
Again, I'm not familiar with ngGrid or its usage, this is just an educated guess, take the conclusions and apply them on the correct API.
I haven't tested this, but one possible issue is that you are overwriting an object that is on the $scope. This can break the two way binding. For a quick test try
$scope.grid = {
Options: {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enableCellEdit: true,
enableColumnReordering: true,
enablePinning: true,
showGroupPanel: true,
groupsCollapsedByDefault: true,
enableColumnResize: true
}
}
$scope.grid.Options = GridOptionsService.getGridOptions('documents');
You would need to update the grid options in the directives attribute as well of course.
The problem is that the controller functions for filtering and paging use the options defined on the controller $scope, but the ng-grid UI is not bound to those objects.
The controller methods for filtering and paging use $scope.pagingOptions as the data source. However, the ng-grid UI is bound to $scope.gridOptions.pagingOptions. When you create the $scope.gridOptions explicitly in the controller, $scope.gridOptions.pagingOptions refers to the same object as $scope.gridOptions, so making changes in the UI will change the value used in the controller functions.
However, when $scope.gridOptions is assigned using the service, the service is creating new pagingOptions, so there is no connection between $scope.gridOptions.pagingOptions and $scope.pagingOptions. Making changes in the UI will not change the values used in the controller functions.
The same is true for filterOptions.
One way to resolve the issue is
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
$scope.pagingOptions = $scope.gridOptions.pagingOptions
$scope.filterOptions = $scope.gridOptions.filterOptions

Categories

Resources