Getting external variables inside Angular.js factory - javascript

I am giving my first steps with Angular.js and I am facing a little problem which I don't know how to deal with Angular. I know many jQuery ways for solving it, but I am sure that it may exists an Angular way:
Firstly, I have an select element set with currentPlant as ng-model
<select class="form-control" name="planta" ng-model='currentPlant' ng-options='plant.name for plant in plants' >
</select>
I print the current name as page title, I see this element updating when I change the selected value, so I am sure that currentPlant is being correctly updated.
<h1>{{ currentPlant ? currentPlant.name : 'Seleccione una planta'}}</h1>
Now I have the following button:
<button class='btn btn-primary' ng-click='loadTowers()'>
Cargar torres
</button>
When the button is pressed, the loadTowers function is executed, and is now when the problems start. I have set the loadTowers function in the controller that way:
$scope.loadTowers = plants.towers;
plant is a angular service which has implemented the towers function inside. I need to get the value of currentPlant inside this function. Take a look:
.factory('plants', ['$http',function($http){
var o = {
plants: [],
towers : []
};
o.towers = function(){
var id = ¿?; // I NEED SET THIS VARIABLE WITH THE currentPlant.id VALUE
$http.get('/api/v1/plants/'+id+'/towers')
.success(function(data){
angular.copy(data, o.towers);
})
}
return o;
}]);
I could set an data-plant attribute in the option element, and get the selected through jQuery, but I am trying avoid this kind of solutions. How do you deal with this?
NOTE: Take account that I simply want get the option selected when I press the button and make different get requests to the API`in function of that through the service.
Thanks in advance.

Pass the value in the ngclick:
<button ng-click='loadTowers(currentPlant)'>
Then add it in the factory args:
o.towers = function(currentPlant){
}

Related

Displaying json data via angular

I have created a datatable showing a list of persons and their details. When the datatable is clicked, it has to show the entity of the single person with their details but my problem is, when I click the datatable it is opening a chat box showing the entity of the last clicked person changing all other chat box details.
1.How can I limit the append directive, that is, one chatbox for one id?
2.How to display the name of the particular person without changing the older chat box entity?
Here is the link to Plunker http://plnkr.co/edit/VTWcZQjlAda0KQ9sjFzI?p=preview
Actually i really think there is no need for a directive here.
It can simply be done by using ng-repeat and a collection.
See it working in this plunker
I added this in the controller :
$scope.bottomListCollection = [];
$scope.addToBottomList = function(artist) {
$scope.bottomListCollection.push(artist);
}
And this kind of ng-click on your rows :
ng-click="addToBottomList(item)"
Some advices to do things cleaner in angular :
Never use :
$compile.
$element.
$broadcast.
Jquery.
Take care of custom directives, theses are 90% miss-used.
Just a reminder : Directives are intended to add a behavior on an element. Not adding html.
Hope it helped.
Some tips that fix your issue
childScope = $scope.$new(true);
to ve an isolated scope you ve to use the first parameter of $new method
for more info look at the docs ($new(isolate, parent); --> https://docs.angularjs.org/api/ng/type/$rootScope.Scope)
then you need to add a control on the AppendText function like this to check if the same chat already exist
$scope.AppendText = function(idx) {
$scope.userInfo = $scope.artists[idx];
var chat = $('#chat_'+$scope.userInfo.shortname);
console.log(chat);
if ($scope.stage === 'Add' && chat.length==0 ) {
childScope = $scope.$new(true);
childScope.userInfo = $scope.userInfo; //<--- add old scope info to new scope
var compiledDirective = $compile('<div my-directive></div>');
var directiveElement = compiledDirective(childScope);
$('.my-directive-placeholder').append(directiveElement);
} else {
$scope.stage = 'Add';
}
}
working plunker http://plnkr.co/edit/TFjlN0U2i4irKtG2D5yu?p=preview
To answer the second part of your question : Create an isolate scope!
That can be done by passing true while creating a new scope: childScope = $scope.$new(true).
Once the isolate scope is created, you can do:
childScope.userInfo = $scope.userInfo;
PLUNK : http://plnkr.co/edit/IxPh4EmLpr8WAqRWtRlo?p=preview
Also, a hackish solution using one time databinding (not recommended):
http://plnkr.co/edit/RjZNOSyaemqg2eZ4Gma1?p=preview
To answer the first part: You could keep track of the id's that are passed to the $scope.AppendText function perhaps?
PLUNK: http://plnkr.co/edit/BCNju0rToyVYvNjqzVON?p=preview
Hope this helps! IMHO it would be much more simpler if you could just ng-repeat over your json data to generate the chatboxes.

AngularJS / Javascript create $scope runtime

I've been looking at this for hours and so apologize if this is a dumb question, but is there a way in AngularJS or Javascript to do the following:
HTML
// I have a number of html piece of code (templates) for different controls
<button data-action="flip" ng-click="imageControl($event)"></button>
// ... 10 other imagecontrol types e.g. rotate, etc.
// objective - show or hide html based on which button is pressed
<div ng-show="(imageControl.flip)> // html stuff </div>
// angular function
$scope.imageControl = function($event) {
// get data-action from html element e.g. flip
var actionType = $event.currentTarget.getAttribute("data-action");
$scope.actionType ? $scope.actionType = false : $scope.actionType = true;
}
HERE IS THE QUESTION:
is there a way from me to use actionType to create and set a variable at runtime
why? so I don't have to create 10 different if statement for each control
btw - I know this won't work, but using it to illustrate example
I would simplify the issue, by not using the data attributes since angular does not need it.
markup:
<button ng-click="imageControl('flip')"></button>
<div ng-show="actions.flip"> // html stuff </div>
and in your controller:
$scope.imageControl = function(action) {
$scope.actions[action] = !$scope.actions[action];
};

Setting an Angular model using Protractor

I'm trying to emulate a user story on my website with Protractor.
The user has to type in an input that uses auto-completion. In real life, the user has to type some text in the input, then select the right proposition with either her mouse or more naturally her downarrow key.
The problem is that I can't seem to simulate that with Protractor. element.sendKeys just does not allow you to do that. I have tried in a dozen different manners and it yields unpredictable results at best.
So I would like to manipulate the ng-model behing my input directly. Is there a way to access the scope of an element from Protractor and call functions/set properties on it?
Here is a simplified version of my problem :
View :
<div ng-controller="MyController">
<input id="my-input" ng-model="myModel"/>
</div>
Controller :
myModule.controller('MyController', ['$scope', function($scope){
$scope.myModel = "";
//[...]
}]);
e2e Protractor test :
describe("setting myModel to a fixture value", function(){
it("should set myModel to 'a test value'", function(){
var myInput = element('my-input');
// Now what?
});
});
You can use .evaluate() to set your model value directly:
var elm = element(by.model("obj.field"));
elm.evaluate("obj.field = 'test';");
To get the model value:
elm.evaluate("obj.field").then(function (value) {
console.log(value);
});
In this answer: How to select option in drop down protractorjs e2e tests
They use this: .sendKeys(protractor.Key.ARROW_DOWN); for sending DOWN arrows.
It worth a try.

link 2 javascript objects

I am working on an angularJS app and i found a problem that I cant solve. I have a variable with predefined text that I want to replace in an email with the actual values, it looks like this:
$scope.predefinedVars = {
'client_name': $scope.itemPartner.name,
'client_city': $scope.itemPartner.city,
'client_county': $scope.itemPartner.county,
'client_address': $scope.itemPartner.address,
'client_phone': $scope.itemPartner.phone,
'client_email': $scope.itemPartner.email
};
and so on...
Now later on, when I choose a partner, the itemPartner object changes the data. For example: i have a function to watch when I change the partner from a select box:
$scope.$watch('newContract.partner_id', function() {
$scope.itemPartner = _.where($scope.listPartners, {'id': $scope.newContract.partner_id})[0];
alert(JSON.stringify($scope.itemPartner));
});
Now, in the alert, I can see the itemPartner data has changed, but if I try to read the values from my 1st variable $scope.predefinedVars, the values are still empty and do not change.
Is there a way to force the values to change when I change the itemPartner object ?
The problem is that you have set the fields of your predefinedVars object to a primitive. In javascript, primitives are passed by value, not by reference (google if you're not sure what that means).
The result is that any reference to the object you used to set it originally is lost.
You have a couple of alternatives:
Instead of replacing the email using the data from $scope.predefinedVars, use the data from $scope.itemPartner. This is less work.
create a function that repopulates predefinedVars
e.g.
function populatePredefinedVars (partner){
$scope.predefinedVars = {
'client_name': partner.name,
'client_city': partner.city,
'client_county': partner.county,
'client_address': partner.address,
'client_phone': partner.phone,
'client_email': partner.email
};
}
$scope.$watch('newContract.partner_id', function() {
$scope.itemPartner = _.where($scope.listPartners, {'id': $scope.newContract.partner_id})[0];
populatePredefinedVars($scope.itemPartner);
});

Input[number] doesn't show the value using angular & twitter bootstrap

My model is populated at the controller, and when I bind the data acts like it is there, but the input box is blank. As soon as I enter a value everything works normally. But if the default value is blank this will confuse my users greatly.
<div class="row" ng-controller="RangeInputCtrl">
<div class="col-md-12">
<label class="leftwall">
Value:
<input type="number" class="form-control"
ng-model="percentOfMax.current"
min="{{percentOfMax.min}}"
max="{{percentOfMax.max}}"
ng-change="changeValue()" />
</label>
</div>
</div>
ng-value is useless with this directive, since angular hijacks the input element. The code was working even worst without the min and max, so I know that they are required (or at least act required).
I have the input directive wrapped in a label due to twitter-bootstrap 3.
(function () {
'use strict';
var controllerId = 'RangeInputCtrl';
angular.module('app').controller(controllerId,['$scope', buildRangeInput]);
function buildRangeInput($scope) {
$scope.percentOfMax = InitCalcPercentOfMax($scope);
$scope.changeValue = function () {
var percentOfMax = $scope.percentOfMax;
$scope.percentOfMax = calcPercentOfMax(percentOfMax);
}
}
})();
There is a function, not posted here that takes $scope and builds the initial data structure from a complex object. Not shown here. InitCalcPercentOfMax has been tested and works as planned. It is external, and called separately due to code reuse.
The other function calcPercentOfMax(percentOfMax) is used to recalculate the formulas used in some directives that have been pulled (for the purpose of troubleshooting).
In short somewhere I must have something acting or posting wrong, and I just don't see it.
I had console.log([some expression]) all over, to see if the data was incorrect or ever blank, but the percentOfMax.current value always has at least a 0, so I still don't see where I am getting a blank from.
No error in console. And if I put in a <span>{{percentOfMax.current}}</span> I get the correct value on the first load.
The value is there in the model, it is just blank in the input box.
I think this is all of details, if I missed something, I will try to reply with an edit ASAP.
Based on a discussion at the angular chat room, I made the following changes.
var d = $q.defer();
var p = d.promise;
//If I leave this off, it never resolves. So there is nothing in the directive to trigger resolve.
d.resolve(function () {
return InitCalcPercentOfMax($scope);
}())
$scope.percentOfMax = p.then(function (POM) {
$scope.percentOfMax = POM;
});
I also added <span>{{percentOfMax}}</span> back to my view. I can see the promise firing, and I can see that the expected data is in my precentOfMax, but the input box is still empty.
Before someone asks, I am using version Angular.js 1.2.14, downloaded from Nuget
It looks like it should work, my guess is that you either have an error on the console that you've missed, or that there's some other logical error. This JSBin example is a simplified version which works: http://jsbin.com/zumedezu/1/edit
Try changing Value: to Value: {{percentOfMax.current}} to output the current value.
Is it possible that InitCalcPercentOfMax performs the calculation async outside of Angular?
InitCalcPercentOfMax($scope) was returning .current as a string. CalcPercentOfMax wasn't correcting it, but knew how to work with a string as a number.
Since the input type is number, the new numeric value was causing the .current to change to a number, so its value was showing up.
It took a greek lunch to see that the initial value was quoted.

Categories

Resources