AngularJS - Not able to get value from factory service - angularjs

I am very much new in AngularJS and due to which I am facing an issue and not able to understand the exact problem. The code which I tried is worked in normal javascript code but now I want to use custom service (factory function). Actually, I have a textarea where user can input their text and then they can do the formating by selecting any of the text. (discovertheweb.in/woweditor - this is existing site which I have created now I want to apply angular in this code). Now, I have created a custom service to check whether the user select any content or not. I have used the below code in my custom services and try to get the selection start, end point so that I can get the selected text from the textarea field. But, the problem is that I am getting 0 value for both selection start and end point. When i used the same code inside directive it works but try to get the value through service it is showing 0 for both. Please find the below code and let me know the code which I missed out here.
Custom Service:
(function(){
"use strict";
var wsApp = angular.module("WorkApp");
wsApp.factory("InputCheckService", function(){
var defaultText = document.getElementById("input-text-area");
var selStart = defaultText.selectionStart;
var selEnd = defaultText.selectionEnd;
var selectedText;
if(selStart != selEnd){
selectedText = defaultText.value.substring(selStart, selEnd);
}else{
selectedText = null;
}
return {
selStart: selStart,
defaultText: defaultText,
selEnd: selEnd,
selectedText: selectedText
};
});
}());
The directive where I called this services. I already included the service inside the main controller in different file.
(function(){
"use strict";
var wsApp = angular.module("WorkApp");
wsApp.directive("generalDirective", generalDirective);
function generalDirective(){
return {
scope: true,
controller:function($scope, InputCheckService){
$scope.collapsed = false;
$scope.CollpasePanel = function(){
$scope.collapsed = !$scope.collapsed;
};
$scope.updatePara = function(){
alert(InputCheckService.defaultText+"Selection start: "+InputCheckService.selStart+" Selection End: "+ InputCheckService);
/**
* defaultText: defaultText,
selStart: selStart,
selEnd: selEnd,
selectedText: selectedText
*/
}
},
templateUrl: 'directive/general-directive.html'
};
}
}());
If you need any more details, please let me know.
Thanks in advance for your time and suggestion.
Regards,
Robin

You should not use service to manipulate DOM element. You should manipulate DOM only at directives. Your problem is you DONT have anywhere to listen to TEXTAREA SELECTION EVENT and your service will not update the data inside. I have created a fiddle for your problem. The watchSelection directive is based on this answer from stackoverflow. Something you should notice :
I use service only to store data. Something like selStart, selEnd or paragraphContent and provide some API to retrieve the data
.factory("InputCheckService", function () {
return {
setSelStart: function (start) {
selStart = start;
},
.....
},
});
On the watchSelection directive, you watch for the mouseup event and will perform update the service so that it will store value you need and later you can retrieve it from other directives or controllers.
elem.on('mouseup', function () {
var start = elem[0].selectionStart;
//store it to the service
InputCheckService.setSelStart(start);
});
In your generalDirective directive you can get value from your service and angular will auto update the view for you.
Hope it helps.

Related

Angularjs get external page data from parameters

I am beginning to use Angularjs. I have this working properly.
There is a dynamically created sidebar listing all the styles.
There is a dynamically created navbar for sizes associated with that style.
When clicked style is "selected" properly.
When clicked size is "selected" properly.
I want these two selections to be parameters in a url that I GET and display within a specific DIV. So the url would look like ...http://xxxddd.com/inventory/{{style}}/{{size}}
I have absolutely no idea where to go from here.
<script>
var app = angular.module("alt", []);
app.controller('StyleController', function(){
this.style = '';
this.selectStyle = function(newValue){
this.style = newValue;
};
this.isSelected = function(styleName){
return this.style === styleName;
};
});
app.controller('SizeController', function(){
this.size = '';
this.selectSize = function(newValue){
this.size = newValue;
};
this.isSelected = function(sizeName){
return this.size === sizeName;
};
});
</script>
I am very new at angular. I hope someone can help me. This is the main hangup with my project right now.
Thanks in advance.
You will want to create a service that utilizes AngularJS' $http library to make a GET request. Look at the documentation and see how to pass parameters (style/size).
You will then want to use AngularJS' Dependency Injection (DI) to inject your service into your controller and call its method which has the $http call.

Accessing Data set via a Promise in an Angular Directive

I am writing my first non-trival Angular App and I have hit a snag with a directive. The directive takes data from a controller's scope and applies it to Google Chart. The chart is not the issue - which is to say it works fine with dummy data - it is access to the properties of the scope object which were obtained via http:
I am accessing data returned via an API in a service which utilizes $http:
dashboardServices.factory('SearchList', ['$http','$q',
function($http, $q){
return {
getSearchDetails:function(searchType, resultType){
return $http.get("api/searches/"+searchType+"/"+resultType)
.then(function(response){
if (typeof(response.data === 'object')) {
return response.data;
} else {
return $q.reject(response.data);
}
},function(response){
$q.reject(response.data);
});
}
}
}]);
In my controller, I am taking the response from this service and attaching to my scope via the promises' "then" method:
dashboardControllers.controller('DashboardCtrl', ['$scope', 'SearchList',
function($scope, SearchList){
$scope.searchData = {};
$scope.searchData.chartTitle="Search Result Performance"
SearchList.getSearchDetails("all", "count").then(function(response){
$scope.searchData.total = response.value; //value is the key from my API
});
SearchList.getSearchDetails("no_results", "count").then(function(response){
$scope.searchData.noResults = response.value;
});
}]);
To an extent this works fine, i can then use the 2-way binding to print out the values in the view AS TEXT. Note: I want to be able to write the values as text as I am trying to use a single scope object for both the chart and the textual data.
{{searchData.total | number}}
As mentioned, I have written a directive that will print a specific chart for this data, in this directive ONLY the $scope.searchData.chartTitle property is accessible. The values that were set in the then functions are not accessible in the directive's link method:
Here is the directive:
statsApp.directive('searchResultsPieChart', function(){
return{
restrict : "A",
scope:{
vals:'#vals'
},
link: function($scope, $elem, $attr){
var dt_data = $scope.vals;
var dt = new google.visualization.DataTable();
dt.addColumn("string","Result Type")
dt.addColumn("number","Total")
dt.addRow(["Successful Searches",dt_data.total]);
dt.addRow(["No Results",dt_data.noResults]);
var options = {};
options.title = $scope.vals.title;
var googleChart = new google.visualization.PieChart($elem[0]);
googleChart.draw(dt,options)
}
}
});
Here is how I am using the directive in the view:
<div search-results-pie-chart vals="{{searchData}}"></div>
I can see that the issue is that the numeric values are not available to the directive despite being available when bound to the view.
Clearly the directive needs to be called later when these items are available or via some callback (or perhaps an entirely different approach), unfortunately i am not sure why this is the case or how to go about solving.
Any help would be greatly appreciated. I hope this makes sense.
I think the following will help you.
First change the directive scope binding for vals to use = instead of # (see this question for good explanation of the differences - basically # interpolates the value whereas = binds to the variable in the parent scope)
Then, move the part of the directive that creates the graph into a render function within your link function.
Then, $watch vals for any changes, then call the render function with the new values
You would also have to slightly change the approach of using ele[0], as you'll need to clear out the contents of it and add a new element with the new chart when the data changes (otherwise many charts will be added as the data changes!)
Here is an example of what to do in your link function with regard to the $watch and new render function (changing the $scope binding like I mentioned is not shown):
$scope.$watch('vals', function(newVals, oldVals) {
return $scope.render(newVals);
}, true);
$scope.render = function (dt_data) {
var dt = new google.visualization.DataTable();
dt.addColumn("string","Result Type")
dt.addColumn("number","Total")
dt.addRow(["Successful Searches",dt_data.total]);
dt.addRow(["No Results",dt_data.noResults]);
var options = {};
options.title = $scope.vals.title;
var googleChart = new google.visualization.PieChart($elem[0]);
googleChart.draw(dt,options)
}
Hope this helps you out!!!

Angularjs : Why I need to click on a button to get my view updated?

I'm trying to developpe a chrome extension with angularjs and I have a strange behaviour when I try to initialize the $scope with the url of the active tab.
Here the code of my controller:
var app = angular.module('app', ['app.service']);
app.controller('ItemCtrl', function ($scope, chromeHelper) {
$scope.website = "No result!";
// Does not work until I click on something :-/
chromeHelper.getActiveTabDomain(function (domain) {$scope.website = domain; });
});
So when I try to initialize directly the $scope.website member it doesn't succeed but when I click on the button aftewards $scope.website then updates.
I really don't understand why.
Here is the code of my Chromehelper service:
var service = angular.module('app.service', []);
service.factory('chromeHelper', function() {
var chromeHelper = {};
chromeHelper.getActiveTabDomain = function (callback){
chrome.tabs.query({'active': true}, function(tabs){
if(tabs && tabs.length > 0) callback(getDomainFrom(tabs[0].url));
});
};
return chromeHelper;
});
function getDomainFrom(url) {
return url.match(/:\/\/(.[^/]+)/)[1];
}
Thank you very much in advance!
The OP solved the problem (see comment above) by adding $scope.$apply() at the end of the callback:
// Does not work until I click on something :-/
chromeHelper.getActiveTabDomain(function(domain) {
$scope.website = domain;
$scope.$apply(); // <-- adding this line did the trick
});
A short explanation for anyone landing on this page with a similar problem:
From the AngularJS docs on 'scope' (more specifically from the section titled 'Scope Life Cycle'):
Model mutation
For mutations to be properly observed, you should make them only within the scope.$apply(). (Angular APIs do this implicitly, so no extra $apply call is needed when doing synchronous work in controllers, or asynchronous work with $http or $timeout services.
See, also, this short demo.

method overloading \ overriding in AngularJS

I have a scenario, i which I have a share button, I have a container controller, called metaCtrl, which is on the html tag.
And also inner controllers.
I have a share button, that calls the model.share() function on click.
code:
app.controller('metaCtrl', function($scope){
$scope.model = {};
$scope.model.share = function(){
$location.path('/share').search({'share' : '1'});
}
});
The controller of the share page it self:
app.controller('shareCtrl', function($scope)){
$scope.layout.shareVisible = $location.path().share == 1 ? true : false;
$scope.model.share = function(){
$scope.layout.shareVisible = $scope.layout.shareVisible ? false : true;
var shareUrlValue = $scope.layout.shareVisible ? 1 : null;
$location.search('share', shareUrlValue);
}
});
The idea is to use the same HTML pattern in the entire application, but only to toggle the share section on the share page(if the user is already there), and to send the user to the share view if he is not currently there.
The problem is that after I go to the sahre page, and then return to the other page, the function share() has a referance to the function in the shareCtrl, rather then to the metaCtrl.
I'm not sure there's enough information here, but I'll take a shot at it (I can't comment because I don't have enough rep). Notice that your shareCtrl is not creating a new model object, you're just assigning $scope.model.share = func....
That assignment is overriding the function in the parent controller because Angular is going up the scope chain to find the model object.
See this Plunker:
http://plnkr.co/edit/yv5rrpdlkniZdg94T4Lf
Notice with $scope.model = {} commented out in shareCtrl, the value of both fields is "shareCtrl". But, if you uncomment that line, then $scope.model is within the shareCtrl scope so Angular doesn't go up the scope chain looking.

Angularjs Directive Assistance with Bootstrap-UI $Dialog

I am working on creating a custom directive which creates a button within an ng-repeat and will bind to the current item. Once the button is clicked, a $dialog opens with a select2 box to select and change the user. On save the user replaces the current user within the ng-repeat. I have all of this working (though would appreciate any code review/revision help since this is my first directive), the part where I am stuck is how to now incorporate a call back to an angular service to push the updated user back to the database.
Here is my current plunker: http://plnkr.co/edit/nzR3mjeLK3kPynSIjE7t?p=preview
Is the best option here to call a function in the parent controller to call the service update?
Is there a way the directive itself should handle this?
Any help or direction would be greatly appreciated.
You can inject a service module with the data persistence implemented inside the service in order to separate the logic from the directive.
.factory('service', function () {
var service = {
doSomething: function(result){
//logic to do the data persistence
console.log('saved!');
}
}
return service;
})
.directive('delegateApproval', function($dialog, service) { //inject the 'service'
$scope.openDelegate = function(approval) {
d.open().then(function(result) {
$scope.approval._approver._user._lastName = '';
$scope.approval._approver._user._firstName = result.empName;
service.doSomething(approval); // consume the service
});
};
Demo

Resources