I have two types of controllers:
The first type of controller only appears once on an html page ("SingleChildController").
The second type of controller appears twice on the same page ("MultipleChildController") and is differentiated by the value of its property "instance".
Both of these are nested within my ParentController (see code below).
How can I access data from any of my 3 child controller instances in the ParentController?
I have tried adding two services, one for the SingleChildController and one for the MultipleChildController, but it seems like by doing so I am duplicating a lot of code. This is especially true in my actual code where each controller has many properties to monitor. Is there a better way to accomplish this?
EDIT: For further clarification, this view is for a form and I want to get all of my data back up into the ParentController so that I can submit my form.
(function() {
var app = angular.module('myApp', []);
app.controller('ParentController', ['$scope',
function($scope) {
var parent = this;
parent.singleName = "";
parent.multiple1Name = "";
parent.multiple2Name = "";
}
]);
app.controller('SingleChildController', ['$scope',
function($scope) {
var single = this;
single.name = "";
}
]);
app.controller('MultipleChildController', ['$scope',
function($scope) {
var multiple = this;
multiple.instance = "";
multiple.name = "";
}
]);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ParentController as parent">
<!-- I would like these to show the data that was inputted below -->
ParentSingleName: {{parent.singleName}} <br />
ParentMultiple1Name: {{parent.multiple1Name}} <br />
ParentMultiple2Name: {{parent.multiple2Name}} <br /><br />
<div ng-controller="SingleChildController as single">
<input type="text" ng-model="single.name" />: {{single.name}}
</div>
<div ng-controller="MultipleChildController as multiple1" ng-init="multiple1.instance='first'">
<input type="text" ng-model="multiple1.name" />: {{multiple1.instance}} - {{multiple1.name}}
</div>
<div ng-controller="MultipleChildController as multiple2" ng-init="multiple2.instance='second'">
<input type="text" ng-model="multiple2.name" />: {{multiple2.instance}} - {{multiple2.name}}
</div>
</div>
Related
Say I have several forms each with their own controller that look like this
<div ng-controller="MyController1 as ctrl">
<label>Some label </label>
</div>
<div ng-controller="MyController2 as ctrl">
<label>Some label </label>
</div>
And I have a global controller, that gets the information about the form names. Now I want to find the controllers for each form. For instance, if in my global controller function, I get the name of the first form, how can I find out that its controller is MyController1? Is that even possible?
Calling a controller from another controller is possible. But, I believe the problem you're trying to solve is somewhere else: the architecture of your app.
Ideally, your app should 'react' to changes on the state. The state should be kept in a single place (also called 'single source of truth'), ie. a service. Then you share that service state with as many controllers as you need.
You can either update the service state directly from the controller, or by calling a method on the service itself.
Look at the example below. I hope that sheds some light.
Cheers!
angular.module('app', [])
.service('MyService', function(){
var self = this;
self.state = {
name: 'John'
};
self.changeNameFromService = function() {
self.state.name = 'Peter';
}
})
.controller('Ctrl1', function($scope, MyService){
$scope.state = MyService.state;
$scope.changeName = function(){
// update the state of the scope, which is shared with other controllers by storing the state in a service
$scope.state.name = 'Mary';
}
})
.controller('Ctrl2', function($scope, MyService){
$scope.state = MyService.state;
// call a method defined in service
$scope.changeName = MyService.changeNameFromService;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="Ctrl1">
Ctrl1: {{state.name}}
<button ng-click="changeName()">Change name!</button>
</div>
<div ng-controller="Ctrl2">
Ctrl2: {{state.name}}
<button ng-click="changeName()">Change name from service!</button>
</div>
</div>
I have a very hierarchical data structure in JSON. I also have a re-usable (Django) template that gets bound to part of that structure. So, I need to know where in that structure I am for any given template. I am almost there:
http://jsfiddle.net/trubliphone/fd64rn3y/
The missing bit is being able to pass the current ng-model to a controller.
Here is some code (but the jsfiddle above shows more detail):
my_app.js:
var myApp = angular.module('myApp', []);
myApp.factory('$global_services', ['$http', function($http) {
var data = {};
$http.get("some_url", {format: "json"}) {
/* THIS REQUEST RETURNS A BIG CHUNK OF HIERARCHICAL JSON */
.success(function (data) {
data = data;
})
});
return {
getPathFromModel: a_clever_fn_i_wrote_that_returns_a_string(),
getModelFromPath: a_clever_fn_i_wrote_that_returns_a_model()
}
}]);
myApp.controller('MyController', ['$scope', '$attrs', '$global_services', function( $scope, $attrs, $global_services ) {
if ($attrs.currentModelPath) {
/* if you passed current_model_path, get the current_model too */
$scope.current_model_path = $attrs.currentModelPath;
$scope.current_model = $global_services.getModelFromPath($scope.current_model_path);
}
else if ($attrs.currentModel) {
/* if you passed current_model, get the current_model_path too */
$scope.current_model = $attrs.currentModel;
$scope.current_model_path = $global_services.getPathFromModel($scope.current_model);
}
}]);
my_template.html:
<div ng-app="myApp">
<div ng-controller="MyController" current_model_path="data">
{{current_model.name}}:
<ul ng-repeat="child in current_model.children">
<input type="text" ng-model="child.name"/> {{ child.name }}
<!-- LOOK: HERE IS A NESTED CONTROLLER -->
<div ng-controller="MyController" current_model="child">
{{current_model.name}}:
<ul ng-repeat="child in current_model.children">
<input type="text" ng-model="child.name"/> {{ child.name }}
</ul>
</div>
</ul>
</div>
</div>
The problem is in the "div" element for the nested controller; I pass the {{child}} ng variable as an attribute, but when the controller recieves it, it just interprets it as the JavaScript string "child". How can I pass the actual model object?
Thanks.
<div ng-controller="MyController" ng-init="data = child">
this will add an object to the inner scope named data.
I have a form field (input text) with an ng-if being false at the begining. At some point the ng-if value become true.
When this happen, I want to execute some javascript which manipulate the DOM. To keep it simple, let's say that I need to select the input value and focus the field.
<input type="text" ng-value="foo" ng-if="show" onshow="doSomething()"/>
<button ng-click="toggle()"></button>
The JavaScript
ctrl.foo = "bar";
ctrl.show = false;
ctrl.toggle = function(){
ctrl.show = !ctrl.show;
}
I know that it looks like a "non-angular approach", but here I think the action is not model related.
Since the ng-if directive execute the template each time show become true, you can use ng-init for that. See the following snippet and replace alert('test); by anything you want.
angular.module('test', []).controller('test', function($scope, $element) {
$scope.show = false;
$scope.toggle = function() {
$scope.show = !$scope.show;
};
$scope.init = function() {
alert($element.find('input').val());
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="test">
<input type="text" value="foo" ng-if="show" ng-init="init()" />
<button ng-click="toggle()">Toggle</button>
</div>
</div>
Using Angularjs, I want to create say 10 <div> if user inputs '10' in a text box.
<input type="text" id="gcols" placeholder="Number of Columns" ng-model="cols"/>
So whatever value user enters in this box, those many shall be created. So my generalized question is, how to 'ng-repeat' a <div> for 'ng-model' times?
UPDATE:
Appreciating for all of your answers, I did something like following, by referring your answers. And that is working as of now, but tell me if any other logic is more efficient than this.
$scope.divs = new Array();
$scope.create=function(){ //I added a button & invoked this function on its ng-click
var a = $scope.cols;
for(i=0;i<a;i++)
{
$scope.divs.push(a);
}
alert($scope.divs);
};
You need to create an array to iterate on it.
In your controller:
app.controller('ctrl', function ($scope) {
$scope.cols = 0;
$scope.arr = [];
$scope.makeArray = function () {
$scope.arr.length = 0;
for (var i = 0; i < parseInt($scope.cols) ; i++) {
$scope.arr.push(i);
}
}
});
In your view:
<div ng-controller="ctrl">
<input ng-model="cols" type="text" ng-change="makeArray()" />
<div ng-repeat="o in arr">hi</div>
</div>
JSFiddle
DEMO: http://jsfiddle.net/JsFUW/
In your module/app/controller
$scope.cols =4; /* a default */
$scope.divs =[]; /* whatever these are */
In the template
<input type="text" id="gcols" placeholder="Number of Columns" ng-model="cols"/>
<div ng-repeat="div in divs | limitTo:cols" >
...
</div>
As comment question , try this :
In the controller
$scope.cols
In the template
<input type="text" id="gcols" placeholder="Number of Columns" ng-model="cols"/>
<h3>cols: {{cols}} </h3>
Do you see that value change on the page as you type ? If not your $scope.cols is not in the template scope.
In your view:
<input ng-model="cols" ng-change="colsChanged()"/>
<div ng-repeat="item in items track by $index">{{$index}} {{item}}</div>
In your controller:
app.controller('MyCtrl', function($scope) {
$scope.cols = 0;
$scope.colsChanged = function () {
$scope.items = new Array(+$scope.cols);
};
//optional: if you're starting with 1 or more "cols"
$scope.colsChanged();
});
Alternatively, you could roll your own directive that used transclusion to repeat itself multiple times, but that could get involved and probably isn't going to gain you much.
I'm trying to generate a set of check-boxes from an object array. I'm aiming to have the check-boxes dynamically map their ng-model to a property of the new object that will be submitted into the array.
What I had in mind is something like
<li ng-repeat="item in items">
<label>{{item.name}}</label>
<input type="checkbox" ng-model="newObject.{{item.name}}">
</li>
This doesn't work as can be seen on this JSFiddle:
http://jsfiddle.net/GreenGeorge/NKjXB/2/
Can anybody help?
This should give you desired results:
<input type="checkbox" ng-model="newObject[item.name]">
Here is a working plunk: http://plnkr.co/edit/ALHQtkjiUDzZVtTfLIOR?p=preview
EDIT
As correctly noted in the comments using this with ng-change requires a "dummy" ng-model to be present beforehand. It should however be noted that apparently with 1.3 the required options have been provided by the framework. Please check out https://stackoverflow.com/a/28365515/3497830 below!
/EDIT
Just in case you are like me stumbling over a simple case while having a more complex task, this is the solution I came up with for dynamically binding arbitrary expressions to ng-model: http://plnkr.co/edit/ccdJTm0zBnqjntEQfAfx?p=preview
Method: I created a directive dynamicModel that takes a standard angular expression, evaluates it and links the result to the scope via ng-model and $compile.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.testvalue = 'data.foo';
$scope.eval = $scope.$eval;
});
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.testvalue = 'data.foo';
$scope.eval = $scope.$eval;
});
app.directive('dynamicModel', ['$compile', function ($compile) {
return {
'link': function(scope, element, attrs) {
scope.$watch(attrs.dynamicModel, function(dynamicModel) {
if (attrs.ngModel == dynamicModel || !dynamicModel) return;
element.attr('ng-model', dynamicModel);
if (dynamicModel == '') {
element.removeAttr('ng-model');
}
// Unbind all previous event handlers, this is
// necessary to remove previously linked models.
element.unbind();
$compile(element)(scope);
});
}
};
}]);
Usage is simply dynamic-model="angularExpression" where angularExpression results in a string that is used as the expression for ng-model.
I hope this saves someone the headache of having to come up with this solution.
Regards,
Justus
With Angular 1.3, you can use ng-model-options directive to dynamically assign the model, or bind to an expression.
Here is a plunkr: http://plnkr.co/edit/65EBiySUc1iWCWG6Ov98?p=preview
<input type="text" ng-model="name"><br>
<input type="text" ng-model="user.name"
ng-model-options="{ getterSetter: true }">
More info on ngModelOptions here: https://docs.angularjs.org/api/ng/directive/ngModelOptions
This is my approach to support deeper expression, e.g. 'model.level1.level2.value'
<input class="form-control" ng-model="Utility.safePath(model, item.modelPath).value">
where item.modelPath = 'level1.level2' and
Utility(model, 'level1.level2') is the utility function that returns model.level1.level2
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<form name="priceForm" ng-submit="submitPriceForm()">
<div ng-repeat="x in [].constructor(9) track by $index">
<label>
Person {{$index+1}} <span class="warning-text">*</span>
</label>
<input type="number" class="form-control" name="person{{$index+1}}" ng-model="price['person'+($index+1)]" />
</div>
<button>Save</button>
</form>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope) {
$scope.price = [];
$scope.submitPriceForm = function () {
//objects be like $scope.price=[{person1:value},{person2:value}....]
console.log($scope.price);
}
});
</script>
</body>
</html>