I have a directive in which I am calling promise function inside constructor.Below is the snippet of it
class exampleController {
constructor(exampleService) {
var vm = this;
vm.itemLoaded = false;
exampleService.getSomeData().then(function(response){
vm.data = response;
vm.itemLoaded = true;
}
}
}
angular.module('exampleApp').controller('exampleController',exampleController)
.directive('exampleDirective', exampleDirective)
.service('exampleService', exampleService);
class exampleDirective {
constructor(){
this.controller = 'exampleController';
this.controllerAs = 'vm';
}
}
function exampleService() {
getData: function () {
return $http.get('url');
}
}
In template if I call
<example></example>
<example></example>
<example></example>
The updation of itemLoaded scope goes to last directive though each directive have it's respective async function got called and I also add a break point in promise function and for second/third directive I am getting first directive value.It's behaving like a singleton directive sharing same address for all 3 directive.
Anyone face this type of issue?I am using babel to convert it into ES5
Related
Learning AngularJS is a Work In Progress for me so I just want to understand why/when we should use one over another in particular case below. Is it just matter of taste or more than that? See examples below.
In both cases, when user clicks OK button, create() function of parent controller is called from child controller.
RESOLVE STYLE
CreateController
...
var vm = this;
vm.create = create;
function create() {
console.log('Created!');
}
vm.openCreateModal = function() {
vm.modalInstance = $uibModal.open({
...
resolve: {
create: function() {
return create;
},
// Others if any
}
});
}
...
CreateModalController
...
vm.ok = function() {
create();
$uibModalInstance.close('ok');
};
...
SCOPE STYLE
CreateController
...
var vm = this;
vm.create = create;
function create() {
console.log('Created!');
}
vm.openCreateModal = function() {
vm.modalInstance = $uibModal.open({
...
scope: $scope,
resolve: {
}
});
}
...
CreateModalController
...
vm.ok = function() {
$scope.$parent.vm.create();
$uibModalInstance.close('ok');
};
...
Update
The actual reason why I ask this question is, accessining/injecting parent/root/container like objects of one service/controller in/to another controller/service is considered as a "bad practise" in some languages/frameworks I use.
The idea of resolve is that it will run that first before initializing the rest of your code. Generally you would use resolve in your routing like so:
$routeProvider
.when('/', {
templateUrl: "views/view.html",
caseInsensitiveMatch: true,
resolve: {
load: function() {
localStorage['Location'] = "/View";
}
}
})
In the above example resolve will fire the load function before my controller is ever initialized. On the other hand scope is used to bind directly to something in a controller or directive. You should use scope when triggering functions and binding to values between controllers and directives.
To add to this based on the comments below, if the resolve fails it will reject the modal and the window will not open.
I'm trying to use a 1.5 component with AngularJS. I have a service that gets my JSON file using $HTTP and returns the promise. I then resolve the promise in my components controller and assign it to a value on the controller using this.work. Although this doesn't show in my HTML page. I have included comments in the code to explain a little better if this is confusing.
It's my understanding that the resolving of the promise (in my controller) is happening asynchronously but once it's resolved why am I not shown the updated changed to the variable $ctrl.work in the view. Instead I never get a value from the variable.
// Component
(function () {
angular.module('development')
.component('pgDev', {
templateUrl: 'app/development/development.template.html',
controller: ['workList', function (workList) {
//this.work = 'HELLO WORLD'; // <- this shows up in the html if uncommented
workList.getWorkItems().then(function (d) {
console.log(d.test); // outputs: myjsonfile
this.work = d.test; // <- this doesnt show in the html
});
}]
})
}());
// HTTP Get Service
(function () {
angular.module("development").factory("workList", ["$http",
function ($http) {
return {
getWorkItems: function () {
return $http.get("data/worklist/worklist.json").then(function (d) {
return d.data;
});
}
}
}])
})();
// html
workitems: {{$ctrl.work}}
You are loosing context of execution which means that this inside your callback function if not and instance of component controller. A simple (and modern) solution is to use arrow function instead of normal anonymous:
workList.getWorkItems().then(d => {
this.work = d.test;
});
I am dealing with angular 1 component, I made a datatable component which accepts a dataset as a parameter.
here is how I am using datatable component.
index.html
...
<datatable dataset="ViewModel.dataset"></datatable>
...
index.controller.js
(function() {
'use strict';
angular
.module('DashboardApplication')
.controller('PagesIndexController', PagesIndexController);
function PagesIndexController() {
var self = this;
self.dataset = {};
Restangular.one('someurl').get().then(function( pages ) {
self.dataset = pages;
});
}
})();
datatable.component.js
(function() {
'use strict';
angular
.module('DashboardApplication')
.component('datatable', {
bindings: {
dataset: '<'
},
templateUrl: '/frontend/templates/dashboard/components/data-table.html',
controller: 'DataTableController'
});
})();
datatable.controller.js
(function() {
'use strict';
angular
.module('DashboardApplication')
.controller('DataTableController', DataTableController);
function DataTableController() {
var self = this;
console.log(self.dataset); // which is undefined!
}
})();
The problem is I'm getting undefined for dataset in datatable.controller.js. Is there any solution for this?!
Use the $onChanges life-cycle hook to see the value when it becomes defined:
angular
.module('DashboardApplication')
.controller('DataTableController', DataTableController);
function DataTableController() {
var self = this;
//console.log(self.dataset); // which is undefined!
this.$onChanges = function(changesObj) {
if (changesObj.dataset) {
console.log(changesObj.dataset.currentValue);
};
});
}
For more information, see AngularJS Developer Guide -- Components.
I think you are missing a
controllerAs: 'vm'
line from your component which will bind the model to "this" in the your controller instead of $scope (also means you can reach your viewmodel as "vm" inside your view, like:
ng-if="vm.dataset"
But I think it will still be undefined in that exact moment, you have several options here:
you can pass the promise to the component and write a then on it
you can place an ng-if="dataset && dataset.length" where you call the component in your outer html. This way the logic inside your component will only trigger when there is actually data in the property.
<datatable ng-if="ViewModel.dataset" dataset="ViewModel.dataset"></datatable>
you can also write something like this in your component:
$scope.$watch('self.dataset', function () {
if (self.dataset) {
alert ('hi');
}
});
I'd like to create an utility class in Angular.js that can be used by several controllers.
So far I created this:
'use strict';
angular
.module('App')
.factory('AppUtils', AppUtils);
function AppUtils() {
var vm = this;
vm.getPersonOf = getPersonOf;
function getPersonOf(personId, allPersons) {
var person = {};
person.personId = {personId: personId};
allPersons.forEach(function(p) {
if (p.personId === personId) {
person = p;
}
});
return person;
}
}
And I tried to use it in a controller like this:
'use strict';
angular
.module('App')
.controller('AppController', AppController);
function AppController(personId, allPersons, AppUtils) {
var vm = this;
vm.personId = personId;
vm.person = AppUtils.getPersonOf(vm.personId, allPersons);
...
}
But I get this:
PhantomJS 1.9.8 (Windows 7 0.0.0) App should dismiss modal FAILED
Error: [$injector:undef] Provider 'AppUtils' must return a value from $get factory method.
http://errors.angularjs.org/1.5.0/$injector/undef?p0=InvoiceUnitsUtils
(The real names have been renamed to make it easier.)
Why am I getting that error? Am I not declaring properly the Utility module?
The factory is in charge of creating a service and handing its instance to you. To do this, you need to return your utility class:
function AppUtils() {
return {
getPersonOf: getPersonOf
// pass other utility functions...
}
function getPersonOf(personId, allPersons) {
var person = {};
person.personId = {personId: personId};
allPersons.forEach(function(p) {
if (p.personId === personId) {
person = p;
}
});
return person;
}
}
I removed the vm part because we are handing a service which usually has no view model (the controller is in charge of that, service is more of a business logic expert).
Here's some more information about the $get function in Angular's providers:
https://docs.angularjs.org/guide/providers
I am currently trying the 'new' ES6 + Angular combination and got stuck on interpolating a html string in a directive that contains scope bindings.
I have tried the following option:
Current situation
The following code works but it uses a filter instead of a directive.
HTML file
<div class="thumbnail-body">
<div ng-bind-html="vm.labels.hello | interpolate:this"></div>
</div>
filter in module (still old school angular without ES6)
//TODO: .filter('interpolate', () => new InterpolateFilter())
.filter('interpolate', function ($interpolate) {
return function (text, scope) {
if (text) {
return $interpolate(text)(scope);
}
};
});
The reason why I am trying to move the interpolate logic towards a
directive so I don't have to add a filter on multiple elements.
working but ugly situation
HTML file
<interpolate value="vm.labels.hello" scope="this"></interpolate>
Directive
class InterpolateDirective {
constructor() {
this.template = '<div ng-bind-html="value |interpolate:scope"></div>';
this.restrict = 'E';
this.scope = {value: '=',scope:'='};
}
}
export default InterpolateDirective;
Module
.directive('interpolate', () => new InterpolateDirective())
Desired situation (not working yet)
HTML file
<interpolate value="vm.labels.hello"></interpolate>
Directive
class InterpolateDirective {
constructor($interpolate,$scope) {
'ngInject';this.template = '<div ng-bind-html="value"> </div>';
this.restrict = 'E';
this.scope = {value: '='};
this.$interpolate = $interpolate;
this.$scope = $scope;
}
//optional compile or link function
link() {
this.scope.value = this.$interpolate(this.scope.value)(this);
}
}
export default InterpolateDirective;
Module
.directive('interpolate', () => new InterpolateDirective())
In short: I would like to work with the desired situation
Try this:
class InterpolateDirective {
constructor($interpolate) {
this.template = '<div ng-bind-html="value"> </div>';
this.restrict = 'E';
this.scope = {
value: '='
};
this.$interpolate = $interpolate;
InterpolateDirective.prototype.link = (scope) =>{
scope.value = this.$interpolate(scope.value)(this);
}
}
public static Factory() {
var directive = ($interpolate) => {
return new InterpolateDirective($interpolate);
};
directive['$inject'] = ['$interpolate'];
return directive;
}
}
export default InterpolateDirective;
.directive('interpolate', InterpolateDirective.Factory());
Scope in directives isn't injected like in controllers by dependency injection. Directive can access scope by first parameter of link function.
Scope defined by directive's object property isn't the same. It's a part of configuration to create directive's API by scope isolation.