When i create a component with a function which is returning a component object, my component is not initialize! I am sharing these two situation. Can someone explain me what is the difference between them?
Html:
<div ng-app="demoApp">
<navbar></navbar>
</div>
Working code: Fiddle
var NavbarTemplate = `<button ng-click="$ctrl.clickTest()">Click Test</button>`;
var navbar = {
controller: function() {
this.clickTest = clickTest;
function clickTest() {
alert("hello");
}
},
template: NavbarTemplate
};
angular.module('demoApp', []).component('navbar', navbar);
Faulty (without error) code: Fiddle
function getComponent(){
var template = `<button ng-click="$ctrl.clickTest()">Click Test</button>`;
var component = {
controller: function() {
this.clickTest = clickTest;
function clickTest() {
alert("hello");
}
},
template: template
}
return component;
}
angular.module('demoApp', []).component('navbar', getComponent);
You need to add parentheses to getComponent passed as a parameter to the last line like so:
angular.module('demoApp', []).component('navbar', getComponent());
Using simply getComponent (without parentheses) passes a reference to the getComponent function to component() without executing it. However, angular is expecting an object containing your component configuration.
Thus, passing getComponent() calls the function and returns the component configuration object passing said configuration object to angular component() initializer rather than a reference to the function getComponent.
Related
I am using angular material $mdPanel, i can display it using the open() method but i can't remove it (using close button like the demo). The documentation is not clear about that, the close method doesn't work. Is there any solution for that ?
$mdPanel documentation
When you call $mdPanel.open(), it returns a promise. The call to the promise contains a reference to the created panel. You can call close() on that.
$res = $mdPanel.open(...);
$res.then(function(ref) {
$scope.ref = ref;
})
Later on, to close, call:
$scope.ref.close();
There is no close() method on $mdPanel it is instead a method on the panel reference, which is passed on the first argument to the controller for that panel. So to be able to close the panel you need to pass a controller function in your panel definition similar to below.
Hope this helps!
var config = {
...,
controller: PanelController,
controllerAs: 'panelCtrl',
template: '<div><div>Some content</div><button ng-click="panelCtrl.close()">Close</button></div>',
...
};
function PanelController(panelRef) {
this.close = function () {
panelRef && panelRef.close();
};
}
You can inject the mdPanelRef inside a controller and then call mdPanelRef.close()
var config = {
...,
controller: PanelController
};
function PanelController(mdPanelRef) {
this.close = function () {
mdPanelRef.close();
};
}
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');
}
});
Inside the controller I am trying to breakup my code into named functions for readability. However, in the parameterized named functions the scope and the injected dependency are all null. How do access these inside the named functions. Thanks for you help.
(
function() {
'use strict';
var moduleName = 'ufsrAppModule';
var controllerName = 'ufsrController';
var dependencyInjection = ['api', 'appHost', 'userAccount', 'userProfileFactory', 'fsrFactory', 'userFsrFactory', internalFunc];
angular.module(moduleName)
.controller(controllerName, dependencyInjection);
function internalFunc(api, appHost, userAccount, userProfileFactory, fsrFactory, userFsrFactory) {
var vm = this; //controller AS in ng-controller, do not use $scope
init(api, appHost, userAccount, userProfileFactory, fsrFactory, userFsrFactory, vm);
}
function init(api, appHost, userAccount, userProfileFactory, fsrFactory, userFsrFactory, vm) {
vm.facilityChanged = facilityChanged;
...
...
function facilityChanged(vm, fsrFactory) {
/*update UI then retrieve services*/
vm.postStatus = undefined;
vm.services = undefined;
vm.roles = undefined;
vm.services = fsrFactory.service().query({
/*parameters*/
FacilityID: vm.facility
})
.$promise.then(
function(data) {
vm.services = data;
});
}
}
})();
Strict DI can be done separately in this style
angular.module(moduleName)
.controller(controllerName, controllerFunction);
controllerFunction.$inject = ['$scope', '$http'];
function controllerFunction($scope, $http) {
...
}
This style is also recommended by John Papa's Angular style guide.
The facilityChanged is not working because its parameters are overwriting those that are passed into init
It can be fixed by changing
function facilityChanged(vm, fsrFactory) {
to
function facilityChanged() {
Edit: Attached jsbin
I strongly recommend putting the init function inside your controller function to save the parameter passing, just like the activate function in John Papa's guide.
Refined jsbin
I have an onChange event binding in my angular component, which works fine as long I give it a function of an other angular controller. However if I pass an ordinary js function nothing is called.
This is my angular template:
<search-input on-change='$ctrl.updateDetected(value)'></search-input>
And this is the the other controller:
import angular from 'angular';
import htmlTemplate from './mainWindow.html';
import searchInputComponent from './components/searchInput/searchInput';
const moduleName = 'woodstore';
var module = angular.module(moduleName, []);
class MainWindowController {
updateDetected(searchText) {
console.log('In controller: '+searchText);
}
}
module
.component('mainWindow', {
template: htmlTemplate,
controller: MainWindowController
})
.component('searchInput', searchInputComponent);
function updateDetectedFunction(value) {
console.log('In function: '+value);
}
This prints In controller: Text to search for on the console. If I change the template to
<search-input on-change='updateDetectedFunction(value)'></search-input>
nothing happens.
How can I use the updateDetectedFunction() for the call back?
I don't think plain functions could be called in template, angular html template always bind to a scope, any functions in template would be eval in the context of that scope, I think only thing you could do is calling the plain function within a scope function:
<search-input on-change='$ctrl.updateDetected(value)'></search-input>
class MainWindowController {
updateDetected(searchText) {
updateDetectedFunction(searchText)
}
}
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