AngularJS - model changes not updating the view - javascript

AngularJS Classic: Im changing my Model on ng-click an the view is not updating. I thought a simply $scope.$apply() would update but i'm not getting it working. I suppose I'm not setting $apply() in the right place or something like that.
Example: Plunker
The name of the Object is changed on Press button.

Just update your explorerObject one more time after clicking, cause it is still pointing on your previous object:
$scope.click = function () {
ExplorerService.setExplorerObject({
selected : {
name: 'Defined Now !',
id: 'id',
timestamp: 'timestamp'
},
templateURL: 'views/beispiel.html'
});
$scope.explorerObject = ExplorerService.getExplorerObject(); // <--- here
}
Working: http://plnkr.co/edit/UWE7o3mtAzY3xzYRWfkf?p=preview
After question' edit:
You can use $watch in your second controller:
app.controller('SecondCtrl', function($scope, ExplorerService) {
$scope.$watch(function(){
return ExplorerService.getExplorerObject();
},function(n,o){
$scope.explorerObject = ExplorerService.getExplorerObject();
},true)
});
Working: http://plnkr.co/edit/8mZO5kZmTrqwHKnxtBSd?p=preview
Or use a $broadcast approach, like:
app.controller('SecondCtrl', function($scope, ExplorerService) {
$scope.explorerObject = ExplorerService.getExplorerObject();
$scope.$on("EXPLORER_OBJECT_CHANGED" ,function(event,obj){
$scope.explorerObject = obj;
});
});
And in your service add: $rootScope.$broadcast("EXPLORER_OBJECT_CHANGED", explorerObject);
Example: http://plnkr.co/edit/9WKypJTb0wViQZ01m9TN?p=preview

Related

OnChange of a Component binding in Angular Js

I have created a component in angular js which shows some data
But on change of my customer binding i want to call some service in component controller which is not reflecting,Here is the below code
In my Test1.html
<tab-customer title="Test Comp" customerCode="vm.customerCode" functioname="fnTest"></tab-customer>
vm.CustomerCode is changing on basis of dropdown so its changing
app.comp.TestComp is already defined
angular
.module('app.comp.TestComp')
.component('tabCustomer', {
templateUrl: 'Component.html',
controller: tabCompCustomer,
controllerAs: 'vm',
bindings: {
title: '#',
functioname: '#',
customerCode: '<'
}
});
function tabCompCustomer(tableCustomerService, $rootScope,$scope, $filter) {
var vm = this;
$scope.$watch('vm.customerCode', function (newValue, oldValue) {
console.log('got1')
console.log(newValue) // This is also not working
});
$onChanges = function (changes) {
console.log('got')
console.log(changes) // This is also not working
};
};
On Change of a Customer Code i need to fire my component again but i am not getting any fire event can anyone help
If the question is still unclear comment i will detailed it
You should be changing your attribute binding from customerCode to customer-code, So that will pass vm.customerCode to component. And remove $watch function from component controller, as $onChanges function will get fire automatically on each bindings change.
customerCode="vm.customerCode"
should be
customer-code="vm.customerCode"
Note: make $onChanges = to this.$onChanges =

angular router does not select child state (nested states)

I'm having a problem with a new state I added to our web site.
Short description
I have a state (/page)'q.explorer' which contains a button; when clicked it should go to a child state (named 'q.explorer.detail') but it does not. However: in the logging I see that it does try to go to that (/state) and the new url is formatted as defined in the child state.
But still the template and controller that are actually used is the 'parent' which contains the button ...
This may be a little confusing to explain; so I have also added some code in the hope that this will clarify my problem.
The setup looks like this:
$stateProvider
.state('q', {
url: '/:locale/app',
data : { ui: "V2" },
views: {
'application' : {template: '<div ui-view=""><page-home-v2></page-home-v2></div>' }
}, controller: function($scope, $stateParams, siteNavigation) {
siteNavigation.applyUrlParameters($stateParams);
}
}).state('q.explorer', {
url: '/explorer?:year&:month&:guide',
template: '<page-explorer-v2></page-explorer-v2>',
controller: function($scope, $stateParams, siteNavigation) {
console.log("controller: qlaro.explorer");
siteNavigation.applyUrlParameters($stateParams);
}
}).state('q.explorer.detail', {
url: '/detail',
template: '<page-explorer-detail-v2></page-explorer-detail-v2>',
controller: function($scope, $stateParams, siteNavigation) {
console.log("controller: qlaro.explorer.detail");
siteNavigation.applyUrlParameters($stateParams);
}
})
angular
.module('q.components')
.service('siteNavigation', function($state, $location) {
var service = this;
service.applyUrlParameters = function($stateParams) {
if (!$stateParams) $stateParams = $state.params;
console.log('Apply state parameters for state: ' + $state.current.name);
console.log('URL >> ' + $location.url());
};
};
Somewhere deep in the template of "q.explorer" there is a button to open the detail view ("q.explorer.detail"). It uses this code:
function goToDetail() {
var ui = $state.current.data.ui;
if (ui === "V1") { /*...*/ }
else if (ui === "V2") {
var params = {
year: Store.getYear(),
month: Store.getMonth(),
locale: Store.getLocale()
}
var guide = Store.getActiveSidebarItem();
if (guide) params.guide = guide.slug;
console.log("go to explorer: " + params.guide);
$state.go('q.explorer.detail', params);
}
else console.log("Unable to go to state because the ui version is not known.");
}
And this is what I see in the console after clicking the link:
go to explorer: ebit
controller: q.explorer
Apply state parameters for state: q.explorer.detail
URL >> /nl/app/explorer/detail?year=2015&month=11&guide=ebit
As you can see, it uses the controller of the 'parent' iso the child page I want to open. Even though $state.current.name is correct ... Or maybe I should say it does not change from state ...
Any help is welcome.
(PS: We are using Angular 1.4.9)
Looks like you are using nested states such that q.explorer.detail is a child of q.explorer. To render the child state's template, you also need a specific ui-view where it can be placed into. And this will be searched in the template of the parent state. Getting the console.log() output just means the controllers are instantiated, but that even happens if the template isn't rendered at all.
So check if you have an ui-view in the template of the q.explorer state. For more details, please see: https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views
You could also fix this by not making q.explorer.detail a child of q.explorer. A child state is created as soon as you need the dot notation.
Yoy have to add somewhere in 'q.explorer' state's template entry point for nested view 'q.explorer.detail', otherwise child controller will not be called.
For example:
template: '<page-explorer-v2></page-explorer-v2><ui-view></ui-view>',
instead of
template: '<page-explorer-v2></page-explorer-v2>'
See jsfiddle: http://jsfiddle.net/jcpmsuxj/42/
Upd. As #ajaegle mentioned you should to visit official docs page:
https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views

clear form in isolated scope?

I have a text input for comments in an ionic app and, on form submission, need that to clear.
I have the form inside an ionic modal so I am assuming I am falling victim to an isolated scope, however, the form still needs to be cleared.. here is my controller (I have marked where I am trying to clear the form):
.controller('EasternInnerCtrl', function ($http, $timeout, $scope, $ionicLoading, $stateParams, $ionicScrollDelegate, $cordovaSocialSharing, $ionicModal, Easternc, AuthService) {
$scope.eastc = Easternc.get($stateParams.eastcId);
$ionicModal.fromTemplateUrl('commenter.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal
})
$scope.openModal = function() {
$scope.modal.show();
}
$scope.closeModal = function() {
$scope.modal.hide();
};
$scope.$on('$destroy', function() {
$scope.modal.remove();
});
$scope.addComment = function(new_comment, comments){
Easternc.submitComment($scope.eastc.id, new_comment, comments).then(function(data){
if(data.status=="ok"){
var user = AuthService.getUser();
var comment = {
author: {name: user.data.username},
content: new_comment,
date: Date.now(),
user_gravatar : user.avatar,
id: data.comment_id
};
$scope.eastc.comments.push(comment);
$scope.new_comment = ""; // HERE IS WHERE I AM TRYING TO CLEAR THE FORM
$scope.new_comment_id = data.comment_id;
}
});
};
})
Your suspicion about scope issues is correct although in this case it's because of a child scope not isolated scope.
The easiest (and best) way to fix this is by using the "dot" notation so that the property that you are trying to access and clear will be the same one in the form and in your controller.
So instead of using $scope.new_comment you should create an object and then use that object.
For instance in your controller first set up the values:
$scope.new_comment_form = {
content: ""
};
Then in your form html you can use
<input type="text" ng-model="new_comment_form.content">
And finally when you want to clear it, just clear the value inside the object:
$scope.eastc.comments.push(comment);
// clear like this
$scope.new_comment_form.content = "";
$scope.new_comment_id = data.comment_id;
Here I created a sample working plnkr using the same code outline that you have above. It loads the modal from a new file and after you do addComment it "saves" the comment and clears the form: http://plnkr.co/edit/s6CdQN?p=preview
For more information about using the dot notation and why you should use it, see these links:
https://github.com/angular/angular.js/wiki/Understanding-Scopes
Why don't the AngularJS docs use a dot in the model directive?
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
Add $scope.$apply(); as well.
We need to $apply the changes in $scope as Easternc.submitComment() is not angular function and hence any changes inside this function is not calling the angular digest cycle to apply changes.
.controller('EasternInnerCtrl', function ($http, $timeout, $scope, $ionicLoading, $stateParams, $ionicScrollDelegate, $cordovaSocialSharing, $ionicModal, Easternc, AuthService) {
$scope.eastc = Easternc.get($stateParams.eastcId);
$ionicModal.fromTemplateUrl('commenter.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal
})
$scope.openModal = function() {
$scope.modal.show();
}
$scope.closeModal = function() {
$scope.modal.hide();
};
$scope.$on('$destroy', function() {
$scope.modal.remove();
});
$scope.addComment = function(new_comment, comments){
Easternc.submitComment($scope.eastc.id, new_comment, comments).then(function(data){
if(data.status=="ok"){
var user = AuthService.getUser();
var comment = {
author: {name: user.data.username},
content: new_comment,
date: Date.now(),
user_gravatar : user.avatar,
id: data.comment_id
};
$scope.eastc.comments.push(comment);
$scope.new_comment = ""; // HERE IS WHERE I AM TRYING TO CLEAR THE FORM
$scope.new_comment_id = data.comment_id;
$scope.$apply(); // Here changes are applied to $scope.
}
});
};
})

AngularJS: Bind angular service property to scope

Scenario
I have a service UserService that maintains the (boolean) sessionStatus of the user.
The view conditionally shows [LOGOUT] on ng-show=sessionStatus (i.e. if not logged in (false), no show).
sessionStatus of ViewController should therefore always match that of UserService ... right?
If you click [LOGOUT] when it's visible, it does some loggy outty things, sessionStatus value changes, and view should update with new outcome of ng-show....
Problem
Currently, clicking logout does not seem to update var UserService.sessionStatus?
How do I keep $scope.sessionStatus updated when UserService.logout() occurs and changes UserService.sessionStatus?
How do I map the changing $scope to ng-show?
Files
View
<a ng-show="sessionStatus" ng-click="logout()">Logout</a>
ViewController
app.controller('AppController', function($scope, $interval, $http, UserService) {
$scope.logout = function() { UserService.logout(); }
// This ain't working
$scope.$watch(UserService.sessionStatus, function() {
$scope.sessionStatus = UserService.sessionStatus;
});
});
UserService
NB: appUser is an injected global var in the HTML head (a hacky fix until I get session/cookie stuff working properly)
app.factory('UserService', function($http) {
var pre;
var sessionStatus;
function init() { // Logged in : Logged out
pre = appUser.user != undefined ? appUser.user : { name: 'Logged out', uid: '0' };
sessionStatus = pre.uid != "0" ? true : false;
}
function resetSession() { appUser = null; init(); }
init();
return {
sessionStatus: function() { return sessionStatus; }, // update on change!
logout: function() {
$http.get("/logout").then(function (data, status, headers, config) {
resetSession();
})
}
};
});
Instead of a watch, simply use a scoped function that returns the session status from the service.
$scope.sessionStatus = function() {
return userService.sessionStatus();
};
Your Logout link would look as below:
<a ng-show="sessionStatus()" ng-click="logout()">Logout</a>
A stripped down Plunker for your functionality: http://plnkr.co/edit/u9mjQvdsvuSYTKMEfUwR?p=preview
Using a scoped function is cleaner and is the "correct" way to do it. Yet, for the sake of completeness you could also have fixed your watch:
$scope.$watch(function () {
return UserService.sessionStatus;
}, function() {
$scope.sessionStatus = UserService.sessionStatus;
});
The first argument of the $watch method takes a WatchExpression which can be a string or a method.
But again, $watch should not be used in controllers. Using scoped methods as suggested are cleaner and easier to test.

ui-router: open modal and pass parent scope parameters to it

I am using this FAQ entry to open a modal dialog in a child state of a certain state: https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state
My code is below. When I open a modal dialog, I need to get access to the properties of the parent state's scope. Is this possible?
plnkr: http://plnkr.co/edit/knY87n
.state('edit', {
url: '/{id:[0-9a-f]+}',
views: {
'#': {
templateUrl: 'views/edit.html',
controller: 'editContr'
}
}
})
.state('edit.item', {
url: "/item/new",
onEnter: function($stateParams, $state, $modal) {
$modal.open({
controller: 'itemEditContr',
templateUrl: 'views/edit-item.html',
}).result.then(function (item) {
//
// here I need to insert item into the items
// seen by my parent state. how?
//
$state.go('^');
}, function () {
$state.go('^');
});
}
});
function editContr($scope) {
$scope.items = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
}
function itemEditContr($scope, $modalInstance) {
$scope.submit = function () {
$modalInstance.close($scope.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.item = {name: 'test'};
}
Are you planning to update params set by the parent? You should be able to get the parent state using
$state.$current.parent
For e.g. the name of the parent state will be $state.$current.parent.name
EDIT: Updating since the OP wanted to access the Scope and not the parent state.
You can emit an event from the child and then capture it in the parent.
Untested code:
In Parent:
$scope.$on('ADD_ITEM', function(evt, msg) {
$scope.items.add(msg);
}
In the child state:
$scope.$emit('ADD_ITEM', item);
long story short, yes it is. Reading the angularjs developer guide for scopes, is actually one of their more helpful and very well documented pieces: http://docs.angularjs.org/guide/scope
Aside from that, scope in angular is no different than scope with any javascript object.
You've got one or two things that you're not doing correctly. For one, passing item in your onEnter function won't help unless you're grabbing something from the url as an identifier, or resloving some data that you can inject into the states controller. You're trying to do the latter, but you aren't resolving anything, so you are getting undefined on item.
One trick you can use is to set a truthy value in your your parent, and access it.
//in parent ctrl
$scope.newItem = function(itemname){
return {name:itemname}
}
$scope.save = function(item){
$scope.items.push(item);
}
Then when you open your modal call $scope.getItem() within the controller instead of injecting item into the controller directly
function itemEditContr($scope, $modalInstance) {
$scope.submit = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.item = $scope.newItem('test') //this will look for newItem funciton in the local scope, fail to find it, and walk up the heirarchy until it has found newItem() in the parent
//now a save function, also defined on the parent scope
$scope.save($scope.item);
}
Accessing the parent scope is nothing special, just make sure to get a value, and not overwrite it. so you can access $scope.items from the child controller by assigning it to a variable, or you can push it new values, but never set it, or you will create a new local items object on the child scope instead.
I struggled with this too and found an alternative solution. Instead of using the onEnter function you can use a controller with a very simple view that calls a function to open the modal. I used a
config(['$stateProvider', function($stateProvider) {
// Now set up the states
$stateProvider
.state('exmple.modal', {
url: "/example/modal/",
controller: "ExampleController",
template: '<div ng-init="openModal()"></div>'
});
}]);
In the example controller you an put the openModal() function to open the modal where you have a scope.

Categories

Resources