AngularJs accessing scope outside from another function in javascript - javascript

I want to change the value of variables in html with the help of AngularJS. For that I know that onclick function can be called which will enable us to change the value:
The javascript code:
var app = angular.module('graphApp', []);
app.controller('graphAppCtrl', function($scope) {
$scope.count = 0;
$scope.myFunction = function() {
$scope.names= {0:{'Name':'John','Country':'albania'}};
$scope.count++;
keyword_type = 1
check();
}
});
Now I want to change the value of variable names from outside the function after performing some calculations. What is the procedure for that?
HTML :
<div id="search-container" ng-app="graphApp" ng-controller="graphAppCtrl">
<tabs id="mainTabs">
<pane title="Name search">
<p>Name: <input type="text" ng-model="name"></p>
<button ng-click="myFunction()">Search</button>
<table>
<tr>
<td>Index</td>
<td>Name</td>
<td>ID</td>
</tr>
<tr ng-repeat="x in names">
<td>{{ x.Name }}</td>
<td>{{ x.Country }}</td>
</tr>
</table>
<p ng-bind="check"></p>
</pane>
getting value from this function :
function WebSocketTest(keyword_or_query,keywordtype) {
var object_of_each_pair = [];
if ("WebSocket" in window) {
//messageContainer.innerHTML = "WebSocket is supported by your Browser!";
var ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = function() {
var message=[]
message = keyword_or_query + 'XXXX' + keywordtype
ws.send(message);
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
node_id = received_msg
}

(fiddle)
Angular environment:
app.controller('graphAppCtrl', function($scope) {
$scope.some_info = 'foo';
});
This is how you retrieve scope from outside Angular environment:
// in your case
var where_you_put_ng_app = document.getElementById("search-container");
var scope = angular.element(where_you_put_ng_app).scope();
// from here you can modify your $scope variables
scope.some_info = '.....';

if i understand correctly you want to change your controller's variable values from outside angularjs , in javascript.
if that so, you should get the current scope and $apply the changes, like this:
var scope = angular.element($("any class name from your DOM")).scope();
//in order to update the new values to the controller , '$apply'
scope.$apply(function () {
//here you can call and change any variable from your controller
scope.names= {0:{'Name':'xxxx','Country':'xxxx'}};
});

in case your $scope.myFunction() and WebSocketTest() both are in same controller then just after getting response you can assign value to $scope.names
In case both are in different controller then after getting value in WebSocketTest() you can use $rootScope.$broadcast like this
suppose you got your value in
currentVariable//just name of variable
then
$rootScope.$broadcast("broadCastonmessage",currentVariable)
//from here we will broadcast once we got the value
now in you controller
$rootScope.$on('broadCastonmessage',function($event,Value){
//this part will be executed whenever broadcast is done
//now value can be assigned to scope variable
$scope.names =value
})
here is one simple example showing how to use broadcast to assign a changed variable to some other variable in other scope hope it will prove helpful to you
http://codepen.io/vkvicky-vasudev/pen/Eyyzvx

Related

Angularjs: ngDialog only binds and modifies with objects; not basic variables.

I've already found a "solution" to this problem; I was just hoping someone might be able to provide a reason why it works.
This jsFiddle demonstrates the problem:
http://jsfiddle.net/s1ca0h9x/137/
HTML
<div data-ng-app="myApplication">
<div data-ng-controller="MainController">
Click Here
<input type="text" ng-model="accountNum" />
<span>{{accountNum}}</span>
</div>
</div>
ANGULARJS
var myApplication = angular.module('myApplication', ['ngDialog']);
myApplication.controller('MainController', function ($scope, ngDialog) {
$scope.accountNum = 'test';
$scope.ShowNgDialog = function () {
ngDialog.open({
template: '<div><input type="text" ng-model="accountNum"/></div>',
plain: true,
scope:$scope
});
}
});
When I try and manipulate a scope variable (in this case: $scope.accountNum = 'test') from the dialog, it doesn't bind/save it back to the model.
...However, when I change that variable into an object, things just magically work, as shown in this demo: http://jsfiddle.net/s1ca0h9x/138/
HTML
<div data-ng-app="myApplication">
<div data-ng-controller="MainController">
Click Here
<input type="text" ng-model="FormData.accountNum" />
<span>{{FormData.accountNum}}</span>
</div>
</div>
ANGULARJS
var myApplication = angular.module('myApplication', ['ngDialog']);
myApplication.controller('MainController', function ($scope, ngDialog) {
$scope.FormData={accountNum: ''};
$scope.ShowNgDialog = function () {
ngDialog.open({
template: '<div><input type="text" ng-model="FormData.accountNum"/></div>',
plain: true,
scope:$scope
});
}
});
I also tested both options using a template linking to a file, and not using plain:true, in addition to trying ngDialog.openConfirm, etc. I essentially rebuilt the solution found here ngDialog $scope variables not being updated by ngModel fields in $dialog when using scope: $scope piece by piece, and finally the only change that seemed to work was using an object instead of a basic scope variable.
Am I approaching this wrong, or missing some fundamental aspects of data binding?
I think this has nothing to do with the binding. I will explain what I did understood when I dig into the code of ngDialog and AngularJS.
I think the first case is not working as you expect, because $scope.accountNum = 'test'; is a simple string which is a primitive type and is not mutable(ref) or in other words is immutable:
Mutable is a type of variable that can be changed. In JavaScript, only
objects and arrays are mutable, not primitive values.
(You can make a variable name point to a new value, but the previous
value is still held in memory. Hence the need for garbage collection.)
A mutable object is an object whose state can be modified after it is
created.
Immutables are the objects whose state cannot be changed once the
object is created.
String and Numbers are Immutable.
So, in short words, this was the reason why the first variant is not working as you want :)
Now let's have a look on this code of ngDialog, which is a part of open() method:
var scope;
scopes[dialogID] = scope = angular.isObject(options.scope) ? options.scope.$new() : $rootScope.$new();
in your case we are calling options.scope.$new(), because you specified scope in options when opening the dialog.
Now let's go and check this angular code:
$new: function (isolate, parent) {
var child;
parent = parent || this;
if (isolate) {
child = new Scope();
child.$root = this.$root;
} else {
if (!this.$$ChildScope) {
this.$$ChildScope = createChildScopeClass(this); // <---- WE ARE COMING HERE NOW
}
child = new this.$$ChildScope();
}
...
function createChildScopeClass looks like:
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();
this.$$ChildScope = null;
}
ChildScope.prototype = parent; /* <--- They simply assign the derived scope
as prototype of the new one (which is going to be the scope of the ngDialog) */
return ChildScope;
}
We can see that function createChildScopeClass() simply assigns the parent scope's prototype to the new one (which is going to be the scope of the opened ngDialog)
And a sample that is demonstrating mutability and immutability:
var test = 'test';
var test2 = test;
test2 = 'new value';
console.log('test = ' + test + ' // test2 = ' + test2);
var testObj = {test: 'test'};
var test2Obj = testObj;
test2Obj.test = 'new value';
console.log('testObj.test = ' + testObj.test + ' // test2Obj.test = ' + test2Obj.test);
Conclusion
Use objects or arrays in your parent scope if you want binding to work in the derived scope. Sample using AngularJS:
var app = angular.module('sample', []);
app.controller('AppController', ['$scope', function($scope) {
$scope.primitive = 'test';
$scope.obj = {
test: 'test initial'
};
$scope.newScope = $scope.$new();
$scope.newScope.primitive = 'test 2';
$scope.newScope.obj.test = 'updated value';
}]);
app.run();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sample">
<div ng-controller="AppController">
<table>
<thead><tr><th>Property</th><th>Value</th><th></th></tr></thead>
<tbody>
<tr>
<td>primitive</td>
<td>{{ primitive }}</td>
<td><input type="text" ng-model="primitive"></td>
</tr>
<tr>
<td>obj.test</td>
<td>{{ obj.test }}</td>
<td><input type="text" ng-model="obj.test"></td>
</tr>
<tr>
<td>newScope.primitive</td>
<td>{{ newScope.primitive }}</td>
<td><input type="text" ng-model="newScope.primitive"></td>
</tr>
<tr>
<td>newScope.obj.test</td>
<td>{{ newScope.obj.test }}</td>
<td><input type="text" ng-model="newScope.obj.test"></td>
</tr>
</tbody>
</table>
</div>
</div>
For me what worked, is to create a function in the base controller, and call the function from ngDialog controller.
Ex:
myApplication.controller('MainController', function ($scope, ngDialog) {
$scope.accountNum = 'test';
$scope.ShowNgDialog = function () {
ngDialog.open({
template: '<div><input type="text" ng-model="accountNum"/></div>',
plain: true,
scope:$scope,
controller: ['$scope',
function ($scope) {
$scope.updateVar();
}]
});
};
$scope.updateVar = function(){
$scope.accountNum = "changed";
}
});

AngularJS: weird behavior with the 2-way binding (ng-repeat)

I'm using AngularJS in a Firebase app and I have a function where I do some inner join to get some data. More details here. After getting the response from the firebase api I create an object and push it into an array (a scope variable). I see in the debug that the data has been retrieved and that the $scope variable is filled correctly. The problem is that it is not showing in the ng-repeat.
My function:
$scope.getMessagesByRegion = function(regionId){
console.log('Function start');
var rootRef = firebase.database().ref();
var regionMessagesRef = rootRef.child("region_messages/"+ regionId);
$scope.messages_by_region = []; // Here I reset the scope variable
regionMessagesRef.on('child_added', function(rmSnap) {
var messageRef = rootRef.child("messages/"+rmSnap.key);
messageRef.once('value').then(function(msgSnap){
var msg = {
key : msgSnap.key,
name : msgSnap.val().name,
type : $scope.getTypeName(msgSnap.val().type),
show_only_once : rmSnap.val().show_only_once,
pre_requisite_message : rmSnap.val().pre_requisite_message
}
console.log(msg); // here I see the object in the console. it is OK
$scope.messages_by_region.push(msg); // pushing the item
console.log('----------------');
console.log($scope.messages_by_region);
})
});
}
My HTML:
<table class="table">
<thead>
<tr>
<th>Message name</th>
<th>Type</th>
<th>Show only once</th>
<th>Pre requisite</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="msg in messages_by_region">
<td ng-bind="msg.name"></td>
<td ng-bind="msg.type"></td>
<td ng-bind="msg.show_only_once"></td>
<td ng-bind="msg.pre_requisite_message"></td>
</tr>
</tbody>
</table>
This is what I see in the console:
The problem is that even having an object in the array it is not shown in the view. It is like there was an empty array set to the $scope.messages_by_region variable
I'm having a hard time figuring out what I'm doing wrong. Can you see what's wrong with my function?
Thanks for any help.
try,
$scope.$apply(function(){
$scope.messages_by_region.push(msg);
});
or,
$scope.messages_by_region.push(msg);
$scope.$apply();
Since you're using async functions (Cosuming of firebase API) you should tell angular to refresh the HTML;
Use
$scope.$diggest()
More information you can find on https://www.sitepoint.com/understanding-angulars-apply-digest/
As you are performing Async calls you need to tell angular to refresh the changes in the value with $apply call you can do it with:
$scope.getMessagesByRegion = function(regionId) {
console.log('Function start');
var rootRef = firebase.database().ref();
var regionMessagesRef = rootRef.child("region_messages/" + regionId);
$scope.messages_by_region = []; // Here I reset the scope variable
regionMessagesRef.on('child_added', function(rmSnap) {
var messageRef = rootRef.child("messages/" + rmSnap.key);
messageRef.once('value').then(function(msgSnap) {
var msg = {
key: msgSnap.key,
name: msgSnap.val().name,
type: $scope.getTypeName(msgSnap.val().type),
show_only_once: rmSnap.val().show_only_once,
pre_requisite_message: rmSnap.val().pre_requisite_message
}
$scope.$apply(function() {
console.log(msg); // here I see the object in the console. it is OK
$scope.messages_by_region.push(msg); // pushing the item
console.log('----------------');
console.log($scope.messages_by_region);
});
});
});
}
For more information on this behavior you can also read article describing the problem here

AngularJS: ng-hide not working

I am trying to hide a column in my table using ng-hide. Before the user logins the column should not been shown to the user. After they login the hidden column should be shown. But now after i used the ng-hide property the whole table is hidden if the user isnt login into the system. Can i know how to solve this problem.
This is my partial html code:
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th ng-show="noteEnabled">Note</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="movie in movies | pagination: currentPage * entryLimit | limitTo: entryLimit" data-ng-class="{'selected':selectedFilm.film_id===movie.film_id}" >
<td>
{{movie.title}}
</td>
<td>
{{movie.description}}
</td>
<td data-ng-click="selectFilmDetails($event,movie)" ng-show="movie.noteEnabled" >
{{movie.comment}}
</td>
</tr>
</tbody>
</table>
This is my controller.js code:
.controller('AllMovieController',
[
'$scope',
'dataService',
'$location',
function ($scope, dataService, $location){
$scope.noteEnabled = false;
$scope.movies = [ ];
$scope.movieCount = 0;
$scope.currentPage = 0; //current page
$scope.entryLimit = 20; //max no of items to display in a page
var getAllMovie = function () {
dataService.getAllMovie().then(
function (response) {
var userName=dataService.getSessionService('user');
$scope.movieCount = response.rowCount + ' movies';
if(userName){
$scope.movies = response.data;
$scope.userLogin = dataService.getSessionService('user');
$scope.userLoginEmail = dataService.getSessionService('userEmail');
$scope.showSuccessMessage = true;
$scope.successMessage = "All movie Success";
$scope.noteEnabled = true;
}
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
$scope.numberOfPages = function(){
return Math.ceil($scope.movies.length / $scope.entryLimit);
};
//------------------
$scope.selectFilmDetails = {};
$scope.selectFilmDetails = function ($event,movie) {
$scope.selectFilmDetails = movie;
$location.path('/filmDetails/' + movie.film_id);
}
getAllMovie();
}
]
)
At first i set the noteEnabled to false and check with the session if the user is logged in then the noteEnabled will become true. Thanks in advance.
Use ng-hide="$parent.noteEnabled" instead of ng-hide="noteEnabled".
To access $scope variable from the loop (ng-repeat) use $parent
Here is a good example of how to use ng-show and ng-hide and here is the official documentation as well . Hope it helps !
In your case you have use ng-show/ ng-hide only to tag in of the column you want to show/hide. So on any scenario whole table will not be hide unless there is no data so you may is it as hidden.
Anyway as per you code, seems you have misused ng-show/hide. On controller initially you set noteEnabled to false and after you check the logging you set noteEnabled to true. as you have used ng-show/hide as follows
<td data-ng-click="selectFilmDetails($event,movie)" ng-hide="noteEnabled" >
{{movie.comment}}
</td>
the result will be; initially column will be shown and after your dataService receive userName it will hide the column. The opposite of what you want!!!. So change the directive you use from ng-hide to ng-show or change the value set to noteEnabled.
The real cause behind problem is, ng-repeat does create child scope which is prototypically inherited from parent scope, while rendering each iteration element where ng-repeat directive has placed.
And the you have used noteEnabled variable as primitive datatype, so when you use noteEnabled variable inside a ng-repeat div it does gets added inside that ng-repeat div scope.
noteEnabled property to be maintained on each element level of movies collection. Then do toggle, whenever you want.
ng-show="movie.noteEnabled"
By default it will be hidden & toggle it whenever you want to show it.
Even better approach is to follow controllerAs pattern where you don't need to care about prototypal inheritance. While dealing with such a variable access thing on UI.
I solved the problem by myself. Here is the solution for it.
$scope.noteEnabled = false;
$scope.movies = [ ];
$scope.movieCount = 0;
$scope.currentPage = 0; //current page
var getAllMovie = function () {
dataService.getAllMovie().then(
function (response) {
var userName=dataService.getSessionService('user');
$scope.movieCount = response.rowCount + ' movies';
if(userName){
$scope.movies = response.data;
$scope.userLogin = dataService.getSessionService('user');
$scope.userLoginEmail = dataService.getSessionService('userEmail');
$scope.showSuccessMessage = true;
$scope.successMessage = "All movie Success";
$scope.noteEnabled = true;
}else{
$scope.movies = response.data;
$scope.noteEnabled = false;
}

Angular: Best practice for updating a variable from a function?

Within an ng-repeat block I have textboxes. To detect when the content differs from the original, the original data is stored in a variable.
<tr data-ng-repeat="p in products">
<td>
<textarea data-elastic data-ng-model="p.comment" data-ng-change="hasChanged=checkChange(original, rnd.comment);" data-ng-init="original=rnd.comment; hasChanged=false"></textarea>
</td>
<td class="save" ng-show="hasChanged" ng-click="save(p, original)">Save</td>
A save button is shown only when the content has changed. After a successful save the original value should be updated to the new value.
I can do it like this with a function in the controller:
$scope.save = function (p, original) {
//...successful save
this.original = p.comment; //this works
original = p.comment; //this does not
}
Relying on some implicit scope in the form of 'this' doesn't seem sensible.
Why doesn't updating the variable (original = ...) work? What's a smarter way to do this?
Based on comments I've updated it as follows:
ng-click="save(p, this)"
$scope.save = function (p, scope) {
//...successful save
scope.original = p.comment; //this works
}
This seems failrly sensible now. Is passing scope around like this considered bad practice or acceptable?
Products is defined as follows:
productStatusApp.controller('productStatusCtrl', function ($scope, $http, cid) {
$http.get('api/company/products/' + cid).success(function (data) {
$scope.products = data.products;
});
I've found the best way to avoid this kind of problems, is to use services
https://docs.angularjs.org/guide/services
http://viralpatel.net/blogs/angularjs-service-factory-tutorial/
some rough code(use it just for pointers, not tested at all)
<tr data-ng-repeat="p in ProductsService.products">
<td>
<textarea data-elastic data-ng-model="p.comment"></textarea>
</td>
<td class="save" ng-show="p.originalComment!==p.comment" ng-click="ProductsService.save(p)">Save</td>
</tr>
and
var module = angular.module('app', []);
module.service('ProductsService', function () {
var products = [postA,postB,...,PostC];
products = products.map(function(p){p.originalComment=p.comment});
var save = function(p){
p.originalComment=p.comment;
someAjaxRequest(function _callback(err,response){....})
}
return {products:products,save:save};
});
and
module.controller('ProductsController', function ($scope, ProductsService) {
$scope.ProductsService= ProductsService;
});
They also allow better readability , WIN WIN

How to keep track of Array index in angularJS?

Very new to Angularjs. So I have some JSON files that I am reading into my webpage that contain and array of objects that are cars. What I am trying to do is have my "button" when pressed alert me to the data specific to that button.
The ng-repeat is running 8 times so that is the length of the array, but in angularJs i'm not sure how to basically store the array index for each time the ng-repeat passes in my button function.
This is my a snippet of my .html:
<div class="carTable table-responsive text-center" ng-controller="ButtonController" >
<table class="table specTable">
<thead>
<tr>
<th>Make</th>
<th>Model</th>
<th>Year</th>
<th>Color</th>
<th>Milage</th>
<th>Doors</th>
<th class="reserve">Horsepower</th>
<th>Price</th>
<th class="reserve"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cars in car | orderBy:'year'">
<td>{{cars.year}}</td>
<td>{{cars.model}}</td>
<td>{{cars.make}}</td>
<td>{{cars.color}}</td>
<td>{{cars.mileage | number}}</td>
<td>{{cars.doors}}</td>
<td>{{cars.horsepower}}</td>
<td>{{cars.price | number}}</td>
<td><div class="panel panel-default">Reserve</div></td>
</tr>
</tbody>
</table>
</div>
The portion in question is at the bottom where I have a Reserve "button"
I'm leaving out my JSON files, works properly there. I'm just not sure how to keep track of the array index as the ng-repeat does its thing.
Here is the angular:
(function(){
var app = angular.module("myReserveApp", []);
app.controller("ButtonController", ["$scope", "$window", function($scope, $window){
$scope.buttonPress = function(){
$window.alert(JSON.stringify($scope.car[0]));
}
}]);
var MainController = function($scope, $http, $window){
var onGatherBoatData = function(response){
$scope.boat = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Boat Data";
};
var onGatherCarData = function(response){
$scope.car = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Car Data";
};
var onGatherTruckData = function(response){
$scope.truck = response.data;
};
var onError = function(reason){
$scope.error = "Could not fetch Truck Data";
};
$scope.message = "Hello, Angular Here!";
};
app.controller("MainController", ["$scope", "$http", "$window", MainController]);
}());
Currently in the top portion of the code I just have it alerting object[0] but I want it to be specific to which button is pressed. Any help is appreciated, thank you.
$index refers to the index in ng-repeat. So if you want to pass your function the index in array on the button click, change buttonPress() to buttonPress($index)
you'll have to change your controller to something like the following:
$scope.buttonPress = function(index){
$window.alert(JSON.stringify($scope.car[index]));
}
To do the following, you can just pass the current data in the ngRepeat. Moreover,if you want the current index, the ngRepeat directive provide specials properties, as the $index, which is an iterator.
$scope.buttonPress = function(car, index){
//Retrieve current data of the ngRepeat loop
console.log(car);
//Current index of your data into the array
console.log(index);
}
Then you can call your function like this :
Reserve
First, thank you both for the quick responses. Both of these answers work. I found another way to do it as well before reading your posts.
<div class="panel panel-default">
Reserve:{{car.indexOf(cars)}}
</div>
Using (car.indexOf(cars)) gives me the same result
$scope.buttonPress = function(index){
$window.alert(JSON.stringify(index));
}
Now when I click on the "button" it sends me back the array index, so now I should be able to play with that data. Thank you again both, for your help.

Categories

Resources