Issue with pre-populating angular-ui's select2 - angularjs

I have an issue with angular-ui/select2 control.
I would like to use angularjs to pre-populate the control with an array of objects. I use the init function in order to try and achieve this but somehow, the view does not get updated on the page...
Here the client module:
angular.module('searchEngineModule', ['ng', 'languageChooserModule','geolocationModule','currentMemberAddressModule', 'addressAutocompleteModule'])
.factory('searchEngineService', function(){
})
.controller('searchEngineCtrl', [ '$scope', '$http', 'languageChooserService', 'retrieveDefaultLanguagesService', 'geolocationService', 'currentMemberAddressService', 'addressAutocompleteService','addressFromReferenceService', function geolocationCtrl($scope, $http, languageChooserService, retrieveDefaultLanguagesService, geolocationService, currentMemberAddressService, addressAutocompleteService, addressFromReferenceService) {
$scope.searchCriteria = {};
$scope.languageChooser = languageChooserService;
$scope.addressAutocomplete = addressAutocompleteService;
$scope.init = function() {
retrieveDefaultLanguagesService.defaultLanguages().then(function(languages){
$scope.searchCriteria.languages = [{}];
$scope.searchCriteria.languages= languages;//HERE: it does populate the model but the view is not updated...
});
geolocationService.geolocationAddress().then(function(address) {
$scope.geolocationAddress = {};
$scope.geolocationAddress = address;
});
currentMemberAddressService.currentMemberAddress().then(function(address){
$scope.currentMemberAddress = {};
$scope.currentMemberAddress = address;
});
};
$scope.$watch('addressAutocomplete', function (newVal, oldVal) {
if (oldVal == newVal) return;
$scope.onTheFlyAddress = {};
if(newVal){
addressFromReferenceService.addressFromReference(newVal.reference).then(function(address){
$scope.onTheFlyAddress = address;
});
}
}, true);
$scope.performSearch = function(){
console.log('performSearch');
console.log($scope.searchCriteria);
};
}])
.config(function($httpProvider) {
$httpProvider.defaults.headers.common['Content-Type'] = 'application/json';
$httpProvider.defaults.headers.common['X-Ajax'] = 'true';
});
Here is the languageChooserModule:
angular.module('languageChooserModule', ['ng', 'ui.select2'])
.factory('languageChooserService', function(){
return select2Options();
})
.factory('retrieveDefaultLanguagesService', ['$http', '$q', function($http, $q){
function retrieveDefaultLanguagesP(){
var deferred = $q.defer();
var defaultLanguages = [{}];
$http.get('/bignibou/utils/findLanguagesByLanguageStartingWith.json', {params:{language: 'fran'}})
.success(function(languages){
defaultLanguages = languages;
deferred.resolve(defaultLanguages);
});
return deferred.promise;
}
return{
defaultLanguages: function(){
return retrieveDefaultLanguagesP();
}
};
}]);
function select2Options(){
function format(item) {
return item.description;
}
return {
simple_tags: false,
multiple : true,
contentType: "application/json; charset=utf-8",
minimumInputLength : 3,
data:{ text: "description" },
formatSelection: format,
formatResult: format,
ajax : {
url : "/bignibou/utils/findLanguagesByLanguageStartingWith.json",
dataType : 'json',
data : function(term) {
return {
language : term
};
},
results : function(data, page) {
return {
results :
data.map(function(item) {
return {
id : item.id,
description : item.description,
version : item.version
};
}
)};
}
}
};
}
Can anyone please help?
edit 1:
Chaging to the following:
retrieveDefaultLanguagesService.defaultLanguages().then(function(languages){
$scope.searchCriteria.languages = [{}];
$scope.searchCriteria.languages= languages;
$scope.$digest();
});
Causes the following Error:
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.2.1/$rootScope/inprog?p0=%24digest
at http://localhost:8080/bignibou/js/libs/angular.js:78:12
at beginPhase (http://localhost:8080/bignibou/js/libs/angular.js:11878:15)
at Scope.$digest (http://localhost:8080/bignibou/js/libs/angular.js:11412:9)
at Scope.$delegate.__proto__.$digest (<anonymous>:844:31)
at http://localhost:8080/bignibou/js/custom/searchEngineModule.js:18:12
at wrappedCallback (http://localhost:8080/bignibou/js/libs/angular.js:10597:81)
at http://localhost:8080/bignibou/js/libs/angular.js:10683:26
at Scope.$eval (http://localhost:8080/bignibou/js/libs/angular.js:11576:28)
at Scope.$digest (http://localhost:8080/bignibou/js/libs/angular.js:11421:31)
at Scope.$delegate.__proto__.$digest (<anonymous>:844:31)
edit 2:
Changing to the following:
$scope.$apply(function(){
retrieveDefaultLanguagesService.defaultLanguages().then(function(languages){
$scope.searchCriteria.languages= languages;
});
});
causes the following error:
Error: [$rootScope:inprog] $apply already in progress
http://errors.angularjs.org/1.2.1/$rootScope/inprog?p0=%24apply
at http://localhost:8080/bignibou/js/libs/angular.js:78:12
at beginPhase (http://localhost:8080/bignibou/js/libs/angular.js:11878:15)
at Scope.$apply (http://localhost:8080/bignibou/js/libs/angular.js:11675:11)
at Scope.$delegate.__proto__.$apply (<anonymous>:855:30)
at Scope.$scope.init (http://localhost:8080/bignibou/js/custom/searchEngineModule.js:17:11)
at http://localhost:8080/bignibou/js/libs/angular.js:9885:21
at Scope.$eval (http://localhost:8080/bignibou/js/libs/angular.js:11576:28)
at pre (http://localhost:8080/bignibou/js/libs/angular.js:18210:15)
at nodeLinkFn (http://localhost:8080/bignibou/js/libs/angular.js:6104:13)
at compositeLinkFn (http://localhost:8080/bignibou/js/libs/angular.js:5536:15)

If your return value from retrieveDefaultLanguagesService.defaultLanguages() is a $q.defer().promise then (ha!) then will cause a digest to occur and therefore $apply, so your edits are redundant. If you need to do that in the future (usually rare) you should do it this way:
if(!rootScope.$$phase)rootScope.$apply();
To reduce some complexity I would also suggest removing the initialization of searchCriteria and initializing your object structure within your then success callback. Like this:
retrieveDefaultLanguagesService.defaultLanguages().then(function(languages){
$scope.searchCriteria = {languages:languages};
});
If that doesn't work I might guess that your html is incorrect in some way. if you share it you might find more help.
I'm also using angluarjs 1.2.3 and ui-select2 with no issues

I forgot to mention that I use angular 1.2.1 and according to this post: (https://stackoverflow.com/a/20186141/536299) there appears to be an incompatibility between angular js 1.2 and angular ui select2....

Related

Error: [$compile:multidir] Multiple directives [ngSwitchWhen, ngInclude] asking for transclusion

I need a solution for preloading data with ng-include like in this blog post preloading-data-before-executing-nginclude-in-angularjs described.
It seems that this solution works prity well, but the author works with angular version 1.0.7.
Im using version 1.2.1 and have also tried version 1.7.9 and the result is the error message.
Error: [$compile:multidir] Multiple directives [ngSwitchWhen, ngInclude] asking for transclusion on: <div ng-switch-when="one" bn-preload="oneData" bn-include-log="" ng-include=" 'one.htm' ">
How can i fix it with the newer version, or is there a better solution?
In my project, i receive data by reqest and the ng-include should be waiting till the request ends and the data to show are available.
Edit:
My HTML code is like this
<div ng-init="validityList = validities.UploadList;" bn-preload="validities" ng-include="'/validity/list'"></div>
And the error in my project is
Multiple directives [ngInclude, bnPreload] asking for transclusion on
The solution Lemmy suggestet is not working for me, because bn-preload and ng-include need to be on the same element.
Edit2:
I don't know what you mean by "bn-preload html".
This is what i have done so far in angular.
const BaseApp = angular.module('BaseApp')
.factory('preloader', function ($q, $interval) {
this.load = function (target) {
const deferred = $q.defer();
const loadData = $interval(function () {
if ($q[target].length > 0) {
deferred.resolve($q[target]);
$interval.cancel(loadData);
}
}, 100);
return (deferred.promise);
}
})
.directive('bnPreload', function (preloader) {
function compile(templateElement, templateAttribute, transclude) {
function link($scope, element, attributes) {
let injectedElement = null;
let isDestroyed = false;
preloader.load(attributes.bnPreload).then(
function (preloadedData) {
if (isDestroyed) {
return;
}
$scope.setData(preloadedData);
transclude($scope, function (copy) {
element.after(injectedElement = copy);
});
}
);
$scope.$on(
'$destroy',
function () {
isDestroyed = true;
$(injectedElement).remove();
}
);
}
return (link);
}
return ({
compile: compile,
priority: 250,
terminal: true,
transclude: 'element'
});
})
.controller('validity', function ($scope, $http) {
$http({
method: 'POST',
url: '/validity/getall'
})
.then(function (response) {
$scope.validities = response.data;
});
});
Most of the code is equal to the example from the link above.

Cannot read property 'tags' of undefined: angular

I am building a vocabulary application that would sort the words based on the authors, books, tags, etc. the foll is an example of my JSON:
{
"expression": "mithrandir",
"meaning": "language of the elves",
"example": ["mithrandir is cool", "the elves speak mithrandir"],
"pronunciation": "",
"notes": "",
"meta": {
"book": ["There and back again"],
"author": ["Frodo Baggins"],
"tags": ["middle earth", "elves"]}
},
I have three drop downs in my view to filter agsinst book, author, and tags property in my json. Now the aforementioned lists are loading abs fine until i move my json to firebase.
the foll is my factory:
angular
.module("ngClassifieds")
.factory("classifiedsFactory", function($http, $firebaseArray){
var ref = new Firebase('https://PATH.firebaseio.com/');
return {
ref: $firebaseArray(ref)
}
});
})();
the foll is my controller:
angular
.module("ngClassifieds")
.controller("classifiedsCtrl", function($scope, $state, $http, classifiedsFactory, $mdSidenav, $mdToast, $mdDialog) {
$scope.classifieds = classifiedsFactory.ref;
$scope.classifieds.$loaded().then(function(classifieds) {
$scope.tags = getTags(classifieds); // call the getTags method below
$scope.books = getBooks(classifieds); // call the getBooks method below
$scope.authors = getAuthors(classifieds); // call the getAuthors method below
$scope.order = ""; //for sorting in asc or desc order
});
'tags', 'books', and 'authors' are arrays that are used to capture the unqique properties. the foll is an example of the getTags() method.
function getTags(classifieds) {
var tags = [];
angular.forEach(classifieds, function(item) {
angular.forEach(item.meta.tags, function(tag) {
tags.push(tag);
});
});
return _.uniq(tags);
}
The foll is the error im getting in chrome:
TypeError: Cannot read property 'tags' of undefined
at classifieds.ctr.js:135
at Object.forEach (angular.js:321)
at getTags (classifieds.ctr.js:134)
at classifieds.ctr.js:20
at processQueue (angular.js:15552)
at angular.js:15568
at Scope.$eval (angular.js:16820)
at Scope.$digest (angular.js:16636)
at angular.js:16859
at completeOutstandingRequest (angular.js:5804)
And the foll is the error in firefox:
Error: item.meta is undefined
getTags/<#http://localhost:8080/components/classifieds/classifieds.ctr.js:135:5
forEach#http://localhost:8080/node_modules/angular/angular.js:321:11
getTags#http://localhost:8080/components/classifieds/classifieds.ctr.js:134:4
#http://localhost:8080/components/classifieds/classifieds.ctr.js:20:18
processQueue#http://localhost:8080/node_modules/angular/angular.js:15552:28
scheduleProcessQueue/<#http://localhost:8080/node_modules/angular/angular.js:15568:27
$RootScopeProvider/this.$get</Scope.prototype.$eval#http://localhost:8080/node_modules/angular/angular.js:16820:16
$RootScopeProvider/this.$get</Scope.prototype.$digest#http://localhost:8080/node_modules/angular/angular.js:16636:15
$RootScopeProvider/this.$get</Scope.prototype.$evalAsync/<#http://localhost:8080/node_modules/angular/angular.js:16859:15
completeOutstandingRequest#http://localhost:8080/node_modules/angular/angular.js:5804:7
Browser/self.defer/timeoutId<#http://localhost:8080/node_modules/angular/angular.js:6081:7
I realize that variations of this query have been floating around in the forum, but I've been unable to apply the solutions proposed in those threads to my code.
According to the result of classifieds variable (http://imgur.com/6d7fWga), I think there's missing meta key in some specific objects.
Please update your getTags function like this and tell me your output:
function getTags(classifieds) {
var tags = [];
angular.forEach(classifieds, function(item) {
if(item.meta){
angular.forEach(item.meta.tags, function(tag) {
tags.push(tag);
});
}
});
return _.uniq(tags);
}
Try this
angular.forEach(item.tags, function(tag) {
tags.push(tag);
});
instead of
angular.forEach(item.meta.tags,function(tag) {
tags.push(tag);
});

How to dynamical set de widgetDefinitions in malhar angular dashboard via rest client?

I have installed malhar-angular-dashboard module and I want to populate some widgetDefinitions with my rest data.
HTML
<div ng-controller="widgetCtrl">
<div dashboard-layouts="layoutOptions" class="dashboard-container"></div>
</div>
widgetRestService
.factory('widgetRestService',['$http','UrlService','$log','$q',
function($http,UrlService,$log,$q){
var serviceInstance = {};
serviceInstance.getInfo = function(){
var request = $http({method: 'GET', url: '/rest/widgets/getListInfoDashboards'})
.then(function(success){
serviceInstance.widgets = success.data;
$log.debug('serviceInstance.widgets SUCCESS',serviceInstance.widgets);
},function(error){
$log.debug('Error ', error);
$log.debug('serviceInstance.widgets ERROR',serviceInstance.widgets);
});
return request;
};
serviceInstance.getAllWidgets = function () {
if (serviceInstance.widgets) {
return serviceInstance.widgets;
} else {
return [];
}
};
return serviceInstance;
}])
My rest service returns me this array of 3 objects :[{"name":"widgetList","title":" "},{"name":"widgetPie","title":" "},{"name":"widgetTable","title":" "}]
OtherService
.factory("OtherService", ["widgetRestService", "$log", "$q",
function (widgetRestService, $log, $q) {
var deferred = $q.defer();
widgetRestService.getInfo().then(function () {
deferred.resolve(widgetRestService.getAllWidgets());
});
return deferred.promise;
}])
Controller
OtherService.then(function(response){
$scope.layoutOptions = { // layout with explicit save
storageId: 'demo-layouts-explicit-save',
storage: localStorage,
storageHash: 'fs4df4d51',
widgetDefinitions:response , //must be a list
defaultWidgets: [],
explicitSave: true,
defaultLayouts: [
{title: 'Layout 1', active: true, defaultWidgets: []}
]
};
$log.debug('layoutOptions =',$scope.layoutOptions);
});
Result
layoutOptions: Object{defaultLayouts:Array[1],
defaultWidgets:Array[0],
explicitSave:true,
storage:Storage,
storageHash:'fs4df4d51',
storageId: 'demo-layouts-explicit-save',
widgetDefinitions: Array[3]}
TypeError: Cannot read property '$$hashKey' of undefined
at Object.extend (http://localhost:9000/bower_components/angular/angular.js:406:14)
at Object.LayoutStorage (http://localhost:9000/bower_components/malhar-angular-dashboard/dist/malhar-angular-dashboard.js:1064:15)
I searched at line 2: Object.LayoutStorage and I found out this:
angular.extend(defaults, options); //options = undefined (should have some keys and my widgetDefinitions array)
angular.extend(options, defaults);
The options variable is undefined only when I want to setup the $scope.layoutOptions within the then callback function.
Any advice how to set / avoid this ?
The problem is that the dashboard-layouts directive will compile before the asynchronous call from OtherService has finished, which means $scope.layoutOptions will be undefined.
A simple solution is to prevent the dashboard-layouts directive from compiling before $scope.layoutOptions is available.
You can do this by using ng-if:
<div ng-if="layoutOptions" dashboard-layouts="layoutOptions" class="dashboard-container">
</div>

Restangular no BaseUrl when do PUT

i'm using Restangular and trying to PUT some data but it seems to lose the BaseUrl.
In the config function i define the BaseUrl for Restangular and others Restangular fields.
Constants.restangularBaseUrl is http://192.168.1.100/api/
RestangularProvider.setBaseUrl(Constants.restangularBaseUrl)
.setRestangularFields({
selfLink: '_links.self.href',
id: '_id',
etag: '_etag'
})
.addResponseInterceptor(function(data, operation, what, url, response, deferred){
if (operation === 'getList') {
var result = data._items;
result._meta = data._meta;
result._links = data._links;
return result;
}
return data;
});
Then i have some models like this:
(function(){
angular.module('models.ebayItems', ['services.constants', 'restangular'])
.service('EbayItems', ['Constants', 'Restangular', function (Constants, Restangular) {
Restangular.extendModel('ebayitems', function(model) {
model.toggleMonitor = function(){
var item = this;
Restangular.one('ebayitems', this._id).patch({active: this.active}, '', {'If-Match': this._etag})
.then(function(data){
item._etag = data._etag;
}, function(error){
console.log('error', error);
});
};
return model;
});
var ebayItems = Restangular.all('ebayitems');
var ebayItemsOneSearch = function(_id){
return ebayItems.customGETLIST('', {where: {searchId: _id}});
};
return {
items: ebayItems,
oneSearch: ebayItemsOneSearch
};
}])
})();
Now when i try to do a put request with an item based on that model:
item.put()
it uses the wrong url, i mean it loses the BaseUrl, so instead of putting at:
http://192.168.1.100/api/ebayitems/12345
it puts at
http://192.168.1.100/ebayitems/12345
resulting in a 404 error.
Why?
What am i doing wrong?
Any help really appreciated.
Thank you
The problem was that setting a selfLink field that was a relative url from the API backend it overrides the BaseUrl.
Removing that field from the config function it worked.
RestangularProvider.setBaseUrl(Constants.restangularBaseUrl)
.setRestangularFields({
id: '_id',
etag: '_etag'
})
.addResponseInterceptor(function(data, operation, what, url, response, deferred){
if (operation === 'getList') {
var result = data._items;
result._meta = data._meta;
result._links = data._links;
return result;
}
return data;
});

Angular call service on asynchronous data

I have a service that make some calls to retrieve data to use in my app. After I've loaded data, I need to call another service to make some operations on my data. The problem is that second service will not have access to the data of the first service.
I've made a plunker: plunkr
First service
app.factory('Report', ['$http', function($http,$q){
var Authors = {
reports : [],
requests :[{'url':'data.json','response':'first'},
{'url':'data2.json','response':'second'},
{'url':'data3.json','response':'third'}]
};
Authors.getReport = function(target, source, response, callback) {
return $http({ url:source,
method:"GET",
//params:{url : target}
}).success(function(result) {
angular.extend(Authors.reports, result)
callback(result)
}
).error(function(error){
})
}
Authors.startQueue = function (target,callback) {
var promises = [];
this.requests.forEach(function (obj, i) {
console.log(obj.url)
promises.push(Authors.getReport(target, obj.url, obj.response, function(response,reports){
callback(obj.response,Authors.reports)
}));
});
}
return Authors;
}])
Second service
app.service('keyService', function(){
this.analyze = function(value) {
console.log(value)
return value.length
}
});
Conroller
In the controller I try something like:
$scope.result = Report.startQueue('http://www.prestitiinpdap.it', function (response,reports,keyService) {
$scope.progressBar +=33;
$scope.progress = response;
$scope.report = reports;
});
$scope.test = function(value){
keyService.analyze($scope.report.about);
}
I think this is what you are going for? Essentially, you want to call the second service after the first succeeds. There are other ways of doing this, but based on your example this is the simplest.
http://plnkr.co/edit/J2fGXR?p=preview
$scope.result = Report.startQueue('http://www.prestitiinpdap.it', function (response,reports) {
$scope.progressBar +=33;
$scope.progress = response;
$scope.report = reports;
$scope.test($scope.report.about); //added this line
});
$scope.test = function(value){
$scope.example = keyService.analyze(value); //changed this line to assign property "example"
}
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p>Progress notification : {{progress}}!</p>
<div ng-show="show">
<progress percent="progressBar" class="progress-striped active"></progress>
</div>
<pre>{{report}}</pre>
<pre>{{report.about}}</pre>
{{example}} <!-- changed this binding -->
</body>

Resources