ng-click doesn't take parameters from the DOM - javascript

I have the following code:
<input id="id">
<button data-action="bea" ng-click="Create($('#id1')[0].value);" class="btn">Insert ID</button>
<button data-action="bea" ng-click="Create($('#id2')[0].value);" class="btn">Insert ID</button>
In the JS I have:
$scope.Create = function (id){
if (id === undefined) {
$scope.data = "You must specify an id";
} else {
$scope.data = data;
console.log(data);
});
}
};
When the call gets into the Create function the value of the id is undefined.
If I add the following line at the beginging of the Create function everything works ok:
id = $('#id')[0].value;
If I send a constant value it works:
<button data-action="bea" ng-click="Create('SomeID');" class="btn">Insert ID</button>
Why is this happening and how can I do that without putting the line of value into the method?
Thanks

This is just an extension of comments and other answers, You could achieve this in many ways using angular, one simple example could be:-
<!-- Add a controller -->
<div ng-controller="MainCtrl">
<!-- Give a model binding to your text input -->
<input ng-model="userEntry" type="text"/>
<!-- ng-click pass which ever argument you need to pass, provided it is an expression that can be evaluated against the scope or any constants -->
<button data-action="bea" ng-click="Create(userEntry);" class="btn">Insert ID</button>
<!-- Some simple data binding using interpolation -->
{{data}}
<!-- Just for demo on repeater on a list of items on the scope -->
<div ng-repeat="item in items track by $index">{{item}}</div>
</div>
Example Demo
My 2 cents on the lines of what were originally trying to do:-
Use angular bindings instead of accessing DOM directly for getting the data, it really helps you deal with just the data without worrying about how to access or render it in DOM. If you think you need to access DOM for implementing business logic re-think on the design, if you really need to do it, do it in a directive. Angular is very opinionated on the design and when where you do DOM access.
ng-model
ng-binding
controller
all about ngmodel controller

This is not the way you should do in AngularJS. You should really think in Angular if you want to use AngularJS. Refer this post ("Thinking in AngularJS" if I have a jQuery background?)
All DOM manipulation should be done in Directive. Refer this page that I found really clear.
(http://ng-learn.org/2014/01/Dom-Manipulations/)

My guess is that $ is not bound to the jQuery function when the ng-click value is evaluated, because it is not exposed in the Angular scope.
Solutions to adress this:
expose the jQuery function in scope somewhere, e.g $scope.$ = $; in a controller.
make the Create function parameterless as you suggested, with a var id = $('#id')[0].value; at the beginning
my favorite : avoid using jQuery. If you put some data in the #id element, there's probably a more natural and AngularJS-idiomatic way of retrieving it than querying the DOM (e.g an Angular service).
In particular, if the element you're targeting is an <input> element, then use the ngModel directive to link the value to a $scopeproperty that will be accessible in the controller :
<input ng-model="inputData"/>

The JavaScript you are trying to pass as a parameter of the create function is not available in the scope of the Create function.
Try to target the element a different way.
Does that help?

Related

How to reference child controllers of a certain type from an angular directive or component?

When I define an angular component or directive, I can use 'require' to bind to a parent controller like require: {'parentForm' : '^^ngForm'}.
I'd like to do the same thing, but in reverse: require: {'childrenForms' : '[children]ngForm'} would bind childrenForms to an array of all controllers contained within my component which are ngForms.
I want to do this in order to build components that add aggregate behavior to directives I don't have control over. For example my-special-form container could have an isValid() method that returns true if all the ng-forms within it are valid at the moment.
Is there any way to do this? Am I making a mistake by even wanting to?
There is no option to require inner child directive in Angular. Personally I don't think so the approach you thought about would be good approach to go for, like here parent directive want to know directly about child directive. Rather I'd say choose the approach where child directive will pass required data to parent controller by calling one of its method.
If you only wanted to validate inner forms then you can wrap all the inner forms inside wrapper form ng-form with some name(you can have nested form in angular using ng-form directive). You can see example below, where you can see I wrapped all the inner form with one wrapper form element ng-form. So what happen is, when any one of the forms get invalid the parent form will invalid. If all forms are valid then parentForm will become valid as well.
Markup
{{parentForm.$valid}}
<div ng-form name="parentForm" my-directive>
<form name="form1" some-directive>
.....
</form>
<form name="form2" some-directive>
.....
</form>
<form name="form3" some-directive>
.....
</form>
</div>

Dynamically add angular attributes to old html forms

I have a project where I'm currently trying to refactor an old system that was hinged on jquery from the ground up with angular 1.x. However, there are a lot of old HTML forms that I'd like to reuse the bulk of so I don't want to recreate them. I'd love it if there was a way to keep it purely angular, but I'm honestly at a loss of how I'd do that (or whether or not I can). I'm fairly new to angular so there are a lot of inner workings to it that I'm still not privy to.
I've searched around on google and other places including here and I can't really even find other people talking about it. That tells me that either I'm searching badly or it's something that I should probably not be working towards.
All the html pages have identically id'd fields so I feel I can reliably base things on that. For example: all forms with first name text boxes have an id of "cl_fname".
Is there anyway that I can accomplish: getting the form, adding an ng-model="cl_fname" or something to the relevant tag and then display the form? I've gotten to the point where I can get the html page, hold it in the scope and then display using ng-bind-html, but figuring out how to add angular attributes to specific elements I can't figure out.
You can achieve this with jQuery and the attr() method.
I created a plunker here that demonstrates adding angular to an existing "plain" html form.
In the example, I'm using id selectors, but you could use any combination of selectors to ensure you get the right elements.
The below is a quick code snippet from my Plunker example:
HTML:
<div ng-app="myApp">
<form id='myForm1' data-test="test2">
<span>First Name:</span>
<input type="text" id="myForm1_firstName" />
<input type="submit" id="myForm1_Submit" value="Go!" />
</form>
</div>
JS:
// set up angular
var myApp = angular.module('myApp', []);
myApp.controller('MyForm1Controller', ['$scope', function($scope) {
$scope.firstName = 'Angular Working!';
}]);
// use jQuery to add the relevent attributes to our form
var jqMyForm1 = $('form#myForm1');
var jqTxtFirstName = jqMyForm1.find('input[type="text"]#myForm1_firstName');
//add controller to form
jqMyForm1.attr('ng-controller', 'MyForm1Controller');
//bind the textbox to the angular 'firstName' variable
jqTxtFirstName.attr('ng-model', "firstName");
EDIT:
just realised you want to load the html form dynamically.
Version 2 of the plunker (here) will now dynamically load a HTML form from an external resource (separate html page), inject it into the current page, add the angular bindings to it, and then get angular to recognise it.
The key to getting angular to recognise the form is the use of the $compile object (angular $compile documentation).
Again, quick snippets of the code in use:
HTML (main page):
<div ng-app="myApp" ng-controller="LoadingController"></div>
HTML (myForm1.html):
<form id='myForm1' data-test="test2">
<span>First Name:</span>
<input type="text" id="myForm1_firstName" />
<input type="submit" id="myForm1_Submit" value="Go!" />
</form>
JS:
// set up angular
var myApp = angular.module('myApp', []);
// main controller for loading the dynamic form
myApp.controller('LoadingController', ['$scope','$http','$compile', function($scope,$http,$compile) {
$scope.loadHtmlForm = function(formURL) {
$http.get(formURL).then(function successCallback(response){
var jqForm = $(response.data);
var jqTxtFirstName = jqForm.find('input[type="text"]#myForm1_firstName');
//add controller to form
jqForm.attr('ng-controller', 'MyForm1Controller');
//bind the textbox to the angular 'firstName' variable
jqTxtFirstName.attr('ng-model', "firstName");
$('div').append(jqForm);
$compile(jqForm[0])($scope);
});
}
$scope.loadHtmlForm('myForm1.html');
}]);
// form controller for managing the data
myApp.controller('MyForm1Controller', ['$scope', function($scope) {
$scope.firstName = 'Angular Working!';
}]);

Binding Controller Property containing HTML in AngularJS

First of all: I am absolutely new to AngularJS but worked on MVC-projects in other languages.
I try to bind a Property containing HTML.
This is the code:
HTML:
<div ng-controller="MyController">
<p>{{About}}</p>
</div>
JS:
.controller('MyController', ['$scope', function($scope) {
$scope.About="This is me<br/>and not you!"
}
Now the HTML is encoded which I do not want (the <br/> should result in line breaks)
I already tried <p ng-bind-html="About"></p> but that resulted in no output at all
You need to allow html in your text which Angular does not by default.
Plunker: http://plnkr.co/edit/K4KRCQi4Rpe99MJel5J2?p=preview
Angular Docs for $sce
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires
bindings in certain contexts to result in a value that is marked as
safe to use for that context. One example of such a context is binding
arbitrary html controlled by the user via ng-bind-html. We refer to
these contexts as privileged or SCE contexts.
<div ng-controller="htmlChar" ng-bind-html="about"></div>
<script>
angular.module("app",[])
.controller("htmlChar",function($scope, $sce){
$scope.about= $sce.trustAsHtml("This is me<br/>and not you!");
});
angular.bootstrap(document,["app"]);
</script>
You shoudln't need to insert html through model binding in AngularJS since the philosophy of the framework is to keep the HTML (page's structure and style) intact and only bind the data to be shown inside that HTML.
If you really need to bind HTML tags into your data you need to use the $sanitize service.
You have to use angular compile functionality here, go through the link to get more information angular compile

Angular directive inserts wrong DOM node

I have an app with many forms. Each field has several HTML elements, so I thought I could extract some directives (one per type of field) to keep my forms tidy.
I've created a sample app to demonstrate the problem, but I'm getting inconsistent behavior. In the sample app, a <link /> element replaces the <input />. In my real app, <input /> just gets removed from the DOM completely. I feel like this should be easy; why doesn't it work?
To answer your stated question, it's because you told it to, with ng-transclude. That replaces the contents of the tag with the original element, which I don't think you wanted; you probably wanted the original contents to be transcluded as the label instead.
This is probably what you're looking for:
<div class="form-group" >
<label for="{{htmlId}}" ng-transclude></label>
<input id="{{htmlId}}" class="form-control" type="text" ng-model="model" />
<span ng-repeat="error in errors">{{error}}</span>
</div>
I've moved the tranclusion into the label. While this works, I would also recommend the style of actually passing a label attribute, rather than transclude it, just for the sake of having a consistent API and simpler code; it's functionally equivalent, though, so don't let me bully you.
Also, you've got a few errors in your .js as well. First, you want to use = in your scope instead of &
scope: {
model: '=',
errors: '='
},
& is used to pass methods, while = is used for objects (this is a simplification). Since your model and errors are objects, you'll want to use = instead.
Finally, in your example, your html template and your directive's template don't have the same name... you've got an extra 's' in your .js, but that's probably just in the plunker and not your real app.

Data Binding between pages in Angularjs

I am trying to understand data binding in Angularjs.
What I want to do is establish binding between pages that is if I change the input on first.html, the data should automatically change in second.html.
For example,
This is first.html:
<div ng-controller="MyCtrl">
<input type="text" ng-model="value"/><br>
{{value}}
<input type="submit" value="Second page"/>
</div>
and say second.html has only this piece of code {{value}}.
and in the .js file we have $routeProvider which takes the template url as 'second.html' & the controller is 'MyCtrl'.
So the controller is:
MyApp.controller(function($scope){
$scope.value="somevalue";
})
By doing the above way the {{value}} on the second.html is getting the value "somevalue". Which is comming from the controller.
But if I change the input value dynamically that is on the first.html, the value on the second.html is not getting that value.
My question is how do I bind the value on second.html with first.html automatically.
To understand the question clearly, Suppose there is an input field for entering text and a submit button on first.html, then I want to get the Input value of the text field of the first.html on the second.html page on Submit.
Use a service and store your model there. Gloopy already has a good example of this here: https://stackoverflow.com/a/12009408/215945
Be sure to use an object property instead of a primitive type.
If you'd rather use $rootScope, then as above, define an object, rather than a primitive:
$rootScope.obj = { prop1: "somevalue" }`
then bind to that object property in your views:
<input type="text" ng-model="obj.prop1">
{{obj.prop1}}
If you attach your data to $rootScope if will survive transitions across controllers and be part of all $scopes (prototype inheritance magic)
//**attach in controller1:**
function MyCtrl1($rootScope) {
$rootScope.recs= { rec1 : "think philosophically" };
}
//**do nothing in controller for view2:**
function MyCtrl2($scope) {
//nothing
}
//**Markup for view2: automaticall makes use of data in $routeScope**
<p>Try doing this: {{recs.rec1 }}</p>
//**markup for view1 to respond to OPs question in comments**:
<input ng-model="recs.rec1" />
Update: Creating a custom service is a more scalable and structurally sound way to handle this, but the $rootScope method is the quick and dirty way.
Update2: added view1 markup to respond to OP question, edited example to take advantage of correct advice to use object rather than primitive.
Found the Solution to what I was looking for, The solution is in the Angular docs, here is the link http://docs.angularjs.org/cookbook/deeplinking.
Some part of the example on that link answers my question.
You should user $broadcast, $emit or scope communication. Try to avoid overloading the rootScope. It is as a bad practice as saving data into the application sessions.

Categories

Resources