This is what I have:
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
$scope.safeApply(function() {
$scope.settings.columns[3] = $scope.dataSource;
});
});
$scope.settings = {
colHeaders: ["Code", "Comments"],
contextMenu : ["row_above","row_below","remove_row"],
colWidths: [100, 100],
columns : [
{ type: 'dropdown',
source: ['Not Started', 'In Progress', 'Completed']
},
{},
{},
{ type: 'dropdown',
source: $scope.dataSource,
}
]
};
Problem is $scope.dataSource is undefined, it's not displaying the data. What should be the solution to this?
UPDATE:
This displays the data in my $http call. But in the settings source when I call source: $scope.dataSource is undefined
When you are making a request to the server, the rest of the controller will be compiled then you will enter the success function
you are declaring your array inside the success of your request, it's better be declared outside like this
$scope.dataSource = []
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
$scope.safeApply(function() {
$scope.settings.columns[3] = $scope.dataSource;
});
});
UPDATE
try this
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
getSetting();
$scope.safeApply(function() {
$scope.settings.columns[3] = $scope.dataSource;
});
});
var getSetting = function() {
$scope.settings = {
colHeaders: ["Code", "Comments"],
contextMenu: ["row_above", "row_below", "remove_row"],
colWidths: [100, 100],
columns: [{
type: 'dropdown',
source: ['Not Started', 'In Progress', 'Completed']
}, {}, {}, {
type: 'dropdown',
source: $scope.dataSource,
}]
};
}
So first you need to be aware that when you declare $scope.settings = {...} the $http call will not be over yet, so instead of writting
{
type: 'dropdown',
source: $scope.dataSource,
}
You may as well simply write
{
type: 'dropdown',
source: null,
}
Then when the $http call finishes, I assume you want to set this source property. However in your code you are overriding the whole $scope.settings.columns[3] instead of just its source property.
Try this instead:
$http.get("http://localhost/app/api/Suppliers").success(function(response) {
$scope.dataSource = response;
console.log($scope.dataSource);
$scope.settings.columns[3].source = $scope.dataSource;
});
Note that I have removed the safeApply which is an anti-pattern. Here the $http call will take care of the digest cycle.
Found the answer!
$scope.suppliers = [];
$http.get("http://localhost/i95/api/Suppliers").success(function(data, status, headers, config) {
for(var i = 0; i < data.length; i++) {
$scope.suppliers.push(data[i].company);
}
console.log($scope.suppliers);
});
So creating a global variable and adding push method fixed the issue.
Related
I am using angular-datatable ( http://l-lin.github.io/angular-datatables/#/basic/server-side-angular-way) .
Instead on assigning to this.dtOptions in ngOnInit, I want to do it in the response of another api, but the ajax call is not going through.
My code :-
ngOnInit(){
this.firstCall();
}
firstCall(){
this.api.serviceMethod().subscribe((data : model1) => {
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: data.pageLength,
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
that.http
.post<DataTablesResponse>(
'api.com/api',
dataTablesParameters, data.req_body, {}
).subscribe(resp => {
that.persons = resp.data;
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: []
});
});
},
columns: [{ data: 'id' }, { data: 'firstName' }, { data: 'lastName' }]
};
})
}
When I am putting the dtOptions assignment in ngOnInit, it is getting called, what am I missing here.
Thank you in advance.
You must set a check in super tag (like : tbody or table when you loop in tr tag).
component.html
<table *ngIf="!loading" ...>
...
...
component.ts
loading = true;
... .subscribe(resp => {
that.persons = resp.data;
loading = false;
I have a web application, started by a previous company, written in Angular.JS.
The application exposes a request towards the back-end (written in Node.JS+Express) to gather some data required to fill a table.
Specifically, this is the request that the application sends everytime the user enters in the page that holds the table (The config variable holds the access token).
define(['app'], function (app) {
app.factory('AdvService', AdvService);
AdvService.$inject = ['BasicService'];
function AdvService(BasicService) {
var service = angular.extend(BasicService, {});
var $http = service.$http;
var API = service.API
var handleSuccess = service.handleSuccess;
var handleError = service.handleError;
var config = {
params: {
'token': JSON.parse(window.sessionStorage.getItem('session')).token
}
};
service.searchByCriteria = function (criteria) {
debugger;
config.params.pageNumber = criteria.pageNumber;
config.params.pageSize = criteria.pageSize;
return $http.get(API + '/api/v2/admin/ads/getAdsByCriteria', config).then(handleSuccess, handleError);
};
service.createAd = function (ad) {
debugger;
return $http.post(API + '/api/v2/admin/ads/insertNewAd', ad, config).then(handleSuccess, handleError);
};
return service;
}
});
handleSuccess and handleError are so defined
define(['app'], function (app) {
app.factory('BasicService', BasicService);
BasicService.$inject = ['CONF', '$http', '$window', '$q'];
function BasicService(CONF, $http, $window, $q) {
$http.defaults.headers.common['x-access-token'] = JSON.parse($window.sessionStorage.getItem('session')).token;
return {
$http: $http,
API: CONF.API_URL,
handleSuccess: function (res) {
debugger;
var deferred = $q.defer();
res.data.success ? deferred.resolve(res.data) : deferred.reject(res.data.message);
return res.data;
},
handleError: function (error) {
debugger;
return {
success: false,
message: error
};
}
}
}
})
and this is the only point of the application that calls that service
($scope.search = function () {
debugger;
AdvService.searchByCriteria($scope.searchCriteria).then(function (res) {
debugger;
$scope.searchRes = res.data.docs;
//$scope.gridOptions.totalItems = res.data.total;
}, function (err) {
console.log(err);
});
})();
Being a CORS request (the front-end is at port 8010 and the back-end in another one), I see from Chrome's Network Monitoring System that the $http.get part gets executed twice, but here's my problem: even before starting handling on the back-end the first call, the front-end generates the error
angular.js:14961 TypeError: Cannot read property 'data' of undefined
at Object.<anonymous> (ui-grid.js:3291)
at Object.invoke (angular.js:5117)
at $controllerInit (angular.js:11139)
at nodeLinkFn (angular.js:10002)
at angular.js:10410
at processQueue (angular.js:17330)
at angular.js:17378
at Scope.$digest (angular.js:18515)
at Scope.scopePrototype.$digest (hint.js:1495)
at Scope.$apply (angular.js:18903)
and even tough the request does return data, having crashed, Angular cannot correctly render everything.
The only thing I tried was to use the Await/Async mechanism to try to wait and see what could have happened but this always resolves in the above error.
Does anybody have any clue of what's going on? I'm 100% positive that the code that I've posted here is the only one that gets called during this process and I can't honestly understand why should the process fail if both requests return data
So, as correctly pointed out by Alon Eitan in the comments, the error was somehow related with ui-grid and that, probably, it might have been that the gridOptions were not correctly called like the HTML was excepting.
IN FACT ...
HTML
<div class="alerts container-fluid" ng-controller="AdsController">
<div class="row">
WORK IN PROGRESS!
<!-- <span>Pubblicità attive: <b>{{activeAdsCounter}}</b> - </span>-->
<!-- <span>Pubblicità totali: <b>{{totalAdsCounter}}</b> - </span>-->
</div>
<button ng-click="openCreateNewAdModal()"><i class="material-icons">library_add</i></button>
<div class="row">
<div class="col-md-12">
<h3>Pubblicità Nel Sistema</h3>
<div class="grid" ui-grid="adsGridOptions" ui-grid-pagination
ui-grid-auto-resize></div>
</div>
</div>
</div>
Controller
define(['app', 'moment'], function (app, moment) {
app.controller('AdsController', AdsController);
AdsController.$inject = ['$scope', '$mdDialog', 'AdvService'];
function AdsController($scope, $mdDialog, AdvService) {
debugger;
$scope.rowHeight = 30;
$scope.gridOptions = {
data: 'searchRes',
paginationPageSizes: [25, 50, 75],
paginationPageSize: 25,
enableSorting: true,
enablePaginationControls: true,
enableColumnMenus: false,
useExternalPagination: true,
rowHeight: $scope.rowHeight,
columnDefs: [
{
name: 'Cliente',
field: 'customerName',
}, {
name: 'Durata',
field: 'duration',
}, {
name: 'Euro Spesi',
field: 'spentEuros',
}, {
name: 'Data inserimento',
field: 'createdAt',
type: 'date',
width: '130',
cellFilter: "date:'dd-MM-yyyy'",
}, {
name: 'Email Referente',
field: 'referralEmail',
}, {
name: 'Nome Referente',
field: 'referralPerson',
}, {
name: 'Numero Referente',
field: 'referralNumber',
},
],
onRegisterApi: function (gridApi) {
$scope.gridApi = gridApi;
$scope.gridApi.core.on.sortChanged($scope, function (grid, sortColumns) {
if (sortColumns[0]) {
console.log(sortColumns[0].sort);
$scope.searchCriteria.sort = {};
$scope.searchCriteria.sort[sortColumns[0].field] = sortColumns[0].sort.direction;
}
});
$scope.gridApi.pagination.on.paginationChanged($scope, function (pageNum, pageSize) {
$scope.searchCriteria.pageNumber = pageNum;
$scope.searchCriteria.pageSize = pageSize;
$scope.search();
});
}
};
The controller had the options called as "gridOptions", while the HTML had them called "adsGridOptions". Changing the name to "gridOptions" to both sides solved the issue.
Thank you, Alon Eitan!
I have a web application with multiple Selectize objects initialized on the page. I'm trying to have each instance load a default value based on the query string when the page loads, where ?<obj.name>=<KeywordID>. All URL parameters have already been serialized are are a dictionary call that.urlParams.
I know there are other ways to initializing Selectize with a default value I could try; but, I'm curious why calling setValue inside onInitialize isn't working for me because I'm getting any error messages when I run this code.
I'm bundling all this JavaScript with Browserify, but I don't think that's contributing to this problem.
In terms of debugging, I've tried logging this to the console inside onInititalize and found that setValue is up one level in the Function.prototype property, the options property is full of data from load, the key for those objects inside options corresponds to the KeywordID. But when I log getValue(val) to the console, I get an empty string. Is there a way to make this work or am I ignoring something about Selectize or JavaScript?
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
onInitialize: function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
this.setValue(val);
},
load: function(query, callback) {
$.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
})
}
});
};
...
After sprinkling in some console.logs into Selectize.js, I found that the ajax data hadn't been imported, when the initialize event was triggered. I ended up finding a solution using jQuery.when() to make setValue fire after the data had been loaded, but I still wish I could find a one-function-does-one-thing solution.
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
load: function(query, callback) {
var self = this;
$.when( $.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
}) ).then(function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
self.setValue(val);
});
}
});
};
...
You just need to add the option before setting it as the value, as this line in addItem will be checking for it:
if (!self.options.hasOwnProperty(value)) return;
inside onInitialize you would do:
var val = that.urlParams[that.name]; //It might work with parseInt, I haven't used integers in selectize options though, only strings.
var opt = {id:val, text:val};
self.addOption(opt);
self.setValue(opt.id);
Instead of using onInitialize you could add a load trigger to the selectize. This will fire after the load has finished and will execute setValue() as expected.
var $select = $(this).container.selectize({
// ...
load: function(query, callback) {
// ...
}
});
var selectize = $select[0].selectize;
selectize.on('load', function(options) {
// ...
selectize.setValue(val);
});
Note that for this you first have to get the selectize instanze ($select[0].selectize).
in my case it need refresh i just added another command beside it
$select[0].selectize.setValue(opt);
i added this
$select[0].selectize.options[opt].selected = true;
and changes applied
but i dont know why?
You can initialize each selectize' selected value by setting the items property. Fetch the value from your querystring then add it as an item of the items property value:
const selectedValue = getQueryStringValue('name') //set your query string value here
$('#sel').selectize({
valueField: 'id',
labelField: 'title',
preload: true,
options: [
{ id: 0, title: 'Item 1' },
{ id: 1, title: 'Item 2' },
],
items: [ selectedValue ],
});
Since it accepts array, you can set multiple selected items
I am using dgrid and i am attempting to set the dataStore externally. When the page loads i call aliasTicket.load() to create the grid. At the time the grid is loading the datasource is null. When a query is executed the setAliasSource(aliasData); is set.
There are no errors however the grid is still empty. The aliasStore is being updated with data however it isn't being reflected on the grid even after the grid is refreshed. How can i get the data reflected in the grid after the query?
Javascript Object
var aliasTicket = (function (){
var aliasData = [];
require([ "dojo/store/Observable", "dojo/store/Memory"]);
var aliasStore = new dojo.store.Observable(new dojo.store.Memory({
data: aliasData,
idProperty: "id"
}));
return{
load:function(){
require([
........
], function(declare, Memory, OnDemandGrid, ColumnSet, Selection,
selector, Keyboard, DijitRegistry, editor, ColumnHider,
registry, Observable,lang) {
aliasData = this.aliasData;
var Store = this.aliasStore = new dojo.store.Observable(new dojo.store.Memory({
data: aliasData,
idProperty: "id"
}));
console.log(Store);
var CustomAliasNameGrid = declare([OnDemandGrid, selector, Selection, Keyboard, editor, DijitRegistry, ColumnHider]);
var aliasNameGrid = new CustomAliasNameGrid({
store: Store,
columns: {
id: {
label: "Id",
field: "id",
hidden: true,
autoSizeColumn: true
},
employeeTicketId: {
label: "Employee Ticket Id",
field: "employeeTicketId",
hidden: true,
autoSizeColumn: true
},
chkBox: selector({}),
aliasName: {
label: "Alias Names",
field: "aliasTicketName",
autoSizeColumn: true,
formatter: function(str) {
return str.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
}
},
selectionMode: "none",
loadingMessage: "Loading data...",
noDataMessage: "No results found....",
allowSelectAll: true
}, "aliasNameGrid");
aliasNameGrid.refresh()
});
},
setAliasSource: function (data){
console.log(data);
this.aliasSource = data;
},
setAliasData: function (data){
this.aliasData = data;
},
getAliasSource: function (){
return this.aliasSource;
}
};
})();
Setting Data Store Data
aliasData = [{.....},
{.....},
{......];
require(["dijit/dijit"]);
aliasTicket.setAliasSource(aliasData);
dijit.byId('aliasNameGrid').refresh();
You are setting 'this.Store' to an object array, not a real 'dojo store' object. Following your code I can not see where you actually use 'this.Store'. Inside the grid code I do see a local variable named 'Store'.
So I'm not sure if I'm following your code example here but, you should 'set' the store of the grid and then refresh it. Something like this.
setAliasSource: function (data){
console.log(data);
this.Store = data;
dijit.byId('aliasNameGrid').set("store",new dojo.store.Observable(new dojo.store.Memory({ data: data,idProperty: "id"}));
dijit.byId('aliasNameGrid').refresh();
},
I'm working on implementing an Angular factory into a project I'm working on.
I've gotten routing working: ArtLogMain.js
var ArtLog = angular.module('ArtLog', ['ngGrid', 'ui.bootstrap']);
ArtLog.config(function ($locationProvider, $routeProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when("/ArtLog", {
controller: "ArtLogCtrl",
templateUrl: "/Templates/ArtLog/Index.html"
});
$routeProvider.when("/ArtLog/:Id", {
controller: "ArtLogEditCtrl",
templateUrl: "/Templates/ArtLog/Edit.html"
});
$routeProvider.when("/ArtLog/Dashboard", {
controller: "ArtLogDashBoardCtrl",
templateUrl: "/Templates/ArtLog/Dashboard.html"
});
$routeProvider.otherwise("/");
});
Next I setup the Factory: ArtLogDataService
ArtLog.factory("ArtLogDataService", function ($q) {
breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);
var _artLogView = [];
var _artLogSingle = [];
var _getArtLogById = function (Id) {
var deferred = $q.defer();
var manager = new breeze.EntityManager('breeze/BreezeData');
var query = new breeze.EntityQuery().from('Project').where("Id", "Equals", Id);
manager.executeQuery(query).then(function (data) {
angular.copy(data, _artLogSingle);
deferred.resolve();
}).fail(function () {
deferred.reject();
});
return deferred.promise;
};
var _getArtLogView = function () {
var deferred = $q.defer();
var manager = new breeze.EntityManager('breeze/BreezeData');
var query = new breeze.EntityQuery().from('ArtLogView');
manager.executeQuery(query).then(function (data) {
//angular.copy(data.results, _artLogView);
_artLogView = data.results;
deferred.resolve();
}).fail(function () {
deferred.reject();
});
return deferred.promise;
};
return {
artLogView: _artLogView,
artLogSingle: _artLogSingle,
getArtLogView: _getArtLogView,
getArtLogById: _getArtLogById
};
})
The the Controller: ArtLogController.js
function ArtLogCtrl($scope, ArtLogDataService) {
$scope.ArtLogData = ArtLogDataService;
$scope.editableInPopup = '<button id="editBtn" type="button" class="btn btn-primary" ng-click="edit(row)" >Edit</button>';
ArtLogDataService.getArtLogView();
$scope.edit = function (row) {
window.location.href = '/ArtLog/' + row.entity.Id;
};
$scope.gridOptions = {
data: ArtLogDataService.artLogView,
showGroupPanel: true,
enablePinning: true,
showFilter: true,
multiSelect: false,
columnDefs: [
{ displayName: 'Edit', cellTemplate: $scope.editableInPopup, width: 80, pinned: true, groupable: false, sortable: false },
{ field: 'ArtNum', displayName: 'Art Number', resizable: true, pinned: true, groupable: false, width: '100px' },
{ field: 'CreateDate', displayName: 'Date Created', cellFilter: "date:'MM-dd-yyyy'", pinnable: false, width: '110px' },
{ field: 'ArtCompletionDue', displayName: 'Art Comp Due Date', cellFilter: "date:'MM-dd-yyyy'", pinnable: false, width: '160px', enableCellEdit: true },
{ field: 'DaysLeft', displayName: 'Days Left', pinnable: false, width: '90px' },
{ field: 'RevisionNum', displayName: 'Rev Number', pinnable: false, width: '100px' },
{ field: 'Status', displayName: 'Status', pinnable: false, width: '80px' },
{ field: 'Template', displayName: 'Template', pinnable: false, width: '190px' },
{ field: 'Driver', displayName: 'Driver', pinnable: false, width: '160px' },
{ field: 'AssignedTo', displayName: 'Assigned To', pinnable: false, width: '160px' },
{ field: 'BuddyArtist', displayName: 'Buddy Artist', pinnable: false, width: '160px' }
],
filterOptions: {
filterText: "",
useExternalFilter: false
}
};
}
I set a breakpoint on ArtLogDataService.getArtLogData and I see the call fire. I also see the query run and data is returned, but When I look at the ArtLogDataService object returned from the factory it always shows Array[0]. The data never seems to bind to artLogView.
What am I doing wrong?
Thanks!
The problem is that your network callback from Breeze is not part of the Angular update loop. Angular doesn't know that your data changed, so the watcher on the view binding never gets updated.
You need to wire in a $scope.$apply() call when your data comes back. This will cause the bindings to notice the change in the data and update.
Perhaps something like this:
ArtLogDataService.getArtLogView().then(function() {
$scope.$apply();
});
If you do everything from within Angular, you never need to call $scope.$apply, because anything that can mutate data (events, network responses, timeouts, etc) will get handled by Angular (via $http and $timeout, etc) and $apply will automatically get called. It is in these situations where the data gets changed by an event from outside of Angular that $scope.$apply is necessary.
Hope this does it for you!
You do not ... and should not ... use $q.deferred in your query callbacks. The Breeze EntityManager methods already return promises ... $q promises when you use the Breeze.Angular module as explained in the documentation and demonstrated in samples such as "Todo-Angular".
Get rid of your hand-rolled promises and you won't need $apply either.
Should be something like this:
// Create or acquire the manager ONCE for the lifetime of your data service
// Todo: more testable to use a separate "entityManagerFactory" service
// to get your manager.
var manager = new breeze.EntityManager('breeze/BreezeData');
var _getArtLogView = function () {
return breeze.EntityQuery.from('ArtLogView')
.using(manager).execute()
.then(success)
.catch(fail); // only if you have something useful to do here when it fails
function success(data) { return data.results; }
function fail(error) {
// some kind of logging or useful error handling;
// otherwise don't bother with fail here
return $q.reject(error); // so caller sees it
}
};