Two way binding angular component with parent controller - javascript

I have an angular component (vendor-filters) that I would like to pass data to and from a parent controller. The purpose is to create a filter off of mainInfo, and pass that data back to the parent controller where it will reflect the filtering in the component. My problem is that this mainInfo variable is returning undefined in the component controller. Here's my code :
html
<div ng-controller="kanban as ctrl">
<vendor-filters mainInfo="ctrl.mainInfo"></vendor-filters>
<div class="qt-kb">
<kanban-list label="Incoming" type="incoming" cards="ctrl.mainInfo.incoming"></kanban-list>
<kanban-list label="In Progress" type="progress" cards="ctrl.mainInfo.inProgress"></kanban-list>
<kanban-list label="Waiting For Documentation" type="docs" cards="ctrl.mainInfo.documentation"></kanban-list>
<kanban-list label="Pending Approval" type="closed" cards="ctrl.mainInfo.closed"></kanban-list>
</div>
Parent Controller :
app.controller("kanban", ["$scope", "assignmentDataService", "globalSpinner", function ($scope, assignmentDataService, globalSpinner) {
const vm = this;
vm.mainInfo = [];
activate();
function activate() {
getData();
}
function getData() {
var promise = assignmentDataService.getData()
.then(function(data) {
vm.mainInfo = data;
});
globalSpinner.register(promise);
};
}]);
Component controller:
class VendorFilterCtrl {
constructor($http, $scope, $timeout,assignmentDataService) {
this.$scope = $scope
this.$http = $http;
const vm = this;
//I could be initializing this wrong but this is where I'm trying to get
//the data.
vm.data = vm.mainInfo;
}
app.controller('kanban').component("vendorFilters", {
templateUrl: "app/components/vendorFilters.html",
bindings: {
store: "=?store",
onChange: '&',
mainInfo: '<'
},
controller: VendorFilterCtrl,
controllerAs: "ctrl",
bindToController: true
});
Basically I'm trying to get the mainInfo from the parent controller into the component and visa versa. Any idea why this isn't working?

Start by using kebab-case for the attribute:
<vendor-filters ̶m̶a̶i̶n̶I̶n̶f̶o̶ main-info="ctrl.mainInfo"></vendor-filters>
NEXT
Fix this:
app ̶.̶c̶o̶n̶t̶r̶o̶l̶l̶e̶r̶(̶'̶k̶a̶n̶b̶a̶n̶'̶)̶ .component("vendorFilters", {

Related

Angular.js callback from another controller

In my angular project I'm using Angular.js material. And I want to show $mdialog with custom controller, where user changes some data and this data should be applied to my $scope variable. Example what I do now:
function myControllerFn($scope, MyService){
// I do copy of my service variable because I don't want to change it until user will click save button
$scope.name = angular.copy(MyService.name);
$scope.editCurrentProfile = function() {
$scope.showEditProfileDialog($scope.name).then(function(name){
$scope.name = name;
}
}
$scope.showEditProfileDialog = function(name) {
var deferred = $q.defer();
$mdDialog.show({
controller: 'editProfileViewCtrl',
templateUrl: 'controllers/editProfileDialog.tmpl.html',
locals: {
name: name,
deferred: deferred
}
});
return deferred.promise;
};
}
Then in dialog controller I do:
function editProfileViewCtrl($scope, name, deffered) {
deferred.resolve('newName');
}
But I think it is the wrong way. So what is the best way to communicate between two view controllers in angular without new service ? Or better create another service like: EditDialogService, where I will save results ?
When you open a modal, the show() function returns a promise.
$scope.showEditProfileDialog = function(name) {
var modalInstance = $mdDialog.show({
controller: 'editProfileViewCtrl',
templateUrl: 'controllers/editProfileDialog.tmpl.html',
locals: {
name: name
}
});
modalInstance.then(function(result){
// acces what is returned
// In your case, you would do
$scope.name = result;
}, function(error){
// Usually when you cancel your modal
});
}
Your modal controller can be injected with $mdDialog.
function editProfileViewCtrl($scope, name, $mdDialog) {
$scope.close = function() {
$mdDialog.hide('newName');
}
}
You should create a directive with your user as scope variable. Angular in itself is handling the data binding.
It is possible to create a minimal controller function that has access to $scope.
$mdDialog.show({
controller: function () { this.parent = $scope; },
templateUrl: 'controllers/editProfileDialog.tmpl.html',
locals: {
name: name,
deferred: deferred
}
});

AngularJS ES6 parent function not being called by child component

I'm a bit new to the ES6 syntax/structure of Angular 1.x, and I'm running into an issue with passing a function from a parent controller to a child controller.
This is how the app is tied together (I use webpack + babel with this as the entry-point):
const requires = [
'ngRoute',
];
//App
angular.module('kanban',requires)
.controller('dashboardCtrl', dashboardCtrl)
.component('board', board)
.component('task', task)
.config(routes);
In my routes, I have a single route, which is my 'parent'
export default function($routeProvider) {
$routeProvider
.when('/', {
template: dashboardTemplate,
controller: 'dashboardCtrl',
controllerAs: '$ctrl',
});
}
Who's controller looks like this:
export default function($rootScope) {
$rootScope.title = 'Kanban';
let _this = this;
this.boards = [
{
_id: 'b1',
title: 'backlog',
tasks: ['t1', 't2'],
}
];
this.deleteBoard = function(board) {
console.log(board);
let index = _this.boards.indexOf(board);
if (index !== -1) {
_this.boards.splice(index, 1);
}
};
And in the template, the child is created with ng-repeat, passing in the function
<board ng-repeat="board in $ctrl.boards" board="board" onDelete="$ctrl.deleteBoard(board)" ></board>
And the board binds the attribute as a function with an &
export const board = {
template: boardTemplate,
controller: boardCtrl,
bindings: {
board: '=',
onDelete: '&',
}
};
And the function is added to the controller within a different function:
export default function boardCtrl() {
let _this = this;
this.deleteBoard = function(){
console.log(_this.onDelete);
_this.onDelete({board: _this.board});
};
}
And called with a click:
<button ng-click="$ctrl.deleteBoard()"></button>
I can reach the board (child) controller's function, which prints this in the console:
function (locals) {
return parentGet(scope, locals);
}
And returns no errors, but the console.log in the parent deleteBoard function does not get called.
What is happening here? Why does the child seem to recognize that it is calling something in the parent scope, but is not reaching it?
Turns out this issue was because of how the attribute was named in the parent template, where
<board onDelete="$ctrl.deleteBoard(board)"></board>
needed to be the following instead:
<board on-delete="$ctrl.deleteBoard(board)"></board>
even though the attribute is bound as "onDelete" on the child controller.

Angular Directive to component angular 1.5

I have this directive, that i would like to make a component
angular.module('app')
.directive('year', function () {
var controller = ['$scope', function ($scope) {
$scope.setYear = function (val) {
$scope.selectedyear = val;
}
}];
return {
restrict: 'E',
controller: controller,
templateUrl: "views/year.html"
};
});
This is what I got so far:
angular.module('app')
.component('year', {
restrict: 'E',
controller: controller,
templateUrl: "views/year.html"
});
I am not sure how to move my var controller into .component
There are few things you should do convert your directive to component
There is no restrict property for component as it is restricted to elements only.
For controller you could just set as you did at directive declaration but outside of it.
Controllers for components use controllerAs syntax as default so get rid of $scope
So your component should look like this...
angular.module('app')
.component('year', {
controller: ComponentController,
templateUrl: "views/year.html"
});
function ComponentController(){
var $ctrl = this;
$ctrl.setYear = function (val) {
$ctrl.selectedyear = val;
}
}
Your component should look like this:
function YearController() {
var $ctrl = this;
$ctrl.setYear = function (val) {
$ctrl.selectedyear = val;
}
}
angular.module('app').component('year', {
templateUrl: 'views/year.html',
controller: YearController
});
For more details, please read the following Stack Overflow question for more deatils:
Angular 1.5 component vs. old directive - where is a link function?
and the official documentation:
https://docs.angularjs.org/guide/component
Also, please note that components are restricted to elements only by default.

Using AngularJS with a factory and a Controller

I'm trying to do the following:
When a user accesses "localhost/people/:id", the information about the respective person is taken from a MongoDB and displayed via Angular.
I have my api, which works perfectly fine, I've double-checked.
I'm using the latest AngularJS (1.4.9) and the new Router (angular-new-router or ngNewRouter).
I have an Angular module:
var personModule = angular.module('app.personDetailed', []);
a factory:
personModule.factory('personService', ['$http', function($http) {
return {
get : function(id) {
return $http.get('/api/people/' + id);
}
}
}]);
and a controller:
personModule.controller('PersonDetailedController', ['$routeParams', '$scope', 'personService', PersonDetailedController]);
function PersonDetailedController($routeParams, $scope, personService) {
var id = $routeParams.id;
personService.get(id).then(function(res) {
$scope.item = res.data;
});
}
This all should be displayed in this view:
<div data-ng-controller="PersonDetailedController">
<h2>{{ item }}</h2>
</div>
(yes, I'm not bothering trying to parse json yet).
The problem is, I am unable to use both $scope and $routeParams at the same time. I can only have one or the other. If I use both, the $scope works fine, but the $routeParams is empty.
Here's the main controller, just in case:
var appModule = angular.module('app', ['app.main', 'app.personDetailed', 'ngNewRouter', 'ngResource']);
appModule.controller('AppController', ['$router', AppController]);
function AppController($router) {
$router.config([
{ path: '/', component: 'main'}
{ path: '/people/:id', component: 'personDetailed'}
]);
}
Seems the new router does away with $scope and binds to the controller instance in the template instead.
Looks like you should use this instead
personModule.controller('PersonDetailedController', ['$routeParams', 'personService', PersonDetailedController]);
function PersonDetailedController($routeParams, personService) {
var personDetailed = this,
id = $routeParams.id;
personService.get(id).then(function(res) {
personDetailed.item = res.data;
});
}
and your view (do not use ng-controller)
<h2>{{ personDetailed.item }}</h2>

Is there a way to pass variables to a controller from ui.router?

I have a page structured with some nested views, using ui.router and I would like to pass some data from the parent controller to the child controller, without injecting useless services into the child controller.
In my mind, something like these would be perfect
state('home', {
url: "/home",
templateUrl: "parts/home.html",
controller: "FatherController"
}).
state('home.child', {
url: "/child",
templateUrl: "parts/home/child.html",
controller: "ChildController",
params: {$scope.data = $rootScope.someData}
})
Do you happen to know if there is a way to do this?
If your child view is nested within the parent view, your child controller will automatically inherit the parent scope.
You should be able to access the parent controller's data directly from the child controller.
Well, I guess you don't always have the choice to move the data to a parent controller or such.
My recommendation for this would be to use resolvers (https://github.com/angular-ui/ui-router/wiki#resolve) to do some magic.
Here's a sample on how it could be made to work:
var dataResolver = ['$scope', '$stateParams', 'Service',
function($scope, $stateParams, Service) {
Service.get($stateParams.objectId).then( function(obj) {
$scope.myObject = obj;
return obj;
});
};
];
$stateProvider.state("foo.details", {
"url": '/foo/:objectId',
"resolve": { "data": dataResolver },
"controller": "SomeController",
"template": "<ui-view />"
)
And you magically get the $scope.obj data when the controller is instanciated, whatever how.
You can use Query Parameters and access using $stateParams
https://github.com/angular-ui/ui-router/wiki/URL-Routing
Well, in my projects I use resolve of Angular UI router.
Basically, when initializing the parent state, It will retrieve data from the server and store it into listItem. You also can separate the logic of making request to server using a service and inject it into config.
Suppose I click somewhere in the parent state to open the new child state with an Id as a query string. Then we get this id by $stateParams and filter to find the correct item in listItem (using Underscore)
route.js
.state('parent', {
url: '/parent',
templateUrl: 'parent-template.html',
controller: 'ParentController',
resolve: {
listItem: ['$http', '$stateParams', function ($http, $stateParams) {
return $http.get({'/GetListItem'}).then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
return [];
});
}]
}
})
.state('parent.child', {
url: '/{itemId}',
templateUrl: 'child-template.html',
controller: 'ChildController',
resolve: {
item: ['$stateParams', 'listItem', function ($stateParams, bundles) {
return _.findWhere(listItem, { Id: $stateParams.itemId });
}]
}
})
Then you can access to listItem and item in the controller like below.
parent.controller.js
(function () {
function ParentController($scope, listItem) {
}
ParentController.$inject = ['$scope', 'listItem']
angular.module('app').controller('parentController', ParentController)
})()
child.controller.js
(function () {
function ChildController($scope, item) {
}
ChildController.$inject = ['$scope', 'item']
angular.module('app').controller('childController', ChildController)
})()

Categories