Angular View not updating from a model change - angularjs

I have 2 modals that use the same controller. One has:
<div id="addToPlaylist" ng-controller="PlaylistModalCtrl">
<select name="playlist" ng-model="playlist" ng-options="playlist.id as playlist.name|rmExt for playlist in playlists">
</select>
The other has:
<div id="newPlaylist" ng-controller="PlaylistModalCtrl">
<button ng-click="createPlaylist(playlistName);">Save</button>
In my controller, I have:
angular.module('MyApp')
.controller('PlaylistModalCtrl', function ($scope) {
$scope.playlists = [];
$scope.updatePlaylists = function() {
getPlaylist.then(function (response) {
$scope.playlists = response.data.data;
$scope.$$phase || $scope.$apply();
});
}
$scope.createPlaylist = function(playlist_name) {
addPlaylist(playlist_name).then(function(response) {
$("#newPlaylist").modal('hide');
});
}
$scope.updatePlaylists();
});
So one would expect that my first view would have an updated "playlists" in the dropdown, but this isn't the case. So how can I get that view to be updated?

You don't seem to understand how scoping works. Here is a plunkr illustrating that two different controllers have different scopes and stuff.
http://plnkr.co/edit/LqLuLvVkE9ltzcJH6XdN?p=preview
As you can clearly see, expecting two controllers to update the same variable would not work because each of them has the variable in its own isolated scope. What you can do to battle that is to implement a pubsub service that listens to some changes for some properties, use emit and broadcast angular functions, or, the best, rethink why you would need the same controller twice in your app and redesign it.

Related

Why does not work two controller in Angular JS?

I have controller is named "UserController" in top of page:
<div ng-controller="UserController"><input type="text" ng-model="search"></div>
Also the same controller in bottom page from directive ng-view:
<div class="bottom" ng-controller="UserController">{{search}}</div>
Why I dont get value {{search}} in bottom part, when I fill field input in top?
Can I use one controller two times in a page?
Yes, you can use two controllers in AngularJs, Here is a demo.
What happens when I use ng-controller?
When you add ng-controller to a DOM element, angular create an instance of controller function and attaches it with that DOM, and thats why there is no two way data-binding between those divs.
How can I use data binding to share data between controllers?
You can use $rootScope variable or you can use services.
you can create service and inject in controller as dependency, so you can access its property with two way binding feature.
As said by JB Nizet, you need to have everything in the same "div".
<div ng-controller="UserController">
<input type="text" ng-model="search">
<div id="search-query">{{search}}</div>
</div>
Having the search-query at the bottom of the page is a matter of CSS, not Angular.
Controllers are not singletons. You have one controller for the top div, a second controller for the second div. One scope for the top div, one scope for the bottom div.
Both controllers have the same name, but you are ultimatally calling you controller function twice.
Some options you might want to consider to solve your problem:
Option 1) Use parent scope.
ng-model="$parent.search"
{{$parent.search}}
Option 2) Use root scope.
ng-model="$root.search"
{{$root.search}}
Option 3) Store the value in a service.
Services are singletons. If you type myService.search = $scope.search, then that value can read from the other controller.
You wont be able to watch a service variable, so perhaps you want to use the observer pattern here.
app.service("search", function() {
var listerners = [];
this.register = function(listener) {
listerners.push(listener);
};
this.update = function(searchValue) {
for(var i in listerners) {
listerners[i](searchValue);
}
};
});
app.controller("UserController", function($timeout, search){
search.register(function(searchValue) {
$timeout(function(){
$scope.search = searchValue;
});
});
$scope.$watch('search', function (newVal, oldVal, scope) {
search.update(newVal);
});
});
Option 4) Broadcast the new value.
$scope.$watch('search', function (newVal, oldVal, scope) {
$rootScope.$broadcast('search', newVal);
});
$scope.$on('search', function(event, data) {
$scope.search = data;
});
You can have multiple instances of the same controller in your page. They share the same functionality. But every instance of that controller is getting his own $scope. So in your first controller $scope.search can be 'mySearch', but the second controller won't get this, because it's another $scope.
You can do two things:
You can put the controller on a containing element, let's say the body, so both your input and your div are within the same $scope.
OR, if you want them to be seperate, you can use a service to share the search.
Your HTML:
<div ng-app="myApp">
<div ng-controller="UserController">
<input type="text" ng-model="search.mySearch"/>
</div>
<div ng-controller="UserController">
{{search.mySearch}}
</div>
</div>
Your Javascript:
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
return { mySearch: '' };
});
myApp.controller('UserController', function( $scope, Data ){
$scope.search = Data;
});
See Fiddle

How to update scope variable using service

i have two controllers, the data in the first controller will be updated by click functions in the second controller. I am using service to communicate between controllers and not getting the expected result. whats wrong i am doing
link: http://jsbin.com/hozabigedu/1/
Well, you had several issues. First, I changed your data to be an object and to have a boolean as a property, rather than have itself be a boolean. This is good practice if you want to share info using a service. It's better that you share an object so that different controllers share the same reference. Primitives are problematic for that scenario. So here's the new service:
app.service('testservice', function(){
var data={}; //Changed here
getDataText = function() {
return data;
};
setDataText= function(val) {
data.text = val; //And changed here
return data;
};
return {
getDataText: getDataText,
setDataText: setDataText
};
});
Another issue is that your controller called the wrong function, it should be getDataText():
$scope.inputText = testservice.getDataText();
And lastly, you forgot to close the divs of the click elements, so the click 2 events bubbled up to the click 1 events thus showing right after hiding, so replace the 2nd controller div with this one:
<div ng-controller="secondController">
<div ng-click="showDiv();">click1</div>
<br/>
<br/>
<div ng-click="hideDiv();">click2</div>
</div>
</div>

Angular: update ng-include on CRUD event

I have a constant sidebar in my index.html file that lists projects using ng-include. When a project is created, or updated etc.. I would like the sidebar to automatically update along with it. I'm not sure which part of my code to provide, as hopefully it's a fundamental question that's easy to answer, though the solution eludes me.
Edit: feel I'm almost there, but src doesn't seem to pick up the controller property:
<div class="col col-md-4" data-ng-controller="ProjectsController" data-ng-include src="'{{sidebarUrl}}'"></div>
In my projects controller:
// Update existing Project
$scope.update = function() {
var project = $scope.project ;
project.$update(function() {
$location.path('projects/' + project._id);
$scope.$broadcast('projectUpdated');
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
$scope.sidebarUrl = 'modules/projects/views/list-projects.client.view.html';
$scope.$on("projectUpdated",function(event,args) {
$scope.sidebarUrl=null;
$scope.sidebarUrl= 'modules/projects/views/list-projects.client.view.html';
});
This is where services are your friend. You should start by encapsulating your CRUD operations into a service.
function MyCrudService($http, ...){ ... }
angular.module('my-app')
.service('myCrudService', MyCrudService);
Now, there are several ways you could implement the updating.
Use $rootScope and broadcast a message saying something has changed, and listen for that event in your sidebar controller (assuming you have one).
//Inside your service
function updateProject(proj){
//Update project
$rootScope.$broadcast('project-updated', proj);
}
//Inside your controller
function MySidebarController($scope){
$scope.$on('project-updated', function(){ ... });
}
Encapsulate the eventing logic inside your service to avoid using $rootScope. Just maintain your own list of callbacks and execute them.
//Inside your controller
function MySidebarController(myCrudService){
myCrudService.onProjectChanged(function(){ ... });
}
Expose the shared data on your service that can be databound to.
//Inside your controller
function MySidebarController($scope, myCrudService){
$scope.projects = myCrudService.projects;
}
Personally, I try to avoid $scope in my controllers, but using it for eventing is OK. Still, I might write some kind of directive that would allow me to execute an expression whenever an event fired in order to avoid it.
<my-event-binding event='project-updated' expression='sideBar.updateProjects()' />
Okay, so I had the same requirement(dynamically changing menu items in an included side panel) what I did was to use a controller in the ng-include template. The template would then fetch the relevant menu items from a service and update the controller. The view had an ng-repeat directive to show all the menu items (projects in your case).
<div ng-controller="ProjectsCtrl">
<ul>
<li ng-repeat="project in projects">
<a ng-href="project.url">
{{project.name}}
</a>
</li>
</ul>
</div>
The controller function could look something like:
function($scope, projectsSvc){
$scope.projects = [];
loadProjects();
$scope.$on("updatedProjects", loadProjects);
function loadProjects(){
projectsSvc.getProjects.success(function(projects){
$scope.projects = projects;
});
}
}
Projects are fetched from a service. When you update a project, broadcast an event that triggers a load of the projects again.
So after the new projects have been committed into the service backend, the sidebar will update accordingly.

ng-model not updating the value to object using templates

I am working on an MVC application using Angular. I need to open various bootstrap modal in one of the application pages. for that i just wrote a simple angular service to get the template for modal from a folder called templates and load at Run-time. Everything works fine except one thing. ng-model is not working for check box controls and DropDownList(select) items.
service to load template:
var defaultPath = "/app/services/dialog/templates/";
function _loadModalTemplate(templateName) {
var defer = $q.defer();
if (angular.isUndefined($templateCache.get(templateName))) {
return $http.get(defaultPath + templateName).then(function (data) {
$templateCache.put(templateName, data.data);
return defer.resolve();
});
} else {
return $.when($templateCache.get(templateName));
}
return defer.promise;
}
Controller
notebook.controller('createworkitemcontroller', ['$scope', '$modalInstance', 'workitemDataContext', 'common', 'options',
function ($scope, $modalInstance, workitemDataContext, common, options) {
$scope.activities = options.activities || [];
$scope.activity = $scope.activities[0];
}]);
Template HTML
<div class="col-lg-6">
<label class="text-xs">Activity</label>
<select class="form-control input-sm" data-ng-options="a.Name for a in activities" data-ng-model="activity"></select>
</div>
Data Binding is working fine but its not updating the property $scope.activity when any change is made. Same case with checkboxes as well but working with TextBox
I'm wondering if the assignment is breaking the chain of scope. Try adding your variables inside a dedicated object like $scope.model = {activity: ...} and see if it works. I ran into this when I started using multiple modals and controllers. Here's a fiddle I made a while back demonstrating the concept:
http://jsfiddle.net/6XDtN/
http://jsfiddle.net/6XDtN/1/
In the first one, the parent is oblivious because the child re-defined the variable, breaking the chain of scope. It isn't obvious it re-defined the variable, but there's no way to really re-assign a value without doing so.
In the second one, the complex type (i.e. an object), is not redefined, just a property is updated. Thus, the chain of scope is strong in this one.
Hope this helps!

AngularJS models, multiple $scopes, and two-way data binding

I like that AngularJS doesn't require and special syntax for Models, but there's one scenario I can't seem to wrap my head around. Take the following
My dataService wraps whatever flavor of data storage I'm using:
app.factory('dataService', function() {
var data = 'Foo Bar';
return {
getData: function() {
return data;
}
};
});
I have two controllers that both access the same piece of data:
app.controller('display', function($scope, dataService) {
$scope.data = dataService.getData();
});
app.controller('editor', function($scope, dataService) {
$scope.data = dataService.getData();
});
If I then have two views, one of which modifies the data, why doesn't the other update automatically?
<div ng-controller="display">
<p>{{data}}</p>
</div>
<div ng-controller="editor">
<input type="text" value="{{data}}"/>
</div>
I understand how this is working in something like Knockout where I'd be forced to make the data a knockout observable object. So any modifications in one part of the application trigger subscriptions and update views in another. But I'm not sure how to go about it in Angular.
Any advice?
There are few changes that I would suggest to make it work.
change the data object from a string to object type
Use ng-model to bind the input field
HTML
<div ng-controller="display">
<p>{{data.message}}</p>
</div>
<div ng-controller="editor">
<input type="text" ng-model="data.message"/>
</div>
Script
app.factory('dataService', function() {
var data = {message: 'Foo Bar'};
return {
getData: function() {
return data;
}
};
});
Demo: Fiddle
I haven't been stuck with the same situation, but the thing that jumps out at me is what you are sticking in the scope. There was an angular video where scope was discussed. You should put model objects in the scope and not use the scope as your model object.
In your example two scopes will be created each with the string data. Since data is a string and immutable, it will be replaced in scope of editor when changed. In your example if you had dataService return an object and that object is shared between the controllers, then perhaps your problems would be resolved. Try having dataService return model of {data: data} and bind to model.data instead of data.
This is untested, but should work based on how I know angular works.

Resources