View is not updating even if model gets updated. I googled about this problem and i got some solution, that use setTimeout or $timeout function.
I tried above function, but even using $timeout function model gets updated and view is not.
I am setting value of model from one service. When one controller setting value in service another controller listens to that service and update its model.
Note Factory Service
function loadNoteFactory($http) {
var baseURL = "SOME_URL";
var sessionId = "sessionId";
return {
getNotes: function() {
return $http.get(baseURL+"notes?"+sessionId);
},
addNote: function(note) {
return $http.post(baseURL+"note?"+sessionId, note);
},
editNote: function(tag, tagname) {
return $http.put(baseURL +"note/"+ note.id+"?"+sessionId, note);
},
deleteTag: function(tagId) {
return $http.delete(baseURL +"note/"+ note.id+"?"+sessionId);
}
};
}
Factory service
function loadSelectedNoteFactory($rootScope){
var selectedNoteFactory = {};
selectedNoteFactory.note = {};
selectedNoteFactory.setCurrentNote = function(note) {
this.note = note;
$rootScope.$broadcast('noteChanged');
};
return selectedNoteFactory;
}
Contoller 1 - Setting new value in service
function loadNoteListControllar($scope, NoteFactory, tagBasedNoteSearchService, selectedNoteFactory){
getUsersNotes();
function getUsersNotes(){
NoteFactory.getNotes().success(function(data) {
$scope.notes = data.notes;
selectedNoteFactory.setCurrentNote($scope.notes[0]);
});
}
$scope.onSelectNote = function(note){
selectedNoteFactory.setCurrentNote(note);
}
}
Controller 2 - update itself on change in service
function loadDetailControllar($scope, $timeout, selectedNoteFactory){
$scope.note = {};
$scope.$on('noteChanged', testme);
function testme(){
$timeout(function(){
$scope.note = selectedNoteFactory.note;
}, 500);
}
}
Html
<div class="tagWidget" ng-app="tagWidget">
<div class="notelistcontainer floatleft" ng-controller="NoteListController" style="width: 20%; height: 100%;">
<div class="notelist" style="border: solid 5px grey;">
<div class="noteitem greyborderbottom" ng-repeat="note in notes">
<div class="paddinglefttwentypx notesubject attachdotdotdot widthhundredpercent" ng-click="onSelectNote(note)">{{::note.subject}}</div>
</div>
</div>
</div>
<div class="detailview floatright" ng-controller="DetailController" style="width: 60%; height: 100%;">
<div class="paddinglefttwentypx notetext attachdotdotdot widthhundredpercent">{{::note.notehtmltext}}</div>
</div>
</div>
</div>
Injecting Cotroller, Services and Directives
angular.module('tagWidget', [])
.factory('tagBasedNoteSearchService', loadTagBasedNoteSearchService)
.factory('NoteFactory', loadNoteFactory)
.factory('SelectedNoteFactory', loadSelectedNoteFactory)
.directive('editableDiv', loadEditableDiv)
.directive('toggleIcon', loadToggleIcon)
.controller('NoteListController', ['$scope', 'NoteFactory', 'tagBasedNoteSearchService', 'SelectedNoteFactory', loadNoteListControllar])
.controller('DetailController', ['$scope', '$timeout', 'SelectedNoteFactory', loadDetailControllar])
'tagBasedNoteSearchService', 'SelectedNoteFactory', loadTagControllar]);
Solution - removed :: from expression {{::note.notehtmltext}}
Related
When I load the html page, my controller retrieves data from an API end point regarding a course. The page gets populate with the data about the course. But at the same time I want to populate part of the page with data about the lecturer of the course (their image, name , description etc ...). I pass the lecturer name to the method using the ng-init directive but I get a
ReferenceError: lecturerFac is not defined.
I am not sure but I believe the issue is the way I am calling the getLecturer() function using the ng-init directive.
What I want to happen when the page loads is have the Lecturer's details displayed on the page along with the course details.
courses.html
<div class="container" ng-controller="CoursesDetailsCtrl">
<div class="row" >
<div class="col-4" ng-model="getLecturer(courses.lecturer)">
<div>
<h3>{{lecturer.name}}</h3>
<<div>
<img class="img-circle" ng-src="{{lecturer.picture}}" alt="" />
</div>
<p>{{lecturer.description}}</p> -->
</div>
</div>
<div class="col-8">
<div class="myContainer" >
<h2>{{courses.name}}</h2>
<div class="thumbnail">
<img ng-src="{{courses.picture}}" alt="" />
</div>
<div>
<p>{{courses.description}}</p>
</div>
</div>
</div>
</div>
</div>
CoursesDetailsCtrl
todoApp.controller('CoursesDetailsCtrl', ['coursesFac','lecturerFac','$scope','$stateParams', function CoursesCtrl(coursesFac, lecturerFac, $scope, $stateParams){
$scope.getLecturer = function(name){
lecturerFac.getLecturerByName(name)
.then(function (response) {
$scope.lecturer = response.data;
console.log($scope.lecturer);
}, function (error) {
$scope.status = 'Unable to load lecturer data: ' + error.message;
console.log($scope.status);
});
};
}]);
lecturerFac
todoApp.factory('lecturerFac', ['$http', function($http) {
var urlBase = '/api/lecturer';
var coursesFac = {};
lecturerFac.getLecturer = function () {
return $http.get(urlBase);
};
lecturerFac.getLecturerByName = function (name) {
return $http.get(urlBase + '/' + name);
};
return lecturerFac;
}]);
todoApp.factory('lecturerFac', ['$http', function($http) {
var urlBase = '/api/lecturer';
var coursesFac = {};
var service = {};
service.getLecturer = function () {
return $http.get(urlBase);
};
service.getLecturerByName = function (name) {
return $http.get(urlBase + '/' + name);
};
return service;
}]);
i Think the cause of this error is the lecturerFac variable is not initialize in the factory. Create an empty object call lecturerFac in the factory and return it.
todoApp.factory('lecturerFac', ['$http', function($http) {
var urlBase = '/api/lecturer';
var coursesFac = {};
var lecturerFac= {};/////////////////////
lecturerFac.getLecturer = function() {
return $http.get(urlBase);
};
lecturerFac.getLecturerByName = function(name) {
return $http.get(urlBase + '/' + name);
};
return lecturerFac;
}]);
Also avoid calling functions inside the ng-model. When you bind something with ng-model it must be available for both reading and writing - e.g. a property/field on an object. use ng init instead
I have three .js files here. This code works fine except the count scores don't correlate with the other clicking of the button. I would like to add these requirements to my code as well: Each service will store the counter that displays above/below the buttons as a property on the service. Each service will have at least 3 methods: increment, decrement, and reset, which resets the counter to 100.
The counter property in the services must NOT be directly manipulated by a controller - you should create public methods in your services to perform the operations instead, which are called by the controller.
//home.js
var app = angular.module('MyApp');
app.controller("HomeController", ['$scope', 'RedService', 'BlueService', function ($scope, $rs, $bs) {
$scope.title = "The Mighty Clicker";
$scope.redOutput = 100;
$scope.blueOutput = 100;
$scope.countRed = function () {
$rs.countUp++;
$scope.redOutput = $rs.countUp;
$bs.coundDown--;
$scope.blueOutput = $bs.coundDown;
}
$scope.countBlue = function () {
$bs.countUp++;
$scope.blueOutput = $bs.countUp;
$rs.countDown--;
$scope.redOutput = $rs.countDown;
}
}]);
//blueService.js
var app = angular.module("MyBlueService", []);
app.service("BlueService", function () {
this.countUp = 100;
this.coundDown = 100;
})
//redService.js
var app = angular.module("MyRedService", []);
app.service("RedService", function() {
this.countUp = 100;
this.countDown = 100;
})
here is my HTML code
//html
<div class="row">
<div class="col-xs-12 buttons">
<h1 class='title'>{{title}}</h1>
<button class="btn red" ng-click="countRed()">Button</button>
<h1>{{redOutput}}</h1>
<br><br><br><br><br><br>
<button class="btn blue" ng-click="countBlue()">Button</button>
<h1>{{blueOutput}}</h1>
</div>
</div>
enter image description here
Not exactly sure what the rules are but from what I understand I made a plunker:
https://plnkr.co/edit/lrMgM8lcm0FtCIQbZLlf?p=preview
It looks like the code works without needing change except for the typos :D
$scope.blueOutput = blueService.countDown;
You mispelled countDown with coundDown
As #gyc mentioned in his post, there was a typo. I have created the plunkr with the same design architecture (3 modules and each of them with a service).
RedApp and BlueApp modules are added to MainApp module as dependencies and used their in myApp's controller.
var myApp = angular.module("MainApp", ["RedApp", "BlueApp"]);
myApp.controller("MyAppController", ["$scope", "RedAppService", "BlueAppService", function($scope, $rs, $bs) {
$scope.title = "The Mighty Clicker";
$scope.redOutput = 100;
$scope.blueOutput = 100;
$scope.countRed = function() {
$rs.countUp++;
$scope.redOutput = $rs.countUp;
$bs.countDown--;
$scope.blueOutput = $bs.countDown;
}
$scope.countBlue = function() {
$bs.countUp++;
$scope.blueOutput = $bs.countUp;
$rs.countDown--;
$scope.redOutput = $rs.countDown;
}
}]);
var redApp = angular.module("RedApp", []);
var blueApp = angular.module("BlueApp", []);
redApp.service("RedAppService", function() {
this.countUp = 100;
this.countDown = 100;
});
blueApp.service("BlueAppService", function() {
this.countUp = 100;
this.countDown = 100;
});
As I understand You need to have two buttons, if click first -> it counter gets up and counter second one gets down. I have done it by starting from Your code, but I've simplified the solution, so both services has only one counter and I set services directly into scope to avoid many assignments and variables. Check my working example:
//home.js
var app = angular.module('MyApp',[]);
app.controller("HomeController", ['$scope', 'RedService', 'BlueService', function ($scope, $rs, $bs) {
$scope.title = "The Mighty Clicker";
//services to scope directly
$scope.$rs=$rs;
$scope.$bs=$bs;
$scope.countRed = function () {
$rs.count++;
$bs.count--;
}
$scope.countBlue = function () {
$bs.count++;
$rs.count--;
}
}]);
//blueService.js
app.service("BlueService", function () {
this.count = 100;//single counter
})
//redService.js
app.service("RedService", function() {
this.count = 100; //single counter
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="HomeController" class="row">
<div class="col-xs-12 buttons">
<h1 class='title'>{{title}}</h1>
<button class="btn red" ng-click="countRed()">Button</button>
<h1>{{$rs.count}}</h1>
<button class="btn blue" ng-click="countBlue()">Button</button>
<h1>{{$bs.count}}</h1>
</div>
</div>
EDIT. AFTER COMMENT.
//home.js
var app = angular.module('MyApp',[]);
app.controller("HomeController", ['$scope', 'RedService', 'BlueService', function ($scope, $rs, $bs) {
$scope.title = "The Mighty Clicker";
//services to scope directly
$scope.$rs=$rs;
$scope.$bs=$bs;
$scope.countRed = function () {
$rs.increment();
$bs.decrement();
}
$scope.countBlue = function () {
$bs.increment();
$rs.decrement();
}
$scope.reset=function(){
$bs.reset();
$rs.reset();
}
}]);
//return constructor
//create for DRY
app.service("Counter",function(){
return function(){
this.count = 100;//single counter
this.increment=function(){
this.count++;
};
this.decrement=function(){
this.count--;
};
this.reset=function(){
this.count=100;
};
};
});
//blueService.js
app.service("BlueService", ["Counter",function ($cf) {
return new $cf;
}]);
//redService.js
app.service("RedService", ["Counter",function ($cf) {
return new $cf;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="HomeController" class="row">
<div class="col-xs-12 buttons">
<h1 class='title'>{{title}}</h1>
<button class="btn red" ng-click="countRed()">Button</button>
<h1>{{$rs.count}}</h1>
<button class="btn blue" ng-click="countBlue()">Button</button>
<h1>{{$bs.count}}</h1>
<button ng-click="reset()">Reset counters</button>
</div>
</div>
I need to reflect some changes to controller B (inside some event) when I make change at controller A. For that I am using a service.
When I am changing service value from FirstCtrl, ng-change is not firing at SecondCtrl. Is there anything I have missed or need to change?
Please note that I am using angular 1.5.6. and don't want to use watch or even scope.
Below is my code.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
Data.FirstName = self.FirstName;
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.FirstName}}
<input type="text" ng-model="c1.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
As you dont want to use $scope trying modifying the code in order to use $emit and $on feature in angular js to communicate between two controllers. You can refer this link.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
//Data.FirstName = self.FirstName;
Data.$on('emitData',function(event,args){
Data.FirstName=args.message
document.write(Data.FirstName)
})
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
Data.$emit('emitData',{
message:Data.FirstName
})
};
}
]);
The only way then is to directly copy the reference of the data object within the controller. Note that you don't need ng-change to update the value then.
If you want something else, either wrap the FirstName in a sub object of Data and do the same i did :
Data = {foo:'FirstName'};
Or use $watch since it's the whole purpose of that function.
Here is a working code with copying the Data object in the controller.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
self.Data=Data;
debugger
self.changeM = function() {
debugger
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.Data = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.Data.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.Data.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.Data.FirstName}}
<input type="text" ng-model="c1.Data.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
The only way I know to solve the problem is using watch, unfortunately. (I am new to angular.)
From the ngChange document (https://docs.angularjs.org/api/ng/directive/ngChange):
The ngChange expression is only evaluated when a change in the input value causes a new value to be committed to the model.
It will not be evaluated:
if the value returned from the $parsers transformation pipeline has not changed
if the input has continued to be invalid since the model will stay null
**if the model is changed programmatically and not by a change to the input value**
I would like to create a service and a controller in AngualrJS. The problem is I need to access to $scope in my service.
I think the good solution is to put this service in the controller directly but I have no idea how to do it.
This is my HTML :
<div ng-controller="myController">
<input type="text" id="idInput" name="idInput" ng-model="nameModel">
<button class="btn btn-default" ng-click="functionWhenClick()">Execute</button>
</div>
This is my controller :
var variableModuleName = angular.module("nameModule",[]);
variableModuleName.controller('controllerName',function($rootScope,$scope, CommonService) {
$scope.nameModel = '';
$scope.scopeFunctionName = function () {
CommonService.myFunction($scope.nameModel);
};
});
This is my service :
variableModuleName.service('CommonService', ['dataService', function(dataService) {
this.loadData = function(param) {
dataService.getCommitData(param).then(function(res) {
if (res.error) {
$scope.chartData = res.chartData;
}
});
};
this.myFunction = function(concatURL){
this.loadData('URL' + concatURL);
}
}]);
I hope you will can help me.
Thanks.
First of all, You can't/shouldn't use $scope in a service. You can't inject $scope in the service. You can pass $scope as a function's parameter but that's a bad idea. Because, we don't want our service to play with all our $scope variables.
Now, to rewrite your service to return chartData from an async operation using dataService (assuming dataService.getCommitData(param) does have a call to server) , you need to handle the promise well.
var d3DemoApp = angular.module("D3demoApp",[]);
// service. Assuming dataService exists
d3DemoApp.service('CommonService', ['dataService', function(dataService) {
this.loadData = function(param) {
return dataService.getCommitData(param).then(function(res) {
// Should the if condition be res.error or !res.error
if (res.error) {
return res;
}
});
};
this.myFunction = function(parameterItem){
return this.loadData('http://localhost:3412/bubble/' + parameterItem);
console.log("Fonction appellée");
}
}]);
// controller
d3DemoApp.controller('controllerFilterSearch',function($rootScope,$scope, CommonService) {
$scope.searchText = '';
$scope.getSearchText = function () {
CommonService.myFunction($scope.searchText).then(function(res) {
$scope.chartData = res.chartData;
});
};
});
So, in the above code, I am basically returning a promise from this.loadData function. When we call CommonService.myFunction from controller, we get the response in the then resolved callback function and we set the chartData from response to $scope.chartData.
First don't use var d3DemoApp = angular.module("D3demoApp",[]) through your files.
Use angular.module("D3demoApp",[]) once to get your module instantiated and then get the reference of the existing one using angular.module("D3demoApp")
In your plknr :
You forget to include the service file
I don't see any definition of the dataService which is why you have the unknown provider dataServiceProvider error.
There are many ways to do this. My favorite is creating another service which has reference to the scope.
d3DemoApp.service('scopeServer', ['dataService', function(dataService) {
var scope;
return {
scope: function(_scope) {
if (typeof _scope !== 'undefined')
scope = _scope;
return scope;
}
}
}]);
This service maintains a reference to the scope in a singleton an returns it wherever you call scopeService.scope();
You can set the scope in your controller initially.
d3DemoApp.controller('controllerFilterSearch',function($rootScope,$scope, scopeServer) {
scopeServer.scope($scope);
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="controllerInput.js"></script>
<script src="app.js"></script>
<script src="serviceInput.js"></script> <!-- Include -->
</head>
<body ng-app="D3demoApp" ng-controller="controllerFilterSearch">
<input type="text" id="searchTextBox" name="searchTextBox" ng-model="searchText">
<button class="btn btn-default" ng-click="getSearchText()">Rechercher</button>
</body>
</html>
var d3DemoApp = angular.module("D3demoApp",[]);
d3DemoApp.controller('controllerFilterSearch',function($rootScope,$scope, CommonService) {
$scope.searchText = '';
$scope.getSearchText = function () {
CommonService.myFunction($scope.searchText);
};
});
service
d3DemoApp.service('CommonService', ['dataService', function(dataService) {
this.chartData = '';
this.loadData = function(param) {
dataService.getCommitData(param).then(function(res) {
if (!res.error) {
this.chartData = res.chartData;
}
});
};
this.myFunction = function(parameterItem){
this.loadData('http://localhost:3412/bubble/' + parameterItem);
console.log("Fonction appellée");
}
}]);
controller
var d3DemoApp = angular.module("D3demoApp",[]);
d3DemoApp.controller('controllerFilterSearch',function($rootScope,$scope, CommonService) {
$scope.searchText = '';
$scope.getSearchText = function () {
CommonService.myFunction($scope.searchText);
$scope.searchText = CommonService.chartData;
};
});
I have a scope variable, when it returns true, i need to trigger some events or do something. I my case, the every first time, the scope variable returns undefined and later it returns true. In this case i used $watch method to get the expected funcionality. Is there any alternative approach to do the same instead using $watch ?
scope.$watch () ->
scope.initiateChild
, (value) ->
if value is true
$timeout ->
scope.buildOnboarding()
, 1000
You can try using AngularJS $on(), $emit() and $broadcast().
Here is an example: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
You can use JavaScript getters and setters without any expense of using $watch.
Write code in the setter to do what you want when angular changes the your model's value you are using in scope. It gets null or an a State object as user types. Useful for working with type ahead text boxes that have dependencies on each other. Like list of counties after typing state without user selecting anything.
Here is some pseudo style code to get the idea.
<input ng-model="searchStuff.stateSearchText" />
<div>{{searchStuff.stateObject.counties.length}}</div>
<div>{{searchStuff.stateObject.population}}</div>
$scope.searchStuff=new function(){var me=this;};
$scope.searchStuff.stateObject = null;
$scope.searchStuff.getStateObjectFromSearchText = function(search){
// get set object from search then
return stateObject;
};
$scope.searchStuff._stateSearchText= "";
Object.defineProperty($scope.searchStuff, 'stateSearchText', {
get: function () {
return me._stateSearchText;
},
set: function (value) {
me,_stateSearchText = value;
me.stateObject = getStateObjectFromSearchText (value);
}
});
See this fiddle: http://jsfiddle.net/simpulton/XqDxG/
Also watch the following video: Communicating Between Controllers
A sample example is given below
Html:
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
javascript:
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];