AngularJS won't update view - angularjs

I'm just new to angularJS and have my first big problem.
First I wanted to create a list and update it, but it doesn't work.
// index.html
<form ng-controller="ListCtrl">
<div class="form-group">
<input name="listname" ng-model="listname">
<input type="button" ng-click="set()" value="add new list">
</div>
</form>
<div ng-controller="ListCtrl">
<ul>
<li ng-repeat="list in lists">
{{list.id}} - {{list.listname}}
</li>
</ul>
{{output}}
</div>
The fun part is the resetForm(), there I reset the form but also will try to update the $scope.output. But that output never changes, no matter what I try to change.
// ListCtrl
var ListApp = angular.module('ListApp', []);
ListApp.controller('ListCtrl', function ($scope) {
// add new records to database
$scope.set = function() {
$scope.createTableIfNotExists();
$scope.insertSql = 'INSERT INTO Lists (listname) VALUES (?)';
if ($scope.listname) {
$scope.db.transaction(
function (transaction) {
transaction.executeSql($scope.insertSql, [$scope.listname], resetForm);
}
);
}
};
function resetForm() {
// clear the input field
$scope.listname = "";
$scope.listForm.$setPristine();
$scope.output = 'Hello World';
$scope.$apply();
}
Edit:
When I try this with $apply the console shows me an error:
function resetForm() {
$scope.$apply(function () {
$scope.listname = "";
$scope.listForm.$setPristine();
$scope.output = 'Hello World';
});
}
And the error is:
Uncaught TypeError: undefined is not a function
The error points direct to the start of "$scope.$apply(function..."
Edit 2:
When I add a new button in index.html and call a function with ng-click and in this function I just say
$scope.output = 'Hello World!';
Then my view updates.
It will only not update when I use a callback to change the scope. Don't understand that. I thought everything is connected in angularJS, especially when I'm in the same controller.
Edit 3:
Here is the plunker

Uncaught TypeError: undefined is not a function
That's because you called $scope.apply instead of $scope.$apply.
Try calling it with a callback function:
$scope.$apply(function () {
$scope.listname = "";
$scope.listForm.$setPristine();
$scope.output = 'Hello World';
});

Related

AngularJS - Callback after ng-repeat update

I got some trouble understanding how I make a callback after I've updated an ng-repeat. I basically want to be able to make a callback function after my updates to my ng-repeat has been finished. Currently have this:
var app = angular.module('myApp', []);
app.directive('onLastRepeat', function() {
return function(scope, element, attrs) {
if (scope.$first)
console.log("ng-repeat starting - Index: " + scope.$index)
if (scope.$last) setTimeout(function(){
console.log("ng-rpeat finished - Index: " + scope.$index);
}, 1);
};
});
app.controller('MyController', function($scope) {
$scope.data = [1,2,3,4,5,6,7,8,9,10,12,12,13,14,15,16,17,18,19,20];
$scope.buttonClicked = function() {
console.log('Btn clicked');
$scope.randomItems = getRandomItems(this.data.length);
};
});
HTML
<div ng-app="myApp">
<div ng-controller="MyController">
<button ng-click="buttonClicked()">Highlight random</button>
<ul class="item" >
<li ng-repeat="item in data" ng-class="{highlight: randomItems.indexOf($index) > -1}" on-last-repeat>{{ item }} </li>
</ul>
</div>
</div>
Link to fiddle: https://jsfiddle.net/hbhodgm3/
So how the "app" works is that it lists the content of the data-array then when you click the "highlight"-button it randomly highlights 2 in the list. So my problem is that I want to have a callback function for when the highlighting/DOM-render is done. I found a way to do this for the initial ng-repeat with $scope.first and $scope.last to check when ng-repeat is done, but doesn't seem to work with the highlighting.
Hope I managed to explain the problem,
Thanks in advance.
See $q and Promises for a better understanding of how to work with the asynchronous nature of angular.
Presuming getRandomItems(this.data.length); is an API call that could take seconds to perform:
asyncItems(this.data.length).then(function(randoms){
$scope.randomItems = randoms;
//handle post rendering callback
});
function asyncItems(length) {
var deferred = $q.defer();
var items = getRandomItems(length);
if (items){
deferred.resolve(items);
}
else {
//no items :(
deferred.reject([]);
}
return deferred.promise;
}

How to reflesh $scope in Angular in AJAX reponse?

I have AJAX response inside that is deleted object:
request.success(function (data) {
delete $scope.notifications.system[key];
$scope.$apply();
});
I have HTML code with block, that would be appear by condition:
<span ng-show="notifications.system.length == 0" ng-cloak>
DELETED
</span>
So, I tried to use $scope.$apply(); in response at once after removing object. But I have got error:
Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.13/$rootScope/inprog?p0=%24digest
How I can reload template when notifications.system.length is equal zero?
When you use delete on arrays it doesn't change the length of the array instead it replaces the element in the array with undefined. So your ng-show never changes because the length of the array isn't changing. Use splice instead and the array will shorten and your $scope should update at you expect.
$scope.notifications.system.splice($scope.notifications.system.indexOf(key), 1);
you shouldn't need $scope.$apply() for something like this.
If you choose to use the $scope.$apply() you should wrap everything in a $timeout and call it like this.
request.success(function(resp){
$timeout(function(){
$scope.$apply(function(){
//do stuff here to the scope.
});
});
});
Passing in a function reference to $apply will cause it to execute that function then $digest. Seems a bit strange I know, but the reason for this is that AngularJS typically calls $digest in response to user interaction, not necessarily to events like $http.success.
You could also do the managing of your errors differently.
Instead of adding directly to an object you could add the error objects to an array.
Deleting can then be done with the following code:
$scope.removeError = function (errorName) {
angular.forEach($scope.notifications, function (error, index) {
if (error.hasOwnProperty(errorName)) $scope.notifications.pop(index);
});
};
Have a look at the demo below and here at jsfiddle.
angular.module('myApp', [])
.controller('mainController', MainController);
function MainController($http, $scope, $timeout) {
$scope.notifications = [{
errorImg: 'failed to load image',
}//,
/*{ // other errors
//errorJS: 'failed to load js'
}*/];
$scope.removeError = function (errorName) {
angular.forEach($scope.notifications, function (error, index) {
//console.log(index, error.hasOwnProperty(errorName), $scope.notifications);
if (error.hasOwnProperty(errorName)) $scope.notifications.pop(index);
//console.log(index, error.hasOwnProperty(errorName), $scope.notifications);
});
};
$scope.clear = function () {
$http.jsonp('http://www.mocky.io/v2/556f7ba53db19a8f05f1e555?callback=JSON_CALLBACK')
.success(function (response) {
//dummy request
//console.log(response, $scope.notifications);
//delete $scope.notifications['errorImg'];
$scope.removeError('errorImg');
}) //.bind(this))
}
}
MainController.$inject = ['$http', '$scope', '$timeout'];
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller='mainController as main'> <pre>{{notifications |json}}</pre>
<!--<button ng-click="main.show()">Show data</button>
<ul>
<li ng-repeat="info in main.data">{{info.test}}</li>
</ul>-->
<button ng-click="clear()">clear error</button>
<ul>
<li ng-repeat="i in dummy">{{i}}</li>
</ul>
<div ng-show="notifications.length == 0">deleted</div>
</div>

angularjs: scope value doesn't get updated in view

there are buttons in detail.html file:
<div ng-controller="test.views.detail">
<div data-ng-repeat="item in details" scroll>
<button ng-click="showDetails(item)">The details</button>
in detail.js file
angular.module('test')
.controller('test.views.detail', function($scope) {
$scope.detailsClicked = false;
$scope.showDetails = function(item){
$scope.detailsClicked = true;
}....
in formDetail.html code:
<div ng-controller="test.views.detail">
{{detailsClicked}}
<div ng-if="detailsClicked">...
Initially it shows false for detailsClicked, when I click on button it goes to showDetails function but value of $scope.detailsClicked never get updated! It is straight forward not sure why it doesn't work:(
This is because you're using the same controller at two places and expecting the scope object to be the same which it is not. Everytime you call ng-controller in your markup a new scope object will be created. If you want them to be based off the same data then use a service.
Here is an example
app.controller('test.views.detail', function($scope, detailsClicked) {
$scope.detailsClicked = detailsClicked;
$scope.showDetails = function(item){
$scope.detailsClicked.isClicked = true;
}
});
Create a factory/service which will retain the data, make sure the data is a
app.factory('detailsClicked', function(){
var data = {
isClicked: false
}
return data;
});

Angular binding model in scope issue?

Hi I am new to the angular js and trying to write the chat application using the socket io and angular js with ionic for android platform. But in my chat page there is one issue.
I am trying to bind the textbox to the $scope.message variable using ng-model but it is not getting bind as the test when i show the same variable value in page itself it works as it is but in controller i gets value as undefined or empty
index.html
<body ng-app="farmApp" ng-controller="farmAppController" >
<ion-content>
<ul id="Messeges">
<li class="right">Welcome to Chat</li>
<li ng-repeat="chatMessage in messages">
{{chatMessage}}
</li>
</ul>
<form ng-submit="sendMessage()">
<input placeholder="Your message" ng-model="message">
<input type="submit" name="send" id="send" value="Send" class="btn btn-success">
<br/>
{{message}} //This updates the value as i type in textbox
</form>
</ion-content>
</body>
but when i see print that model on console it shows undefined when i define at the start in controller then it shows empty value
Controller.js
var farmAppControllers = angular.module('farmAppControllers',[]);
farmAppControllers.controller('farmAppController',['$scope','socket',function($scope,socket){
$scope.messages = [];
$scope.message = ''; //When i don't declare then console shows undefined on sendMessage function if declared then empty
socket.on("update", function (data) {
console.log(data);
$scope.messages.push(data);
});
$scope.sendMessage = function (){
console.log($scope);
console.log($scope.message); // This shows undefined or empty on console
socket.emit("msg",$scope.message);
$scope.messages.push($scope.message);
$scope.message ='';
};
}]);
My app.js
'use strict';
var farmApp = angular.module('farmApp', ['farmAppControllers','farmAppServices','ionic']);
And services.js for socket wrapper
var farmAppServices = angular.module('farmAppServices',[]);
farmAppServices.factory("socket",function($rootScope){
var socket = io.connect();
return {
on:function (eventName,callBack){
socket.on(eventName,function(){
var args = arguments;
$rootScope.$apply(function(){
callBack.apply(socket,args);
});
});
},
emit:function(eventName,data,callBack){
socket.emit(eventName,data,function(){
var args = arguments;
$rootScope.$apply(function(){
if(callBack){
callBack.apply(socket,args);
}
});
});
}
};
});
i stuck here... i try to google it but not able to solve it. In case of confusion feel free to comment. Any help would be great.
UPDATE
When i used the first answer of this question the problem got solved but still not clear why ng-submit is not working ? Any Idea Why ? Because it seems i am still unable to update the view scope from the controller ?
I think the problem is <ion-content> which is a directive and seems to create its own scope. From the docs:
Be aware that this directive gets its own child scope. If you do not understand why this is important, you can read https://docs.angularjs.org/guide/scope.
Therefore your message property isn't in the scope of your controller.
Objects are passed by reference and sendMessage is a function respectively an object, thats why it's still called correctly from the child scope.
What you should do is create an object with a name that makes sense to "package" the properties you want to share.
$scope.package = {}
$scope.package.messages = [];
$scope.package.message = 'You default message...';
And then in your function:
$scope.package.messages.push($scope.package.message);
And in your template:
<input placeholder="Your message" ng-model="package.message">
Here is a plunker with a working solution. It throws some random errors, I actually don't know ionic. But the example works and everything else doesn't matter.

Modifying the $scope in a callback within a controller function

Here is my Angular.js controller function code:
myControllers.controller("TreeDataController", ["$scope", function ($scope) {
$scope.greeting = "TreeDataController";
treeDataSource.getData("123", function (rootNode) {
// this is the callback when the AJAX operation succeeds, rootNode is a JSON object which contains hierarchical data.
$scope.tree = rootNode;
console.log("Got rootNode.");
$scope.foobar = "baz";
}, function (e) { alert(e); });
}]);
Here is my View:
<h2>Queries</h2>
{{greeting}}
{{foobar}}
<script type="text/ng-template" id="treeItemRenderer.html">
{{node.name}}
<ul>
<li ng-repeat="node in node.value" ng-include="''treeItemRenderer.html''"></li>
</ul>
</script>
<ul>
<li ng-repeat="node in tree" ng-include="''treeItemRenderer.html''">
{{node.name}}
</li>
</ul>
When I run this page in Chrome, the page displays "TreeDataController" in the {{greeting}} placeholder, and the console reports it got the data (see my console.log call), however the tree data is never displayed. I thought it was a problem with my recursive-template so I added $scope.foobar = "baz"; and {{foobar}}, however that isn't populated either.
This is a common error you can get when dealing with Angular.
Because you're updating the $scope outside of a scope apply, you aren't actually causing Angular to do any template rerendering.
This is fairly simple to fix, wrap any $scope calls within $scope.$apply, see the following:
treeDataSource.getData("123", function (rootNode) {
$scope.$apply(function () {
$scope.tree = rootNode;
console.log("Got rootNode.");
$scope.foobar = "baz";
});
}, function (e) { alert(e); });

Resources