$scope is undefined in view but not in controller - javascript

I have a $scope that needs to show the iso code on select from a list of countries.
So the countries list is being fetched a $http call factory and it populates into a scope, so far so good here
///////// get countries ////////////////
tableFactory.getCountries().then(function(data){
$scope.presentCountries = data;
});
presentCountries will be sent into typeahead where the user will type for a country and select it, like so:
<div class="countrySearchField">
<div class="row">
<h3 class="searchTitle">Pick a country</h3>
</div>
<input type="text" class="form-control search-input" ng-model="countryPicked" uib-typeahead="presentCountry.name for presentCountry in presentCountries | filter:{name: $viewValue}:startsWith" typeahead-min-length="1" typeahead-on-select="onSelectCountry($item, $model, $label)" typeahead-editable="false"/>
</div>
Once it's selected, it will call the scope function onSelectCountry.
$scope.onSelectCountry = function($item, $model, $label){
$scope.brandCountryCode = $item.iso_code_2;
// $scope.brandCountryCode = $item;
console.log($scope.brandCountryCode);
}
I can see the data,$scope.brandCountryCode, in the console from console.log, but not in the modal view(overlay view).
<div class="row subtitleModalRow">
<h5 class="unlock-description">Please check the following parameters before merging</h5>
<!-- <p class="unlock-description" ng-bind-html="overlayMessage"></p> -->
<ul>
<li>Brand Country: {{brandCountryCode}}</li>
</ul>
</div>
I don't understand why it's not showing up, but I can see the console.log output. I'm suspecting is because the scope is under a function, but I thought if you set a value into a $scope, it should be available anywhere, please correct me if I'm wrong.
Also, it could be that the overlay view is a separate html file from the typeahead html. However, this overlay view has the controller, tablePageCtrl. The controller should be present in both html files.
this.openModal = function(type) {
this.type = type;
if(type == 'mergeRequest'){
var templateUrl = 'app/views/dataMerge/overlayMsg.html';
var backdropClass = 'auth-backdrop';
var controller = 'tablePageCtrl';
}
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
controller: controller,
windowTopClass: 'auth-template',
backdrop: 'static',
backdropClass: backdropClass,
});
};
I did a test, outside the function called $scope.test = 'test' in the controller, and I see the value in the overlay view. I have other scopes based onSelect function that are running into this situation, and they all are under a $scope function.
I added an initializer, $scope.brandCountryCode = '', but it didn't do anything. If I can solve this one, then the other ones should be fine.
Not sure what's going here, does anybody have any suggestions. Still new to Angular and your help will be appreciated. Thanks!
EDIT
full service code for the overlay modal:
(function() {
'use strict';
angular
.module('com.xad.se-tools')
.service('tableModalService', function($scope, $uibModal){
this.modalInstance = null;
this.type = null;
this.openModal = function(type) {
this.type = type;
if(type == 'mergeRequest'){
var templateUrl = 'app/views/dataMerge/overlayMsg.html';
var backdropClass = 'auth-backdrop';
var controller = 'tablePageCtrl';
}
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
// controller: 'AuthCtrl',
controller: controller,
windowTopClass: 'auth-template',
backdrop: 'static',
backdropClass: backdropClass,
scope: $scope
});
};
this.closeModal = function() {
this.modalInstance.dismiss('cancel');
}
});
})();

I think that you should set scope config option in your modal instance to set scope used in modal:
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
controller: controller,
windowTopClass: 'auth-template',
backdrop: 'static',
backdropClass: backdropClass,
scope: $scope
});
Please check angular bootstrap modal documenation here for more details.

Related

UI-grid: How to access MODEL_COL_FIELD from custom function?

I need to call from external scope a function, which opens the $modal, and on closing the modal, returns the result.
<div class="ui-grid-cell-contents">
<input ng-class="colt + col.uid"
ng-model="MODEL_COL_FIELD" ng-click="grid.appScope.openModal()"/>
</div>
and the function
$scope.openModal = function () {
var dialogScope = $scope.$new();
dialogScope.result = '';
var modalInstance = $modal.open({
templateUrl: 'url here',
scope: dialogScope,
size: 'lg'
});
modalInstance.result.then(function() {
// here I need to assign dialogScope.result to ui-grid Cell variable (MODEL_COL_FIELD)
})
How can I update MODEL_COL_FIELD variable using my external scope function? It's probably available somewhere in grid.appScope
If I understand correctly, you are just trying to pass that row data into a modal. I suggest using this method, which will give you access to the row object itself. This will allow any edits to the row in the modal to be passed to the parent grid.
Using this method, you would not be passing the result and updating the row, but instead updating the row from the modal itself because you are using a reference to the row object.
I have left dialogScope.result, but it will not be necessary to have for what you are doing here.
Template
<div class="ui-grid-cell-contents">
<input ng-class="colt + col.uid"
ng-model="MODEL_COL_FIELD" ng-click="grid.appScope.openModal(row)"/>
</div>
Function:
$scope.openModal = function (row) {
var dialogScope = $scope.$new();
dialogScope.result = '';
dialogScope.rowEntity = row.entity;
var modalInstance = $modal.open({
templateUrl: 'url here',
scope: dialogScope,
size: 'lg'
});
modalInstance.result.then(function() {
// here I need to assign dialogScope.result to ui-grid Cell variable (MODEL_COL_FIELD)
})

Accessing ng-hide value of one controller to manipulate ng-style of another controller within a global controller

So I am trying to access the boolean value of ng-hide from one controller to change css properties of another controller using a global controller. Here is the link on jsfiddle: https://jsfiddle.net/dqmtLxnt/
HTML
<div ng-controller="mainCtrl">
<div class="first" ng-style="changeColor" ng-controller="firstCtrl">
{{ accessOne }}
</div>
<div class="second" ng-hide="hideSecond" ng-controller="secondCtrl">
{{ accessTwo }}
</div>
</div>
JS
angular.element(document).ready(function() {
angular.module('app', [])
.controller('mainCtrl', ['$scope', function($scope) {
if ($scope.hideSecond == true) {
$scope.changeColor = {
'color': 'blue'
};
}
}]).controller('firstCtrl', ['$scope', function($scope) {
$scope.accessOne = "One";
}]).controller('secondCtrl', ['$scope', function($scope) {
$scope.accessTwo = "Two";
$scope.hideSecond = true;
}]);
angular.bootstrap(document.body, ['app']);
});
I am able to change both changeColor and hideSecond from mainCtrl but I'm not able to access the value set from secondCtrl in the mainCtrl. I even tried setting hideSecond=true in ng-hide and access it on mainCtrl but to no avail.
Can someone help me on this?
You can use a Angular Service for this, storing the value into a service and than retrieving it from the service in another controller.
In your situation the main controller is loaded before the second controller so it would never know when the value is updated. You can use $watch for this, calling an update function when the $scope is updated.
Here if your fiddle updated: https://jsfiddle.net/dqmtLxnt/2/
Service:
angular.module('app.services', [])
.factory('hide', function() {
// Allows for passing of data between controllers
var x = false;
// Sets savedData to what ever is passed in
function set(data) {
x = data;
}
// Returns saved data
function get() {
return x;
}
return {
set: set,
get: get
}
})
Using it in Main Controller:
$scope.$watch(function () { return hide.get(); },
function (value) {
console.log(value);
$scope.update(value);
});
$scope.update = function(value) {
if (value == true) {
$scope.changeColor = {
'color': 'blue'
};
}
}
Using it in Second Controller:
$scope.hideSecond = true;
hide.set(true);
Make sure you inject the service into the app and the service into the controllers.

Why is my angular binding read only in a bootstrap UI modal in an MVC5 project?

I am trying to pop up a modal to get some input, but the angular binding via ng-model seems to be read only. This is my modal markup:
<script type="text/ng-template" id="signatureWindow.html">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Signature Capture</h4>
</div>
<input type="text" width="100px" ng-model="theName" />
<div class="modal-footer">
<button ng-click="accept()" class="btn btn-primary">Accept</button>
<button ng-click="cancel()" class="btn btn-default">Cancel</button>
</div>
</script>
Then, I invoke this modal as follows:
$scope.getSignatureModal = function(signatureBase64) {
var modalInstance = $modal.open({
templateUrl: 'signatureWindow.html',
controller: 'SignatureModalController',
size: 'lg',
resolve: {
signatureData: function() {
return signatureBase64;
}
}
});
modalInstance.result.then(function(signatureData) {
alert('Signed');
signatureBase64 = signatureData;
}, function() {
alert('Canceled');
});
};
And the following controller code for the modal:
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, signatureData) {
$scope.base64 = signatureData;
$scope.thename = "NamesRus";
$scope.accept = function() {
debugger;
$modalInstance.close($scope.thename);
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
}
});
The modal pops up as expected, and the input has the initial value "NamesRus", but when I close the modal, invoking accept in the modal controller, $scope.thename still has it's initial value, not any new value I type when the modal is active. What could be wrong here?
NOTE: At the debugger breakpoint in accept, no matter what I type in the modal's input, theName still has the initial assigned value.
MORE: My Plunker for this works fine. It's when in-place, in an ASP.NET MVC5 project, that I get the strange behaviour.
I think that you mix up two differents scopes.
If you want several variables to be passed to and retrieved from the modal you have to mention them:
in the resolve attribute
resolve: {modalData: function (){return {signatureData:$scope.base64,name:$scope.theName}}}
pass modalData as dependencie to your controller
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, modalData)
update the modal controller $scope with modalData
$scope.signatureData = modalData.signatureData;
$scope.name=modalData.name;
invoke
$modalInstance.close({signatureData:$scope.base64,name:$scope.theName})
reassign the original $scope with the result of promise
modalInstance.result.then(function (data) {
$scope.base64 = data.signatureData;
$scope.thename=data.name;
}
take a look at this plunker forked from ui-boostrap modal orginal example: http://plnkr.co/edit/whLSYt?p=info

AngularJS : Passing a function to an isolated scope of a directive to be called within its controller?

I'm trying to call a function passed from a controller's scope into a directive via the "&" operation from the directive's controller. That method, however, is claimed by Angular to be undefined. After reading my code over and over, scouring the internet, and then repeating that process, I've decided to turn to help here.
Here's the relevant part of my controller. It contains the method I pass to my directive.
angular.module('myApp.controllers', []).controller('PostCtrl', ['$scope', 'postalService', function($scope, postalService) {
$scope.posts = [];
$scope.getPosts = function() {
postalService.getPosts(function(err, posts) {
if(err);
else $scope.posts = posts;
});
};
}]);
Here's my directive. I am unable to invoke onPost.
angular.module('myApp.directives', []).directive('compose', ['postalService', function(postalService) {
return {
restrict: 'E',
transclude: false,
replace: true,
scope: {
onPost: "&" //why will it not
},
templateUrl: "partials/components/compose-partial.html",
controller: function($scope, postalService) {
$scope.title = "";
$scope.content = "";
$scope.newPost = function() {
postalService.newPost($scope.title, $scope.content, function(err) {
if(err) console.log(err + ":(");
else {
console.log("Success getting posts.");
//why can I not invoke onPost()??
$scope.onPost();
}
});
};
},
};
}]);
And here's the relevant part of my html
<div ng-controller="PostCtrl">
<section class="side-bar panel hide-for-small">
<compose onPost="getPosts()"></compose>
</section>
<!--more, non-relevant html here-->
</div>
I know the problem is not with my postalService Service. Instead, the directive reports that no function is passed to it. Why??
Replace
<compose onPost="getPosts()"></compose>
with
<compose on-post="getPosts()"></compose>
and it'll work.
The Angular docs say why it's so:
Directives have camel cased names such as ngBind. The directive can be
invoked by translating the camel case name into snake case with these
special characters :, -, or _.

Instantiate and initialize controller in AngularJS

I have a problem instanciating controller with Angular. I have a main controller AlkeTypeDefListController from which I want to dynamically create/remove controllers of type AlkeTypeDefController, so I have done that :
Code of AlkeTypeDefListController:
// Create main controller
Alke.controller('AlkeTypeDefListController', ['$scope', '$controller', function($scope, $controller)
{
var primitives =
[
];
// Add some properties to the scope
angular.extend($scope,
{
typedefs : primitives,
addTypeDef : function()
{
var controller = $controller("AlkeTypeDefController", {$scope:$scope.$new()});
$scope.typedefs.push(controller);
}
});
}]);
Code of AlkeTypeDefController:
// Create main controller
Alke.controller('AlkeTypeDefController', ['$scope', '$controller', function($scope, $controller)
{
// Add some properties to the scope
angular.extend($scope,
{
name : "New Type",
fields : [],
addField : function()
{
}
});
}]);
The html code is this one:
<div id="typedefs-editor" ng:controller="AlkeTypeDefListController">
<button ng:click="addTypeDef()">Add</button>
<button>Remove</button>
<div id="typedef-list">
<ul class="list">
<li ng:repeat="typedef in typedefs">{{typedef.name}}</li>
</ul>
</div>
</div>
The problem does not really come from the instantiation (which works fine), but from the initialization. In fact, when the new "li" appears when I push the "Add" button, the text "New type" (initialized in the controller) does not appear.
I think it is about the scope or something like that, but I can't really find how to fix this.
I wanted to know if this method seems correct, and also how could I fix the problem I have.
Thanks
Reading the code, I understand that you want to create typedefs dynamically and those typedef items have to be controlled by an AlkeTypeDefController.
In that case I would create AlkeTypeDefController using ng:controller directive, so you don't need to create the controller programmatically, because then you would need to attached it to the view and that's just what the ngController directive does for you.
Notice AlkeTypeDefListController does not create a AlkeTypeDefController controller, this is done in the view
Demo on Plunker
Controllers:
.controller('AlkeTypeDefListController', ['$scope', function($scope) {
var primitives = [];
$scope.typedefs = primitives;
$scope.addTypeDef = function() {
var typeDef = { name: 'New Type' };
$scope.typedefs.push(typeDef);
}
}])
.controller('AlkeTypeDefController', ['$scope', function($scope) {
$scope.addField = function() {
alert('add Field');
}
}]);
View (notice how ng-controller directive is specified in li element):
<div id="typedefs-editor" ng:controller="AlkeTypeDefListController">
<button ng:click="addTypeDef()">Add</button>
<button>Remove</button>
<div id="typedef-list">
<ul class="list">
<li ng:repeat="typedef in typedefs" ng:controller="AlkeTypeDefController">
{{typedef.name}}
</li>
</ul>
</div>
In the code above, ngRepeat is going to create a new $scope for each typedef. AlkeTypeDefController then decorates that scope with functions and values.
I hope it helps
When you call $controller("AlkeTypeDefController") it will essentially call new on the AlkeTypeDefController constructor and give you back the return value not the scope. You are assign the name attrubute to the scope though so it is not being accessed in your html when you have typedef.name.
Try changing your AlkeTypeDefController to this:
Alke.controller('AlkeTypeDefController', function() {
this.name = "New Type";
this.fields = [];
this.addField = function() {};
});
Then you can instantiate it with: var controller = $controller("AlkeTypeDefController"); and you shouldn't need to worry about creating nested scopes.
If I get what you're saying correctly then I think I'd try to leverage the power of a custom directive here instead of dynamically generating controllers.
plunker
Controller:
Alke.controller('alkeTypeDefListController', ['$scope', '$controller',
function($scope, $controller) {
var primitives = [];
var addTypeDef = function() {
$scope.typedefs.push({
name: 'new name'
});
};
var removeTypeDef = function(){
$scope.typedefs.pop();
};
var properties = {
typedefs: primitives,
addTypeDef: addTypeDef,
removeTypeDef: removeTypeDef
};
// Add some properties to the scope
angular.extend($scope, properties);
}
]);
Directive:
Alke.directive('alkeTypeDef', function() {
return {
restrict: 'A',
scope: {
typeDef: '=alkeTypeDef'
},
template: '{{typeDef.name}}',
link: function(scope, element, attr) {
var properties = {
fields: [],
addField: function() {
}
};
angular.extend(scope, properties);
}
};
});
HTML:
<div ng-app='Alke'>
<div id="typedefs-editor" ng-controller="alkeTypeDefListController">
<button ng-click="addTypeDef()">Add</button>
<button ng-click="removeTypeDef()">Remove</button>
<div id="typedef-list">
<ul class="list">
<li alke-type-def='typedef' ng-repeat="typedef in typedefs"></li>
</ul>
</div>
</div>
</div>
If you want a controller then you can use one in the directive instead of a linking function.

Categories

Resources