AngularJS: after-select-item not triggering - angularjs

I am using angularjs version 1.6.4 with angular-multiple-select module for multi selecting. Every thing is working fine. I am able to select from suggestions but whenever i do selection "after-select-item" directive is not triggering. According to angular-multiple-select module documentation
afterSelectItem : Listen for event before adding an item
<div class="form-group float-label-control">
<label>Skills</label>
<multiple-autocomplete ng-model="model.user.skills"
object-property="name"
after-select-item="model.afterSelectItem"
suggestions-arr="model.skills">
</multiple-autocomplete>
</div>
My controller few code lines:
(function () {
"use strict";
var module = angular.module(__appName);
function fetchSkills($http) {
return $http.get(__apiRoot + "/skills")
.then(function (response) {
return response.data;
})
}
function controller($http) {
var model = this;
model.$onInit = function () {
fetchSkills($http).then(function (skills) {
model.skills = skills;
});
};
model.afterSelectItem = function (item) {
console.log("after select item");
console.log(item);
}
}
module.component("userEdit", {
templateUrl: "components/user-edit/user-edit.template.html",
bindings: {
userId: "<",
onUserSaved: "&"
},
controllerAs: "model",
controller: ["$http", controller]
});
}());

Related

Why my service does not share data between controllers?

I built a factory to get Data from the Database and pass to all controllers in my application like this:
(function () {
angular.module('appContacts')
.factory('dataService', ['$http', dataService]);
function dataService($http) {
return {
getCurrentOrganization: getCurrentOrganization,
};
function getCurrentOrganization(id) {
return $http({
method: 'GET',
url: '/api/organization/' + id + '/contacts'
})
}
}
})();
And I have a view like this:
<div ng-app="myapp">
<div ng-controller="contactController">
<a ui-sref="organization({Id: organization.id})" ng-click="vm.setCurrentOrganization(organization)"> {{organization.organizationName }}</a>
</div>
</div>
That link redirect from a view the view contactsView.html to a detail view organizationDetail.html managed by a second controller:
....
.state("home", {
url: "/",
templateUrl: "views/contactsView.html",
controller: "contactsController",
controllerAs: "vm"
})
.state("organization", {
url: "/organization/:Id",
templateUrl: "views/organizationDetail.html",
params: { Id: null },
controller: "organizationsController",
controllerAs: "vm"
})
...
My problem is that I get the data, I see in the console, but when the new URL comes into place, the Data is gone and the view is shown empty.
How could I use the data produced in the factory in the second Controller?
EDIT:
Here are the Controllers:
//organizationsController.js
(function () {
"use strict";
angular.module('appContacts')
.controller('organizationsController', function organizationsController(dataService) {
var vm = this;
vm.setCurrentOrganization = function (organization) {
vm.theOrganization = organization;
vm.visible = true;
dataService.getCurrentOrganization(vm.theOrganization.id).then(function (result) {
vm.organizationData = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
}
});
})();
And the contactsController:
//contactsController.js
(function () {
"use strict";
angular.module('appContacts')
.controller('contactsController', function contactsController(dataService) {
var vm = this;
vm.visible = false;
activate();
function activate() {
dataService.getAllContacts().then(function (result) {
vm.allcontacts = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
dataService.getAllOrganizations().then(function (result) {
vm.organizations = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
}
});
})();
The problem is that I click the llink in the view A (contactsView.html/ContactsViewController) and I should end in the VIEW B (OrganizationDetails.html/organizationController), using the Data fetch in the service.
You are doing it wrong here
<div ng-app="myapp">
<div ng-controller="contactController">
<a ui-sref="organization({Id: organization.id})" ng-click="vm.setCurrentOrganization(organization)"> {{organization.organizationName }}</a>
</div>
</div>
Your contactController does not have the function setCurrentOrganization. Instead its in another controller. you can remove the code ng-click="vm.setCurrentOrganization(organization)" from the HTML. and read the id using $stateParams in the organizationsController. After getting the id, use it call the service as below:
dataService.getCurrentOrganization(id).then(function (result) {
vm.organizationData = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});

AngularJS: Append to url the page number while paginating

I am working on a application where i am paginating through some records by making calls to the server like random/api/endpoint?page=1/2/3
Now i while i paginate,
i need to append the page i am requesting to the url like http://www.paginate.com/somedata/{1/2/3} and on opening this url it should also fetch that specific page in the view {meaning if i navigate to hhtp://www.paginate.com/somedata/4 then the app/view should reflect data from the api call random/api/endpoint?page=4}.
Presently i am using angular-route 1.4.12 with the same version of AngularJS. Very new to angular (2 days), any help will be greatly appreciated.
EDIT : What i want to do ?
When i click next while paginating, it should append the pageNumber to the url.
route.js
angular
.module('mainRouter', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/somedata/:page', {
templateUrl: 'partials/somedata.html',
controller: 'PaginationCtrl',
controllerAs: 'vm',
reloadOnSearch: false
}).
otherwise( { redirectTo: "/somedata/1" });
}
]);
PaginationCtrl.js
angular
.module('controllers.Pagination', [])
.controller('PaginationCtrl', PaginationCtrl);
PaginationCtrl.$inject = ['$routeParams', 'paginationService'];
function PaginationCtrl ($routeParams, paginationService) {
var vm = this;
vm.paginationData = {
perPage: 10,
currentPage: 1
};
vm.isLoading = false;
vm.paginate = paginate;
paginate(vm.paginationData.currentPage);
calculateTotalPages();
function calculateTotalPages () {
paginationService.findAll(0, 0)
.success(function (res) {
var paginationData = vm.paginationData || {};
paginationData.totalPages = Math.ceil(res.count / paginationData.perPage);
})
.error(function (res) {
console.log('Error trying to get the total number of pages', res);
});
}
function paginate (pageNumber, perPage) {
vm.isLoading = true;
var paginationData = vm.paginationData || {};
if (! perPage) {
perPage = paginationData.perPage;
}
console.log($routeParams);
paginationService.findAll(perPage, pageNumber)
.success(function (res) {
paginationData.items = res.documents;
vm.isLoading = false;
})
.error(function (res) {
console.log('Error fetching more Logs', res);
});
}
}
PaginationService.js
angular
.module('services.Pagination', [])
.service('paginationService', PaginationService);
PaginationService.$inject = ['$http', 'Constants'];
function PaginationService ($http, Constants) {
// console.log($http);
this.findAll = function (perPage, page) {
var url = Constants.baseUrl + '/sms/get/data';
if (page > 0) {
url += '?page=' + page;
}
return $http.get(url);
};
}
directive being used
var app = angular.module('directives.Pagination', []);
app.directive('pagination', [function () {
return {
restrict: 'E',
template: '<div class="ui pagination menu"> \
<a class="icon item" ng-click="vm.previous()"><i class="left arrow icon"></i></a> \
<div class="icon item">{{ vm.paginationData.currentPage }} / {{ vm.paginationData.totalPages }}</div> \
<a class="icon item" ng-click="vm.next()"><i class="right arrow icon"></i></a> \
</div>',
scope: '=',
link: function (scope, element, attrs) {
var vm = scope.vm;
vm.paginationData.currentPage = 1;
vm.next = function () {
vm.paginationData.currentPage++;
if (vm.paginationData.currentPage > vm.paginationData.totalPages) {
vm.paginationData.currentPage = vm.paginationData.totalPages;
}
vm.paginate(vm.paginationData.currentPage);
};
vm.previous = function () {
vm.paginationData.currentPage--;
if (vm.paginationData.currentPage < 1) {
vm.paginationData.currentPage = 1;
}
vm.paginate(vm.paginationData.currentPage);
};
}
};
}]);
You should be able to access your :page parameter via $routeParams, which you've already injected in your controller.
Just call paginate with $routeParams.page instead of your default of 1.
In order to update the url as you go (in such a way that allows the user to copy the url for later use), without updating the route and re-initializing the controller, you can just call $location.search({page: page}). When this is called with reloadOnSearch set to false (as you've already done) it shouldn't re-initalize the controller.
Lastly, in case its not clear, you'll have to update the URL at the same time you make your API call. There isn't a built in angular way to do this, but it should be pretty straightforward.

RxJs and Angular1 component - how to avoid $scope?

Pseudo code for angular 1.5 component with RxJs:
component('demo', {
template: `<div>
<div ng-if="verificationFailed">Sorry, failed to verify</div>
<button ng-if="continueEnabled">Continue</button>
<button ng-click="verify()">Verify</button>
</div>`,
controllerAs: 'ctrl',
bindings: {
someOptions: '='
},
controller: ($scope, someService) => {
var ctrl = this;
ctrl.continueEnabled = false;
ctrl.verificationFailed = false;
ctrl.verify = function() {
Rx
.Observable
.interval(10 * 1000)
.timeout(2 * 60 * 1000)
.flatMapLatest(_ => { someService.verify(ctrl.someOptions.id)})
.retry(1)
.filter((result) => { result.completed })
.take(1)
.subscribe(_ => {
$scope.$evalAsync(_ => {
ctrl.continueEnabled = true
});
}, _ => {
$scope.$evalAsync(() => {
ctrl.verificationFailed = true;
});
});
};
}
});
Any way to avoid using $scope with $evalAsync to trigger digest? Without it the view is simply not updating.
Why? Because there is no $scope on angular2 and i want to make migration as easy as it is possible
You can use angular1-async-filter. Take a look at this good article:
http://cvuorinen.net/2016/05/using-rxjs-observables-with-angularjs-1/
Here is an example:
(function(angular) {
var myComponent = (function () {
function myComponent() {
this.template = "<div><br/> Time: {{ctrl.time | async:this}}</div>";
this.controllerAs = 'ctrl';
this.controller = "myController";
}
return myComponent;
}());
var myController = (function() {
function myController() {
this.time = Rx.Observable.interval(1000).take(50);
}
return myController;
}());
angular.module('myApp', ['asyncFilter']);
angular.module('myApp').component('myComponent', new myComponent());
angular.module('myApp').controller('myController', myController);
})(window.angular);
See it working on Plunker:
https://plnkr.co/edit/80S3AG?p=preview

AngularJs UI Grid rebind from Modal

I have a main controller in which I load data into a "angular-ui-grid" and where I use a bootstrap modal form to modify detail data, calling ng-dlbclick in a modified row template :
app.controller('MainController', function ($scope, $modal, $log, SubjectService) {
var vm = this;
gridDataBindings();
//Function to load all records
function gridDataBindings() {
var subjectListGet = SubjectService.getSubjects(); //Call WebApi by a service
subjectListGet.then(function (result) {
$scope.resultData = result.data;
}, function (ex) {
$log.error('Subject GET error', ex);
});
$scope.gridOptions = { //grid definition
columnDefs: [
{ name: 'Id', field: 'Id' }
],
data: 'resultData',
rowTemplate: "<div ng-dblclick=\"grid.appScope.editRow(grid,row)\" ng-repeat=\"(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name\" class=\"ui-grid-cell\" ng-class=\"{ 'ui-grid-row-header-cell': col.isRowHeader }\" ui-grid-cell></div>"
};
$scope.editRow = function (grid, row) { //edit row
$modal.open({
templateUrl: 'ngTemplate/SubjectDetail.aspx',
controller: 'RowEditCtrl',
controllerAs: 'vm',
windowClass: 'app-modal-window',
resolve: {
grid: function () { return grid; },
row: function () { return row; }
}
});
}
});
In the controller 'RowEditCtrl' I perform the insert/update operation and on the save function I want to rebind the grid after insert/update operation. This is the code :
app.controller('RowEditCtrl', function ($modalInstance, $log, grid, row, SubjectService) {
var vm = this;
vm.entity = angular.copy(row.entity);
vm.save = save;
function save() {
if (vm.entity.Id === '-1') {
var promisePost = SubjectService.post(vm.entity);
promisePost.then(function (result) {
//GRID REBIND ?????
}, function (ex) {
$log.error("Subject POST error",ex);
});
}
else {
var promisePut = SubjectService.put(vm.entity.Id, vm.entity);
promisePut.then(function (result) {
//row.entity = angular.extend(row.entity, vm.entity);
//CORRECT WAY?
}, function (ex) {
$log.error("Subject PUT error",ex);
});
}
$modalInstance.close(row.entity);
}
});
I tried grid.refresh() or grid.data.push() but seems that all operation on the 'grid' parameter is undefinied.
Which is the best method for rebind/refresh an ui-grid from a bootstrap modal ?
I finally solved in this way:
In RowEditCtrl
var promisePost = SubjectService.post(vm.entity);
promisePost.then(function (result) {
vm.entity.Id = result.data;
row.entity = angular.extend(row.entity, vm.entity);
$modalInstance.close({ type: "insert", result: row.entity });
}, function (ex) {
$log.error("Subject POST error",ex);
});
In MainController
modalInstance.result.then(function (opts) {
if (opts.type === "insert") {
$log.info("data push");
$scope.resultData.push(opts.result);
}
else {
$log.info("not insert");
}
});
The grid that received inside RowEditCtrl is not by reference, so it wont help to refresh inside the RowEditCtrl.
Instead do it right after the modal promise resolve in your MainController.
like this:
var modalInstance = $modal.open({ ...});
modalInstance.result.then(function (result) {
grid.refresh() or grid.data.push()
});

Directive inside $modal window throws "undefined is not a function"

Using ui-bootstrap I have a really simple custom directive that lists alerts at the top of the page. On normal pages it works like a champ. When I use my directive inside a $modal popup I get "undefined is not a function" at ngRepeatAction.
The directive I have behind the modal on the main page still works. I can see it behind the modal. It's just the one in the modal popup that breaks. What am I doing wrong?
Modal open code:
$modal.open({
templateUrl: 'partials/main/servers/serverAuths/edit.html',
controller: function($scope, $modalInstance) {
$scope.auth = angular.copy(auth);
$scope.auth.password = null;
$scope.saveAuth = function() {
Auths.editAuth($scope.auth).then(
function(resp) {
if (resp.rc===0) {
Alerts.addAlert('success', 'Auth `'+$scope.auth.name+'` saved.');
_.extend(auth, $scope.auth);
$modalInstance.close();
} else {
Alerts.addAlert('danger', 'Auth `'+$scope.auth.name+'` could not be saved. ' + resp.message, 'serverAuths');
}
}
);
};
$scope.resetAuth = function() {
$modalInstance.close();
};
}
}).result.then(
function() {
Auths.getAuthList().then(
function(resp) {
$scope.auths = resp;
}
);
}
);
Directive template:
<div class="alert-wrapper alert-{{ alert.type }}"
ng-repeat="alert in alerts"
ng-class="{ 'relative':relative }">
<div class="container">
<div alert type="alert.type" close="closeAlert($index)">{{alert.msg}}</div>
</div>
</div>
Directive code:
angular.module('app')
.directive('appAlerts', function() {
return {
restrict: 'A',
replace: true,
scope: {
watchForm: '=',
relative: '#'
},
templateUrl: 'partials/directives/appAlerts.html',
controller: function($scope, Alerts) {
$scope.closeAlert = function(idx) { Alerts.closeAlert(idx); };
$scope.alerts = Alerts.getAlerts();
}
};
});
Alerts Factory:
angular.module('app').factory('Alerts', function($timeout) {
var alerts = [];
function timeoutAlert(a) {
$timeout(function() {
a.splice(0, 1);
}, 2500);
}
var addAlert = function(type, msg) {
alerts.push({type:type, msg:msg});
timeoutAlert(alerts);
};
var closeAlert = function(index) {
alerts.splice(index, 1);
};
var getAlerts = function() {
return alerts;
};
var killAlert = function(msg) {
var alert = _.where(alerts, {msg:msg});
var idx = _.indexOf(alerts, alert[0]);
if (idx > -1) {
closeAlert(idx);
}
};
return {
addAlert:addAlert,
closeAlert:closeAlert,
getAlerts:getAlerts,
killAlert:killAlert
};
});

Resources