Share $scope between DOM elements with same ng-controller - angularjs

I'm stuck with something (I've been reading similar questions but none of them get me to the solution).
I have 2 DOM elements (let's say 2 div's) with different id's but the same ng-controller (this is for basic example, in my real app I have 2 differente pages but works the same).
<div id="layer1" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value" ng-change="changeType(selectedType.value)"></select>
</div>
<div id="layer2" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value" ng-change="changeType(selectedType.value)"></select>
</div>
And in JS
var myAppModule = angular.module('myApp', [])
.factory('selectedType', function(){
return{}
})
.controller('appCtrl', ['$scope',function($scope){
$scope.ptype = [
{
value: 1,
label:'Kg'
},
{
value: 2,
label:'Pza'
}];
selectedType = $scope.ptype[0];
$scope.changeType = function(value){
if(value==1){selectedType = $scope.ptype[0];}
else{selectedType=$scope.ptype[1];}
};
}])
As you can see I have the options for the SELECT and the ng-model, what I need is when change the selected value in any SELECT (doesn't matter which DIV) the other gets updated too.
Here a Plunker with code SEE HERE.
Thanks!

Using a Shared service like your attempt:
you are in the good way but you just forgot that :
1 - you use the scope in ng-model,
2 - to have 2 way data binding for factory,
bind the factory itself to a scope,
and use an item inside the factory not the factory itself (eg: data).
code:
.factory('selectedType', function(){
return {
data: {} // <---- We use 'data' here for example
}
})
and in the controller now:
.controller('appCtrl', ['$scope', 'selectedType',function($scope, selectedType) {
$scope.selectedType = selectedType; // <-- Not the selectedType.data (Important)
/* but the factory object */
$scope.ptype = [
{
value: 1,
label:'Kg'
},
{
value: 2,
label:'Pza'
}];
$scope.selectedType.data = $scope.ptype[0];
})
Now we dont need the ng-change at all:
<div id="layer1" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value"></select>
</div>
<div id="layer2" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value"></select>
</div>
plunker: https://plnkr.co/edit/5qg45F?p=preview
NB: Instead of using shared service, you can also use $rootScope or $scope.$parent for that.

Related

ng-repeat is not refreshed when ng-click method called from directive

I am working on creating reusable directive which will be showing composite hierarchical data .
On first page load, Categories like "Server" / "Software"/ "Motherboard" (items array bound to ng-repeat) would be displayed . If user clicks on "Server" then it would show available servers like "Ser1"/"Ser2"/"Ser3".
html :
<div ng-app="myApp" ng-controller="myCtrl" ng-init="init()">
<ul>
<li ng-repeat="item in items">
<div my-dir paramitem="item"></div>
</li>
</ul>
</div>
Now first time Items are loading, but clicking on any item is not refreshing ng-repeat. I have checked ng-click, "subItemClick" in below controller, method and it is being fired. However the items collection is not getting refreshed.
http://plnkr.co/edit/rZk9cbEJU90oupVgcSQt
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.init = function() {
$scope.items = [{iname: 'server',subItems: ['ser1', 'ser2','ser3']}
];
};
$scope.subItemClick = function(sb) {
if (sb.subItems.length > 0) {
var zdupitems = [];
for (var i = 0; i < sb.subItems.length; i++) {
zdupitems.push({
iname: sb.subItems[i],
subItems: []
});
}
$scope.items = zdupitems;
}
};
}])
.directive('myDir', function() {
return {
controller: 'myCtrl',
template: "<div><a href=# ng-click='subItemClick(paramitem)'>{{paramitem.iname}}</a></div>",
scope: {
paramitem: '='
}
}
});
I am expecting items like ser1/ser2 to be bound to ng-repeat on clicking "Server" but it is not happening .
Any help?
I think that onClick is screwing up the method's definition of $scope. In that context, the $scope that renders the ngRepeat is actually $scope.$parent (do not use $scope.$parent), and you're creating a new items array on the wrong $scope.
I realize the jsfiddle is probably a dumbed down example of what you're dealing with, but it's the wrong approach either way. If you need to use a global value, you should be getting it from an injected Service so that if one component resets a value that new value is reflected everywhere. Or you could just not put that onClick element in a separate Directive. What's the value in that?

ng-init does not initialize my selected model

I have a select option where I get a list of titles i.e. Mr, Mrs, Dr etc. from an API, the code looks like this in the front end:
<select class="form-control" name="titleSelect" id="titleSelect" ng-options="option.value for option in titleData.availableOptions track by option.key" ng-model="adult[$index].selectedTitle" ng-init="adult[$index].selectedTitle = titleData.availableOptions[0]"></select>
And this in the controller:
var getPassengerTitle = PassengerDetailsAndCardDetailsService.getPassengerTitle();
PassengerDetailsAndCardDetailsService is service, where I get the API data i.e. the title data. Furthermore this service returns a promise.
This is where I set the title Data:
getPassengerTitle.then(function(result){
$scope.availableTitles = angular.fromJson(result.titleList);
$scope.tempListOfTitles = [];
for(var key in $scope.availableTitles){
$scope.tempListOfTitles.push({key : key, value : $scope.availableTitles[key]});
};
$scope.titleData = {
availableOptions: $scope.tempListOfTitles,
};
});
When I try to ng-init to the first option (that is Mr.) it does not work as in a blank option is shown. However when I tried statically defining the titleData in an object it works, could you please help me?
UPDATE:
I failed to mention that the title data is inside an ng-repeat to support input for multiple people/passengers in this case. Therefore I'm using adult[$index] where it creates an ng-model for each person/passenger.
I am not sure if this is the required behavior you want, but if you want to initialise the title on selecting an option you can either use the value from ng-model or use the event handler ng-change. Here is an example jsbin which handles the change and assigns the new value to ng-model. Let me know if this is what you were looking for
try like this.
var myapp = angular.module('app', []);
myapp.controller('Main', function ($scope) {
var vm = this;
vm.adult = {
'selectedTitle':'1'
};
vm.titleData = [
{'key':'0','value':'Mr'},
{'key':'1','value':'Ms'},
{'key':'2','value':'Dr'}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app = "app">
<div ng-controller="Main as ctrl">
<div class="select-style">
<select ng-options="option.key as option.value for option in ctrl.titleData" ng-model="ctrl.adult.selectedTitle"></select>
</div>
</div>
</div>

angularjs dynamic model in directive from json object

I have a json file of objects that store the properties to be used in a directive.
I want to use the json obj model value in the directive, but nothing I am trying is working.
Anyone have any ideas what I am doing wrong / missing? I find this very confusing.
Hope someone can help been trying this for days now!
Edit::
I have a $http service that gets and returns the Json object and I can access the properties fine.
I am specifically trying to use the value of the json obj model property -- "model" : "ticketData.contactname" as the dynamic value of the ng-model.
If I just use the ticketData.contactname obj then it works fine and I can edit the model value, but if I try and use the string from the Json obj then it just prints the string into the input box.
I do not know what to do. I am sure it is something basic I am missing.
Thanks in advance
Json sample:
[
{
"inputsContact" : [
{
"labelName" : "Contact Name",
"placeholder" : "Enter your name",
"model" : "ticketData.contactname",
"type" : "text"
}
}
]
Html sample:
<text-input-comp inputdata="contactName" ng-model="contactModel"> </text-input-comp>
Directive text-input-comp:
.directive('textInputComp', [ '$compile', function($compile){
return {
restrict: 'E',
scope: {
inputData: '=inputdata',
modelData: '=ngModel'
},
templateUrl: '/app/views/partials/components/textInputComp.html'
}
}]);
Directive template sample:
<label> {{ inputData.labelName }} </label>
<input type="text" ng-model="modelData" ng-model-options="{ getterSetter: true }" placeholder="{{ inputData.placeholder }}" />
<div ></div>
Controller sample:
$scope.contactName = $scope.inputData[0].inputsContact[0];
$scope.contactModel = $scope.inputData[0].inputsContact[0].model;
i think u need to get the json file first then do all the manupilation
have a look at this code
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="customersCtrl">
<ul>
<li ng-repeat="x in myData">
{{ x.Name + ', ' + x.Country }}
</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('customersCtrl', function($scope, $http) {
$http.get("customers.json").then(function (response) {
$scope.myData = response.data.records;
});
});
</script>
</body>
</html>
What u were missing is this
Just replace the customer.json file with your json and u are good to go
and
1.$http is service responsible for communicating with other file. $http says get file from ‘js/data.json’ and if the operation is a ‘success’ then execute a function holding the ‘data’ which it got automatically from data.json file
to make u understand.
2.A one more line above: [‘$scope’, ‘$http’, function($scope, $http){ … }] is little bit tricky: it takes an array holding two objects one is $scope and other is a service i.e $http. The reason we have this in array is angularJS automatically minifies code which means it removes extra spaces and shorten variable names for faster performance but sometimes this minification causes trouble so we just told controller NOT to minify $scope, $http services and function inside array.

Angular.js. Two-way data binding breaks when using services

The problem is that SecondName attribute is not updating when I input text in the field.
please look at this code at jsfiddle: http://jsfiddle.net/HEdJF/253/
HTML:
<div ng-app="myApp">
<div ng-controller="FirstCtrl">
<div>
<input type="text" ng-model="Data.FirstName"><!-- Input entered here -->
<br>FirstName is : <strong>{{Data.FirstName}}</strong><!-- Successfully updates here -->
</div>
<hr>
<div ng-controller="SecondCtrl as Second">
SecondName: {{Second.Data.SecondName}}<!-- How do I automatically updated it here? -->
</div>
</div>
</div>
JS:
var myApp = angular.module('myApp', []);
myApp.service('Data', function(){
var obj;
return obj = { FirstName: '54',
SecondName: '22',
f: function(){
obj.FirstName = '1';
obj.SecondName = obj.FirstName;
}
};
});
myApp.controller('FirstCtrl', function( $scope, Data ){
Data.f();
$scope.Data = Data;
});
myApp.controller('SecondCtrl', function( Data ){
Second = this;
Second.Data = Data;
});
It's not going to work like you think it should. This line:
obj.SecondName = obj.FirstName;
creates a new property SecondName equal by value to the FirstName. However since both properties are primitive types (String) there is no connection between them. In other words obj.SecondName does not reference obj.FirstName.
You have two options.
Option 1. (bad) Set up additional watcher on FirstName change, and once that happens update SecondName respectively
$scope.$watch('Data.FirstName', function() { Data.SecondName = Data.FirstName; });
http://jsfiddle.net/HEdJF/254/
Option 2. Don't introduce additional watchers and change your architecture. For example, use FirstName in the second controller too, since they are supposed to be equal.
http://jsfiddle.net/HEdJF/255/
This is a scoping issue because of your nested scopes. Take a look at this website for a clear explanation: http://toddmotto.com/digging-into-angulars-controller-as-syntax/. There's a few different solutions to solve your problem under the Nested Scopes section.
The problem with your code is that the First Controller is just changing the value of Data.FirstName object, hence the changes are not reflecting on your second controller because the value of SecondName does not change after it is initialized in the first controller. So you have to set your Data.SecondName
in your data as well.
Alternatively, you can do this.
<div>
<input type="text" ng-model="Data.FirstName"><!-- Input entered here -->
<br>FirstName is : <strong>{{Data.SecondName=Data.FirstName}}</strong><!-- Successfully updates here -->
</div>
You can also use directives to achieve this functionality, but I guess you are just looking for the above solution.
Cheers!

Understanding Angularjs scope

I am working angularjs scope, I countered an interesting problem. I have the following html structure:
<body ng-app='myApp'>
<div ng-controller='myCtrl'>
<select ng-model='selectedOption'
ng-options='option.size for option in options'
ng-change='update(selectedOption)'></select>
</div>
<div ng-controller='myCtrl'>
<ul>
<li ng-repeat='asset in assets'>
{{asset}}
</li>
</ul>
</div>
</body>
Notice that i have 2 controllers that have the same name 'myCtrl'.
And the following is my javascript code:
var myApp = angular.module('myApp', []);
myApp.factory('Assets', function() {
return {
assets: [{test:1}]
};
});
myApp.controller('myCtrl', function($scope, Assets, $rootScope) {
$scope.assets = Assets.assets;
var temp = [
{test:5},
{test:6},
{test:7}
];
$scope.options = [
{size: 1},
{size: 2},
{size: 3},
{size: 4}
];
$scope.selectedOption = $scope.options[0];
$scope.update = function(value) {
console.log($scope.$id);
/*for(var i = 1; i < value.size; i=i+value.size) {
$scope.assets.push({
test: value.size
});
}*/
$scope.assets = temp;
};
});
When I run the code and select a option from the dropdown list, console will log '003' which is the first controller's $id. Why inside the 'update' function I can't get the second controller's scope? Is there a way to get it?
Here is a link to JSbin: http://jsbin.com/gecizuwaku/2/edit?html,js,console,output
You could, but you would need to invoke the update() function inside the second ng-controller="myCtrl" block.
The way ng-controller roughly works is that it creates a child scope of the scope of the element it is on. Then it instantiates the controller function, resolving any dependencies and passing them in, along with this new child scope as $scope. That means each ng-controller instance will create a new instance of the controller function with a new child scope supplied.
In your specific case, the two scopes created by ng-controller will be siblings.
You could see the second scope's $id by running the following in the developer console:
angular.element('ul').scope().$id
Or by invoking the update() method:
angular.element('ul').scope().update()
which would log the $id to the console. It will likely be the next sequential id, which in your case will be "004".

Resources