How to make saved object immediately appear in a ng-repeat? - javascript

JS:
$scope.addPano = function () {
var Pano = AV.Object.extend("Panorama"),
pano = new Pano()
var json = {
'name': 'test3',
'index': 0,
'Type': 'real',
'version': 0,
'buildingCode': $scope.buildingId
}
pano.save(json, {
success: function(object) {
console.log('PANO: ', object)
$scope.building.pano.push(json)
$scope.$digest()
},
error: function(object, error) {
console.log('Failed to create new object, with error message: ' + error.message);
}
})
}
HTML:
<div ng-repeat="pano in building.pano">
<p><strong>{{pano.name}}</strong></p>
<div ng-repeat="panodata in pano.panoData">
<p>{{panodata.name}}</p>
</div> <a class="btn btn-default" href="javascript:;" ng-click="addPanodata(pano.objectId)">Add panodata</a> </div> <a class="btn btn-default btn-lg" href="javascript:;" ng-click="addPano()">Add pano</a>
</div>
Right now the only way to make it appear is with:
$scope.building.pano.push(json)
$scope.$digest()
I thought Angular did this automatically?

I think you can try this type But In your code either you reload data after save method calling successfully then you can easily appear easily..
function myctrl($scope){
$scope.data =[{name:'abc'}];
$scope.pushdata = function(name){
$scope.data.push({name});
$scope.name ='';
}
}
<html ng-app=''>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
</head>
<body ng-controller='myctrl'>
<input type='text' name='name' ng-model='name'/>
<button name='clickme' type=submit ng-click='pushdata(name)'>save</button>
<div ng-repeat='obj in data'>
{{obj.name}}
</div>
</body>
</html>

The official way to do this is to write your pano service to support angular. So where you call the success callback in the service:
.factory('pano', function() {
return {
save: function(x, options) {
setTimeout(function() {
options.success(123);
}, 1000);
}
};
});
You should wrap it in a $scope.$apply so angular has an opportunity to run dirty-checking:
.factory('pano', function($rootScope) {
return {
save: function(x, options) {
setTimeout(function() {
$rootScope.$apply(function() {
options.success(123);
});
}, 1000);
}
};
});
The same applies for the error handling callback.
Note: The builtin angular services, like $http, $q, $timeout do this automatically.

Related

Angular click event not working, what gives?

I'm building a silly little calculator in an attempt to learn AngularJS. I'm attempting to use ng-click trigger events to update the "screen" of my calculator. Here's my code:
var calcApp = angular.module('NodeCalc', []);
calcApp.controller('CalcController', ['$scope', function ($scope) {
$scope.memory = {
recall: function() {
console.log('memory recall');
},
clear: function() {
console.log('memory clear');
},
add: function(value) {
console.log('memory add');
}
}
$scope.buttons = {
memory: [
{text: 'mrc', action: $scope.memory.recall},
{text: 'm-', action: $scope.memory.clear},
{text: 'm+', action: $scope.memory.add},
]
};
}]);
<body ng-app="NodeCalc" ng-controller="CalcController">
<form class="calc">
<p class="calc-display">
<input type="text" name="res" id="res" value="0" class="calc-display-input" onfocus="this.blur()">
</p>
<p class="calc-row">
<button ng-repeat="button in buttons.memory" type="button" class="calc-button calc-button-gray" ng-click="{{button.action}}">{{button.text}}</button>
<button type="button" class="calc-button calc-button-red calc-button-big" onclick="a('/')">/</button>
</p>
So, I know it has to do with the way I'm attaching my memory functions to my scope object. Can I not use scope in this way on the ng-click directive? I'm not really sure how else to achieve my goal here. The buttons render correctly, but I get a huge angular error-barf in my console related to:
Error: [$parse:syntax] http://errors.angularjs.org/1.5.8/$parse/syntax?p0=%7B&p1=invalid%20key&p2=2&p3=%7B%7Bbutton.action%7D%7D&p4=%7Bbutton.action%7D%7D
ng-click expression should be ng-click="button.action()"
While using ng-* directive, one is not suppose to wrap it with mustache, passed expression will be parsed by angular
var calcApp = angular.module('NodeCalc', []);
calcApp.controller('CalcController', ['$scope',
function($scope) {
$scope.memory = {
recall: function() {
console.log('memory recall');
},
clear: function() {
console.log('memory clear');
},
add: function(value) {
console.log('memory add');
}
}
$scope.buttons = {
memory: [{
text: 'mrc',
action: $scope.memory.recall
}, {
text: 'm-',
action: $scope.memory.clear
}, {
text: 'm+',
action: $scope.memory.add
}, ]
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="NodeCalc" ng-controller="CalcController">
<form class="calc">
<p class="calc-display">
<input type="text" name="res" id="res" value="0" class="calc-display-input" onfocus="this.blur()">
</p>
<p class="calc-row">
<button ng-repeat="button in buttons.memory" type="button" class="calc-button calc-button-gray" ng-click="button.action()">{{button.text}}</button>
<button type="button" class="calc-button calc-button-red calc-button-big" onclick="alert('/')">/</button>
</p>
One idea is just calling a function defined on $scope in the ng-click (the normal way) and passing the $index from the ng-repeat.
ng-click="clicked($index)"
$scope.clicked = function(index) {
var fn = $scope.buttons.memory[index].action;
fn();
}

Angular uibModal, Resolve, Unknown Provider

I am trying to expose a "generic" modal - using Angular's $uibModal - through a service. Here is the definition of that service:
angular.module('app').service('CustomModalService', ['$uibModal', function ($uibModal) {
var openCustomModal = function (size, title, message) {
var actionToPerformOnConfirm = action;
var modalInstance = $uibModal.open({
templateUrl : 'templates/CustomModal.html',
size: size,
resolve: {
title: title,
message: message
}
});
};
return {
openCustomModal: openCustomModal
};
}]);
Nothing too complicated, above. However, it is not working. If I remove the resolve property from the object, the service works; however, if I include the resolve property, I get the Unknown Provider error originating from that property.
The documentation for the resolve property reads:
(Type: Object) - Members that will be resolved and passed to the
controller as locals; it is equivalent of the resolve property in the
router.
The objective is to be able to provide a template for the modal that utilizes these properties in its DOM, e.g. :
<div ng-controller="CustomModalController">
<div class="modal-header">
<h3 class="modal-title">{{title}}</h3>
</div>
<div class="modal-body">
{{message}}
</div>
<div class="modal-footer">
<button class="ad-button ad-blue" type="button" ng-click="confirmAction()"></button>
<button class="ad-button ad-blue" type="button" ng-click="cancelAction()"></button>
</div>
</div>
What am I missing that is causing this error to be thrown?
You have two problems:
You need to define the controller in your modal config
Your resolve object needs to be a map of string: function, where string is the name of the dependency that will be injected into your modal's controller, and function is a factory function that will be used to provide that dependency when the controller is instantiated.
Working example: JSFiddle
JavaScript
angular.module('myApp', ['ui.bootstrap'])
.controller('MyModalController', MyModalController)
.directive('modalTrigger', modalTriggerDirective)
.factory('$myModal', myModalFactory)
;
function MyModalController($uibModalInstance, items) {
var vm = this;
vm.content = items;
vm.confirm = $uibModalInstance.close;
vm.cancel = $uibModalInstance.dismiss;
};
function modalTriggerDirective($myModal) {
function postLink(scope, iElement, iAttrs) {
function onClick() {
var size = scope.$eval(iAttrs.size) || 'lg'; // default to large size
var title = scope.$eval(iAttrs.title) || 'Default Title';
var message = scope.$eval(iAttrs.message) || 'Default Message';
$myModal.open(size, title, message);
}
iElement.on('click', onClick);
scope.$on('$destroy', function() {
iElement.off('click', onClick);
});
}
return {
link: postLink
};
}
function myModalFactory($uibModal) {
var open = function (size, title, message) {
return $uibModal.open({
controller: 'MyModalController',
controllerAs: 'vm',
templateUrl : 'templates/CustomModal.html',
size: size,
resolve: {
items: function() {
return {
title: title,
message: message
};
}
}
});
};
return {
open: open
};
}
HTML
<script type="text/ng-template" id="templates/CustomModal.html">
<div class="modal-header">
<h3 class="modal-title">{{vm.content.title}}</h3>
</div>
<div class="modal-body">
{{vm.content.message}}
</div>
<div class="modal-footer">
<button class="ad-button ad-blue" type="button" ng-click="vm.confirm()">
confirm
</button>
<button class="ad-button ad-blue" type="button" ng-click="vm.cancel()">
cancel
</button>
</div>
</script>
<button modal-trigger size="'sm'" title="'Hello World!'" message="'This is a test'">
Click Me
</button>

Call 2 functions in single ng-click

I am calling two functions on ng-click. But it doesn't work. I am not sure why the Refresh1() is not called when I cross-checked through debugger.
HTML CODE
<div class="row" ng-controller="PublishManifestCtrl">
<div class="col-xs-12 col-md-12">
<div class="widget">
<div class="widget-header bordered-bottom bordered-themeprimary">
<i class="widget-icon fa fa-tasks themeprimary"></i>
<span class="widget-caption themeprimary">Manifest Status</span>
</div>
<div class="widget-body">
<form class="form-bordered" role="form">
<div class="form-group">
<label style="padding-left: 8px;">Manifest was last published to agents on <b>{{manifeststatus.manifestLastPublishedDate}}</b>.</label>
</div>
<div class="form-group">
<label style="padding-left: 8px;">Manifest was last updated by <b> {{manifeststatus.lastUpdatedByUser}} </b> on <b>{{manifeststatus.manifestLastedUpdatedDate}}</b>.</label>
</div>
<div class="form-group">
<div class="col-sm-offset-1">
**<button id="PublishButton" class="btn btn-default shiny " ng-disabled="manifeststatus.enablePublishButton" ng-click="Save(manifeststatus);Refresh1()">Publish</button>**
</div>
<br/>
<div id="statusDivPublish" ng-show="showstatus">
<alert type="{{alert.type}}">{{alert.msg}}</alert>
</div>
</div>
</form>
</div>
JSFILE
$scope.Save = function (data) {
debugger;
$http.post($rootScope.WebApiURL + '/updatemanifeststatus');
//$http.get({ url: $rootScope.WebApiURL + '/getmanifeststatus' });
$scope.manifeststatus = data;
$scope.showstatus = true;
$scope.alert = { type: 'success', msg: 'Published Successfully.' };
$(".statusDivPublish").show();
}
$scope.Refresh1 = function () {
//refresh
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
}
});
new code
$scope.Save = function (data) {
debugger;
$http.post($rootScope.WebApiURL + '/updatemanifeststatus');
//$http.get({ url: $rootScope.WebApiURL + '/getmanifeststatus' });
$scope.manifeststatus = data;
$scope.showstatus = true;
$scope.alert = { type: 'success', msg: 'Published Successfully.' };
$(".statusDivPublish").show();
$scope.Refresh1();
}
$scope.Refresh1 = function ($rootScope, $state, $stateParams) {
debugger;
return {
restrict: 'AC',
link: function (scope, el, attr) {
el.on('click', function () {
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
});
}
};
};
});
The first one updates and displays a successfull message, while the second function refreshes the page.
use this
$scope.Save = function (data) {
debugger;
$http.post($rootScope.WebApiURL + '/updatemanifeststatus');
//$http.get({ url: $rootScope.WebApiURL + '/getmanifeststatus' });
$scope.manifeststatus = data;
$scope.showstatus = true;
$scope.alert = { type: 'success', msg: 'Published Successfully.' };
$(".statusDivPublish").show();
$scope.refresh();
}
call refresh inside the first function and remove it from the ng-click.
Update
You have a different type of problem i had it too. you try to refresh a state inside a method, it's really difficult i solve that problem with this snippet
if($state.current.name == /*name of the current state*/) {
$state.go($state.current, {}, {reload: true});
$modalInstance.close();
}
else {
$modalInstance.close();
$state.go(/*name of the current state*/);
}
it's not difficult but it didn't behave like you have understand it.
UPDATE
taking your code
$scope.Refresh1 = function () {
//refresh
$state.go($state.current, {}, {reload: true});
}
What about calling refresh inside of save in $http handler ?
Like this:
$http.post($rootScope.WebApiURL + '/updatemanifeststatus')
.then(function(){
$scope.Refresh1();
});
Don't execute two function in one ng-click, instead add the Refresh1 call to the end of the Save call, like so.
HTML
<button id="PublishButton"
class="btn btn-default shiny "
ng-disabled="manifeststatus.enablePublishButton"
ng-click="Save(manifeststatus)">Publish</button>
JS
$scope.Save = function (data) {
debugger;
$http.post($rootScope.WebApiURL + '/updatemanifeststatus');
//$http.get({ url: $rootScope.WebApiURL + '/getmanifeststatus' });
$scope.manifeststatus = data;
$scope.showstatus = true;
$scope.alert = { type: 'success', msg: 'Published Successfully.' };
$(".statusDivPublish").show();
$scope.refresh();
}
Update
If you are using AngularJS V1.2.2 or higher, then using ui-router, the following should work to reload the data.
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
The shortest way to accomplish this though would be with:
$state.go($state.current, {}, {reload: true}); //second parameter is for $stateParams
Its also worth noting that none of these will actually reload the page. If you want to reload the state AND the page, there is no ui-routermethod for it. Do window.location.reload(true)
Update 2
If you are receiving:
$state is not defined at Scope.$scope.Refresh1
(publishmanifest.js:44) at Scope.$scope.Save (publishmanifest.js:37)
at $parseFunctionCall (angular.js:12345) at angular-touch.js:472 at
Scope.$eval (angular.js:14401) at Scope.$apply (angular.js:14500) at
HTMLButtonElement. (angular-touch.js:471) at
HTMLButtonElement.n.event.dispatch (jquery.min.js:3) at
HTMLButtonElement.r.handle (jquery.min.js:3)
You are not injecting the $state service in your controller. You must do this in order to use it.
//without annotation (inferred, not safe when minifying code)
function Controller($scope, $state) {...}
//inline annotation
module.controller('Controller', ['$scope','$state', function($scope, $state) {...}]);
//$inject property annotation
function Controller($scope, $state) {...}
Controller.$inject = ['$scope', '$state'];
Pick one of the methods above to setup your controller to use $state.
Just make a third function like:
function3(data) {
save(data);
refresh1();
}

Angular JS, how to pass URL parameters to $resouce in angularJS?

Hi I have a simple controller where it passes unique integers into my url, but Im running to many issues. I need to change this "4401" dynamically from my controller.
the url Im trying to reach:
https://itunes.apple.com/us/rss/topmovies/limit=50/genre=4401/json
app.factory('classic',function ($resource) {
return $resource('https://itunes.apple.com/us/rss/topmovies/limit=50/genre=:id/json', {
get: {
method: 'JSONP',
id: '#id'
}
});
});
and here is my controller
app.controller('TestCrtl', function ($scope, classic) {
init();
function init(id) {
$scope.movies = classic.get(id);
}
$scope.classicMovies = function(){
var id = "4403";
init(id);
}
$scope.anctionMovies = function(){
var id = "4404";
init(id);
}
});
The error Im getting
Uncaught SyntaxError: Unexpected token :
any help would be highly appreciated.
<div class="col-sm-4">
<button type="button" data-ng-click="actionMovies()" class="btn btn-default">Action</button>
</div>
<div class="col-sm-4">
<button type="button" class="btn btn-default">Scary</button>
</div>
I believe this is the correct way to implement parameters when using a resource factory:
app.factory('movieService',function ($resource) {
return $resource('https://itunes.apple.com/us/rss/topmovies/limit=50/genre=:id/json', {id: '#id'}, {
query: { method: 'GET', isArray:true, params: {id: '#id'} }
});
});
This can be simplified to:
app.factory('movieService',function ($resource) {
return $resource('https://itunes.apple.com/us/rss/topmovies/limit=50/genre=:id/json', {id: '#id'});
});
To call this get method you would need to do the following. Note the parameters that are used in the get method.
app.controller('TestCrtl', function ($scope, movieService) {
$scope.classicMovies = function(){
movieService.query({id: 4403}, function(result){
$scope.movies = result;
});
}
$scope.anctionMovies = function(){
movieService.query({id: 4404}, function(result){
$scope.movies = result;
});
}
});
Additionally, it should be noted that the resource method call is going to return a promise. You can either set it from the return value of the get method, like you did above (The status of the promise isn't guaranteed), or you can set it in the callback, which guarantees that the promise is resolved.
Try this:
app.factory('classic',function ($resource) {
return $resource('https://itunes.apple.com/us/rss/topmovies/limit=50/genre=:id/json', {
get: {
method: 'JSONP',
id: '#id'
}
});
});
And in controller change to :
$scope.movies = classic.get(id);

Updating ng-show from within .then()

I have a loader that I show while an async service call is completed, and simply want to hide the loader when complete. Here is my controller:
app.controller('DataController',
function($scope, DataService) {
// UI state
$scope.loading = true;
DataService.getData({ "count": 10 }).then(function(data) {
$scope.data = data;
// UI state
$scope.loading = false; // does not update ng-view
$scope.$apply(function() { // generates error
$scope.loading = false;
});
});
});
And the view:
<div ng-controller="DataController">
<div id="container">
<div>
{{ loading }}
</div>
<div class="spinner large" ng-show="loading"></div>
<div class="data-container" ng-show="!loading">
...
</div>
</div>
</div>
Note the the {{ loading }} value gets updated properly in the view. Using the wrapping $scope.$apply() call resulted in an error:
Error: [$rootScope:inprog]
UPDATE
As this might be promise-related, here's the promise generating getData() method from the DataService factory:
getData: function(params) {
var deferred = $q.defer();
APIService.data(params).then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
And the last piece, the APIService.data() method:
data: function(params) {
var deferred = $q.defer();
$resource(endpoint + '/data/feed', {}, {
'query': {
method: 'POST',
headers: headers
}
}).query(params).$promise.then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
I would solve this by binding the show/hide directive to the data-property in the controller. It will be the same as false if the data is undefined.
<div class="spinner large" ng-hide="data"></div>
<div class="data-container" ng-show="data">
Try to use
$scope.$evalAsync(function() {
$scope.loading = false;
});
Found the issue - as this is in a Chrome Extension, I needed to include the Angular CSS CSP file, which includes the ng-hide class definition. Including that file resulted in the code working as expected. Thanks everyone for the help. More info:
https://docs.angularjs.org/api/ng/directive/ngCsp

Categories