Pass parameters to a component from a parent controller - angularjs

I am using Angularjs 1.7 and I am trying to pass an array from the parent controller to the component.How can I pass arraySteps and selectedSteps to the component.
I tried the following but didn't work. I am not getting the arraySteps in the component controller
ulMultiselect.component.js
angular.module('ulMultiselect.module').component('ulMultiselect', {
templateUrl: 'app/components/ulMultiselect/ulMultiselect.template.html',
controller: 'ulMultiselect.controller',
bindings: {
arraySteps: '<'
},
transclude: true
});
ulMultiselect.controller.js
angular.module('ulMultiselect.module').controller('ulMultiselect.controller',
function () {
var self = this;
self.$onInit = function () {
if (typeof self.arraySteps === 'undefined') {
self.arraySteps = [];
}
};
});
ulMultiselect.template.html
<div layout="row">
<md-input-container style="width:100%">
<label>Steps</label>
<md-select ng-model="$ctrl.selectedSteps" multiple>
<md-option ng-value="step.StepName" ng-repeat="step in $ctrl.arraySteps">{{step.StepName}}</md-option>
</md-select>
</md-input-container>
</div>
mainTest.html
<ul-Multiselect arraySteps="$ctrl.steps"></ul-Multiselect>
mainTest.controller.js
angular.module('view.mainTest.module').controller('view.mainTest.controller',
function () {
var self = this;
self.$onInit = function () {
self.steps = [{ StepName: "Step1" }, { StepName: "Step2" }, { StepName: "Step3" }];
};
});

The one-way binding needs to be normalized to kebab-case:
̶<̶u̶l̶-̶M̶u̶l̶t̶i̶s̶e̶l̶e̶c̶t̶ ̶a̶r̶r̶a̶y̶S̶t̶e̶p̶s̶=̶"̶$̶c̶t̶r̶l̶.̶s̶t̶e̶p̶s̶"̶>̶<̶/̶u̶l̶-̶M̶u̶l̶t̶i̶s̶e̶l̶e̶c̶t̶>̶
<ul-multiselect array-steps="$ctrl.steps"></ul-multiselect>
For more information, see
AngularJS Developer Guide - Directive Normalization

Related

Using AngularJS component props

I'm new to angularJS, and now I'm trying to realize some parts.
The questions is: how do I get access to callback onFinish() which is passed to component "my-timer" and run it? this.onFinish() returns the error.
Here is my markup:
<div ng-app="app" ng-controller="MyCtrl as myCtrl">
<div>
Status: {{myCtrl.status ? myCtrl.status : 'Waiting...'}}
</div>
<div>
<button ng-click="myCtrl.addTimer(5)">Add timer</button>
</div>
<div ng-repeat="timer in myCtrl.timers">
<div>
<h3>Timer {{timer.id}}</h3>
<button ng-click="myCtrl.removeTimer($index)">X</button>
<my-timer id="{{timer.id}}" start-seconds="{{timer.seconds}}" on-finish="myCtrl.onFinish(endTime)"></my-timer>
</div>
</div>
</div>
And here is index.js
var app = angular.module('app', []);
app.controller('MyCtrl', class {
constructor($scope) {
this.status = null;
this.timerId = 0;
this.timers = [];
this.addTimer(10);
this.addTimer(3);
console.log($scope);
}
addTimer(seconds) {
this.timers.push({
id: this.timerId++,
seconds
});
}
removeTimer(index) {
this.timers.splice(index, 1);
}
onFinish(endTime){
this.status = `Timer finished at ${endTime}`;
console.log(endTime);
}
});
app.component('myTimer', {
bindings: {
id: '#',
startSeconds: '#',
onFinish: '&',
},
controller: function($interval, $scope) {
this.endTime = null;
this.$onInit = function() {
this.countDown();
};
this.countDown = function() {
$interval(() => {
this.startSeconds = ((this.startSeconds - 0.1) > 0) ? (this.startSeconds - 0.1).toFixed(2) : 0;
}, 100);
};
},
template: `<span>{{$ctrl.startSeconds}}</span>`,
});
And here is jsFiddle
this.$onInit = function() {
this.countDown();
};
this.onFinish('1');
The problem here is that you tried to execute this.onFinish right in controller's body. And that wont work that way. If you want this function to be called during initialization, move it to $onInit
this.$onInit = function() {
this.countDown();
this.onFinish('1');
};
Otherwise, call it from another component method. You can only declare variables and component methods in controller body, but not call functions.

How can I access controller method on a modal instance controller on AngularJS?

Can you help me with this? I am trying to access the function on my parent/main controller from the template created on the modal instance but I couldn't access it directly. Can you help me with this?
Here's my code:
Here's the function for accessing and showing the modal:
openQuestionModalForm() {
let ctrl = this;
var modalInstance = ctrl.$modal.open({
templateUrl: 'questionModal.tmpl.html',
scope: this.$scope,
controller: ['$modalInstance', function($modalInstance) {
this.parentController = ctrl;
this.modalOptions = {
mode: 'add',
actionType: 'add'
}
console.log(this.Questionnaire_formController);
//this.modalOptions.data = angular.copy(data);
// this.modalOptions = {
// mode: mode,
// id: id,
// data: angular.copy(data),
// actionType: mode //set to mode temporarily should be ActionType
// }
this.close = $modalInstance.close;
}],
size: 'small',
controllerAs: '$ctrl',
windowClass: 'addeditcourseware-modal'
});
modalInstance.result.then(function(result) {
try {
console.log('*-----*', result);
} catch(e) {
console.log('Error!');
}
})
}
This is triggered by this function and I attached this event on a button
openAddEditCoursewareModal(mode, id, data, actionType) {
let ctrl = this;
if(ctrl.QuestionsStore.questionnaireType == 'Questionnaire') {
this.openQuestionModalForm(); //shows my modal based on the condition
....
<div class="row">
<div class="columns title-box move-right">
<p class="title">Questions</p>
<div class="button_link">
<button class="button-primary button-default" ng-click="$ctrl.openAddEditCoursewareModal('add')">Add Question</button>
</div>
</div>
</div>
Now here's the part where I am calling the function on my parent/main controller.
<label class="stacked">
Question Type:
<select name="question_type" class="form-field" id="questionType" ng-model="$ctrl.questionModal.data.attributes.question_type" ng-change="$ctrl.getSelectedQuestionType()" ng-required="true">
<option value="single">Single</option>
<option value="multiple">Multiple</option>
<option value="true_or_false">True or False</option>
<option value="free_text">Free Text</option>
</select>
</label>
I am calling the function getSelectedQuestionType on the onchange event. But the result on the console is undefined.
Can you help me with this?
You could pass the function reference through a resolve block and then access it in your modal controller, as such:
modalInstance = ctrl.$modal.open({
...
resolve: {
'getSelectedQuestionType': function () {
return ctrl.getSelectedQuestionType;
}
},
controller: ['$modalInstance', 'getSelectedQuestionType', function($modalInstance, getSelectedQuestionType) {
// Bind to controller so that you can call it via $ctrl.getSelectedQuestionType in your view
this.getSelectedQuestionType = getSelectedQuestionType;
}
...
});
Hope this helps :)

Angular. Pass data from component to parent controller

Hot to recive data from component in parent controller.
I have this code:
index.html
<div ng-controller="formController">
<phones-list phone="phone"></phones-list>
<button ng-click="saveForm()">Save</button>
</div>
form.controller.js
var app = angular.module('myApp');
app.controller('formController', ['$scope', function($scope) {
$scope.saveForm = function() {
console.log($scope.phone)
}
}]);
phoneList.component.js
var app = angular.module('myApp');
app.component('phonesList', {
templateUrl: '/scripts/components/phones/phonesList.template.html',
controller: 'phonesListController',
bindings: {
phone: '='
}
});
phoneList.template.html
<select name="" id="" ng-change="$ctrl.change()" ng-model="$ctrl.phone">
<option ng-repeat="phone in $ctrl.phones">{{ phone.name }}</option>
</select>
phoneList.controller.js
var app = angular.module('myApp');
app.controller('phonesListController', function() {
this.phones = [
{
name: 'ABC',
number: '723-543-122'
},
{
name: 'ABCDE',
number: '324-531-423'
}
];
this.change = function() {
console.log(this.phone)
}
});
So I have select list with phones. What I want is to get phone object in formController after select and submit form. For now I get only text value from .
Add an additional binding to your phoneList component with a function to call when the selection changes.
phoneList.component.js
var app = angular.module('myApp');
app.component('phonesList', {
templateUrl: '/scripts/components/phones/phonesList.template.html',
controller: 'phonesListController',
bindings: {
phone: '=',
onChange: '&' //This will allow you to bind a function to your
} //component that you can execute when something
}); //happens
phoneList.controller.js
var app = angular.module('myApp');
app.controller('phonesListController', function() {
this.phones = [
{
name: 'ABC',
number: '723-543-122'
},
{
name: 'ABCDE',
number: '324-531-423'
}
];
this.change = function() {
console.log(this.phone);
this.onChange({phone: phone}); //call our new callback and give it
} //our update
});
index.html
<div ng-controller="formController">
<phones-list phone="phone"
on-change="phoneSelected(phone)">
<!--Tell our component which function we wish to bind to-->
</phones-list>
<button ng-click="saveForm()">Save</button>
</div>
form.controller.js
var app = angular.module('myApp');
app.controller('formController', ['$scope', function($scope) {
$scope.saveForm = function() {
console.log($scope.phone)
}
$scope.phoneSelected(phone){
//Do your stuff here!
}
}]);
I hope that helps!
I found the solution. I changed component template and add ng-options directive to . I don't know why it is more diffucult to do the same in standard list.
index.html
<div ng-controller="ProposalController">
<phones-list phone="phone"></phones-list>
<button ng-click="saveForm()">Zapisz</button>
</div>
phoneList.component.js
var app = angular.module('myApp');
app.component('phonesList', {
templateUrl: '/components/phones/templates/phoneList.template.html',
controller: 'PhoneListController',
bindings: {
phone: '='
}
});
phoneList.controller.js
....
this.change = function() {
this.phone = this.selectedPhone;
}
....
phoneList.template.html
<select ng-model="$ctrl.selectedPhone" ng-change="$ctrl.change()" ng-options="phone.name for phone in $ctrl.phones"></select>
form.controller.js
$scope.saveForm = function(){
console.log($scope.phone)
}

AngularJS UI Modal and select doesn't update the scope values

I am having a lot of trouble trying to save values from the modal component available in Angular UI.
Here is the page controller that calls the modal dialog
$scope.sourceSchema = [];
$scope.targetSchema = [];
$scope.apiDefinition = [];
$scope.availableSchemas = availableSchemas.get();
$scope.addComponent = function (type) {
$scope.$broadcast('addComponent', [type]);
var templateUrl = "";
var controller = null;
var resolve = null;
var componentSchema = [];
switch (type) {
case "sourceSchema":
templateUrl = 'source-schema.tpl.html';
controller = 'SourceCtrl';
componentSchema = $scope.sourceSchema;
break;
case "targetSchema":
templateUrl = 'target-schema.tpl.html';
controller = 'TargetCtrl';
componentSchema = $scope.targetSchema;
break;
case "api":
templateUrl = 'api.tpl.html';
controller = 'SourceCtrl';
componentSchema = $scope.apiDefinition;
break;
}
var modalInstance = $modal.open({
templateUrl: templateUrl,
controller: controller,
resolve: {
existingSchemas: function () {
return $scope.availableSchemas;
}
}
});
modalInstance.result.then(function (selectedItem) {
componentSchema.push(selectedItem);
}, function () {
// $log.info('Modal dismissed at: ' + new Date());
});
};
Here is the SourceCtrl that controls one of the modal dialogs I am using:
.controller("SourceCtrl", function ($scope, $modalInstance, existingSchemas) {
$scope.existingSchemas = existingSchemas;
$scope.sourceSchema = "";
$scope.ok = function () {
$modalInstance.close($scope.sourceSchema);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.$watch('sourceSchema', function(newValue, oldValue) {
console.log(newValue, oldValue);
})
})
And finally here is the template for this controller (SourceCtrl).
<div class="modal-header">
<h3>New Source Schema</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-3">
<label for="schema-source">Source</label>
</div>
<div class="col-xs-9">
<select name="sourceSchema" ng-model="sourceSchema" ng-options="s as s.name for s in existingSchemas">
<option value="">-- choose source --</option>
</select>
</div>
<h5>Name: {{sourceSchema.name}}</h5>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
The funny thing is that when I change the value in the select, the {{sourceSchema.name}} line does show the correct name of the schema, however the changes do not get reflected in the controller and the actual value is not being passed on. I have used a watch to detect when something gets changed and apparently it doesn't. But the value does get changed otherwise why would it get displayed when I select it in the dropdown list.
Make sure that you've got a dot in your ngModel expression - that is - that you are binding to an object property and not directly to the scope. Something like:
.controller("SourceCtrl", function ($scope, $modalInstance, existingSchemas) {
$scope.existingSchemas = existingSchemas;
$scope.source = {
schema: ''
};
$scope.ok = function () {
$modalInstance.close($scope.source.schema);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.$watch('source.schema', function(newValue, oldValue) {
console.log(newValue, oldValue);
})
})
And then, in your markup:
<select name="sourceSchema" ng-model="source.schema" ng-options="s as s.name for s in existingSchemas">
<option value="">-- choose source --</option>
</select>
If you can provide a plunker I can help you fixing the code.

The "with" binding of KnockoutJS in AngularJS?

I have just switched from KnockoutJS to AngularJS and I am not able to find the KnockoutJS's "with" data-bind in AngularJS.
Here is the piece of code in KnockoutJS. The "with" binding creates a new binding context, so that descendant elements are bound in the context of a specified object.
<h1 data-bind="text: city"> </h1>
<p data-bind="with: coords">
Latitude: <span data-bind="text: latitude"> </span>,
Longitude: <span data-bind="text: longitude"> </span>
</p>
<script type="text/javascript">
ko.applyBindings({
city: "London",
coords: {
latitude: 51.5001524,
longitude: -0.1262362
}
});
</script>
Does AngularJS have anything like context?
Nothing like with that I know of.. this is the best I could do:
<h1>{{city}}</h1>
<p ng-repeat="c in [coords.or.possibly.deeper.in.tree]">
Latitude: {{c.latitude}},
Longitude: {{c.longitude}}
</p>
Create a custom directive that loops through the source object and creates corresponding properties on the directive's scope that are getter/setter references to the source object.
Check out this plunker.
directive module:
angular.module('koWith', [])
.directive('koWith', function () {
return {
controller: function ($scope, $attrs) {
var withObj = $scope.$parent[$attrs.ngWith];
function getter(prop) {
return this[prop];
}
function setter(val, prop) {
this[prop] = val;
}
for (var prop in withObj) {
if (withObj.hasOwnProperty(prop)) {
Object.defineProperty($scope, prop, {
enumerable: true,
configurable: true,
get: getter.bind(withObj, prop),
set: setter.bind(withObj, prop)
});
}
}
},
restrict: 'A',
scope: true
};
});
app module:
angular.module('myApp', [])
.controller('myController', function ($scope) {
$scope.customer = {
name: "Timmeh",
address: {
address1: "12 S Street",
address2: "",
city: "South Park",
state: "CO",
zipCode: "80440"
}
};
});
html:
<div ko-with="customer">
<h2>{{name}}</h2>
<div ko-with="address">
{{address1}}<br>
{{address2}}<br>
{{city}}, {{state}} {{zipCode}}
</div>
</div>
Explanation
In KnockoutJS, bindings keep the bindingContext and data separated so creating the with binding is trivial since it only needs to create a new child bindingContext from the current one and use the with object as its data value.
In AngularJS, a directive's scope is basically the bindingContext and data object rolled into one. When a new scope is created, in order to get the with-like behavior, the properties of the with object have to be referenced onto the newly created scope object.
Here is solution based on #nwayve, but it supports expressions in koWith and also it watches for updating property/expression specified in koWith:
.directive('koWith', function () {
return {
restrict: 'A',
scope: true,
controller: function ($scope, $attrs, $parse) {
var ScopePropertyDesc = function (prop) {
var self = this;
self.propName = prop;
self.parsed = $parse(prop);
self.enumerable = true;
self.configurable = true;
//self.writable = true;
self.get = function () {
var withObj = $scope.$parent[$attrs.koWith];
var res = self.parsed($scope.$parent, withObj);
return res;
};
self.set = function (newValue) {
var withObj = $scope.$parent[$attrs.koWith];
self.parsed.assign(withObj, newValue);
};
};
$scope.$parent.$watch($attrs.koWith, function (oldVal, newVal) {
var withObj = $scope.$parent[$attrs.koWith];
(function copyPropertiesToScope(withObj) {
for (var prop in withObj) {
if (withObj.hasOwnProperty(prop)) {
Object.defineProperty($scope, prop, new ScopePropertyDesc(prop));
}
};
})(withObj);
});
}
};
});

Resources