fold / unfold form fields in AngularJS - javascript

I just started with Angular and have lots of doubts. Right now I have a form with several fields, check image:
I want to include a link under Keywords Label-input field, something like "Advanced" and if clicked then Must not include and Must include form fields fold / unfold, or appear/dissapear.
That said, I'm not sure if I have to use ng-show or ng-hide in the fields to be shown / hidden and then use ng-click on the "Advanced" link to make change their status. Something like this for instance:
<div class="cg">
<label class="cl">Keywords</label>
<div class="controls">
<input id="id_search"
ui-select2 = "{multiple: true, simple_tags: true, tags: []}"
/>
<div class="qs"
popover-placement="left"
popover-trigger="mouseenter"
popover="Type your search"></i></div>
<a ng-click="changeStatus()">Advanced</a>
</div>
</div>
<div class="cg" ng-show="form_element.status">
<label class="cl">Must not include</label>
<div class="controls">
<input id="id_must_not_include"
class="input-block-level"
ng-disabled="tagItem.perm == 'r'"
ui-select2 = "{multiple: true, simple_tags: true, tags: []}"
ng-model="not_include"/>
<div class="qs"
ng-show="is_modal"
tooltip="Must not include words."
tooltip-placement="left"><i class="icon-question-sign"></i></div>
<span class="help-block" ng-hide="is_modal"></span>
</div>
</div>
<div class="cg" ng-show="form_element.status">
<label class="cl">Must include</label>
<div class="controls">
<input id="id_must_include"
class="input-block-level"
ng-disabled="tagItem.perm == 'r'"
ui-select2 = "{multiple: true, simple_tags: true, tags: []}"
ng-model="must_include"/>
<div class="qs"
ng-show="is_modal"
tooltip="Must include words."
tooltip-placement="left"><i class="icon-question-sign"></i></div>
<span class="help-block" ng-hide="is_modal"></span>
</div>
</div>
Is this the correct way to do this? Should I use a different approach? Some help would be great!
Thanks!
Alejandro
NOTE: For other people in similar situation, check this jsbin to see how to do this

Ng-show and ng-hide are used for manipulating the view as you would like. The error most people do when starting angular is that they directly try to target content to show/hide instead of creating states of the form. That state of the form is what will dictate the view of your form. All of this can happen in your controller to keep it nice and readable For example:
App.controller('Ctrl1', function Ctrl1 ($scope, TestService) {
$scope.isAdvancedState = false;
$scope.changeStatus = function() {
$scope.isAdvancedState = true;
}
});
You are totally on the right track with this. Angular is a little strange at first but quickly becomes pretty amazing. Stick with it :)
Thanks,
Jordan

What you have there seems like it would work alright assuming the changeStatus() call would do something like: is_modal = !is_modal; You could definitely define your template a little more simply. For example, wrap both of the advanced fields in a single div with a single ng-show="is_modal" then you wouldn't have to deal with the extra span, but it just depends on what you're trying to accomplish with your markup...
But, it looks like what you have there should work just fine.

Related

Submit with button, on enter, outside of form with only Angular

What solution would you implement for an Angular "form", where you wanted it to submit on an enter key press, but you can't rearrange the elements to fit in a <form> element. For instance something like this:
<div>
<input/>
<input/>
//This might act as a kind of custom built select
<ul>
<li></li>
</ul>
<form></form> //Dropzone form for instance.
<button type="submit" ng-click="submitFunction()">Submit</button>
</div>
I want the user to be able to hit enter while the focus is on any of these elements(Not just the true form elements), and have the form submit(validation passing of course). The solution should be purely Angualr(if possible). I'd rather it didn't use complex CSS positioning, I don't want to have to rearrange and muddle the html structure.
I saw a solution online for a custom "ngEnter" type directive that I thought had promise but I don't think everyone on the team is gonna like that solution. I'd like to know how else one might approach this.
See this article for more info.
Here's a snippet showing how to conditionally log to the console using ng-keypress:
angular.module('myExample', [])
.controller('myController', ['$scope', function($scope) {
$scope.myFunction = function() {
console.log('hi!');
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myExample">
<div ng-controller="myController">
<input placeholder="type something" ng-keypress="$event.which === 13 && myFunction()">
</div>
</div>

Disabling submit button based on fields added with ng-bind-html

JSFiddle here: http://jsfiddle.net/c6tzj6Lf/4/
I am dynamically creating forms and buttons and want to disable the buttons if the required form inputs are not completed.
HTML:
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
JavaScript:
angular.module('choicesApp', ['ngSanitize'])
.controller('ChoicesCtrl', ['$scope', '$sce', function($scope, $sce) {
$scope.custom = "Required Input: <input required type='text'>";
$scope.trustCustom = function() {
return $sce.trustAsHtml($scope.custom);
};
$scope.buttons = [
{text:'Submit 1'},
{text:'Submit 2'}];
}]);
choicesForm.$invalid is false and does not change when entering text into the input field.
Solution:
I ended up using the angular-bind-html-compile directive from here: https://github.com/incuna/angular-bind-html-compile
Here is the relevant bit of working code:
<ng-form name="choicesForm">
<div ng-if="choices" bind-html-compile="choices"></div>
<button ng-click="submitForm()" ng-disabled="choicesForm.$invalid">
Submit
</button>
</ng-form>
And choices might be a snippit of HTML like this:
<div><strong>What is your sex?</strong></div>
<div>
<input type="radio" name="gender" ng-model="gender" value="female" required>
<label for="female"> Female</label><br>
<input type="radio" name="gender" ng-model="gender" value="male" required>
<label for="male"> Male</label>
</div>
The main problem is that ngBindHtml doesn't compile the html - it inserts the html as it is. You can even inspect the dynamic input and see that it doesn't have the ngModel's CSS classes (ng-pristine, ng-untouched, etc) which is a major red flag.
In your case, the form simply doesn't know that you've added another input or anything has changed for that matter. Its state ($pristine, $valid, etc) isn't determined by its HTML but by the registered NgModelControllers. These controllers are added automatically when an ngModel is linked.
For example this <input required type='text'> won't affect the form's validity, even if it's required, since it doesn't have ngModel assigned to it.
But this <div ng-model="myDiv" required></div> will affect it since it's required and has ngModel assigned to it.
The ngDisabled directive on your buttons works as expected since it depends on the form's $invalid property.
See this fiddle which showcases how ngModel registers its controller. Note that the html containing the dynamic input gets compiled after 750ms just to show how NgModelControllers can be added after FormController has been instantiated.
There are a few solutions in your case:
use a custom directive to bind and compile html - like this one
use ngInclude which does compile the html
use $compile to compile the newly added HTML but this is a bit tricky as you won't know exactly when to perform this action
This is an answer yet imcomplete because i cannot do the code at the moment.
I think your html will be included, not compiled. So the inputs are not bind to angular and are not part of the angular form object.
The only way i see is to use a directive that will compile the passed html and add it to your form. This may be quite tricky though, if you want to go on this way i suggest to edit your question to ask for the said directive.
However i'm not really familiar with $compile so i don't know if it'll work to just add $compile around $sce.trustAsHtml()
You can write a method as ng-disabled does not work with booleans, it works with 'checked' string instead:
So on your controller place a method :
$scope.buttonDisabled = function(invalid){
return invalid ? "checked" : "";
};
And on your view use it on angular expression :
<button ng-repeat="button in buttons" ng-disabled="buttonDisabled(choicesForm.$invalid)">
Here is a working fiddle
Working DEMO
This is the solution you are looking for. You need a custom directive. In my example I have used a directive named compile-template and incorporated it in div element.
<div ng-bind-html="trustCustom()" compile-template></div>
Directive Code:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
I found the directive in this fiddle.
I believe what is really happening though due to jsfiddle I'm unable to dissect the actual scopes being created here.
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
The first div is your top level scope, your form is the first child scope. Adding the div using a function creates the dynamically added input field as a child of the first child, a grandchild of the top level scope. Therefore your form is not aware of the elements you're adding dynamically causing only the static field to be required for valid form entry.
A better solution would be to use ng-inclue for additional form fields or if your form isn't to large then simply put them on the page or template you're using.

AngularJS ng-change on select with custom directive

In my application I have a custom directive with select tag.
<div class="row pv10" ng-if="item.visible">
<div class="col-xs-4 text-danger"><span ng-if="item.label_visible"> {{item.label}}</span></div>
<div class="col-xs-8">
<div class="row">
<div class="col-xs-1">
<select ng-options="oneItem for oneItem in item.options" ng-model="selectedItem" ng-change="changeEvent()">
{{selectedItem | json}}
</select>
</div>
</div>
</div>
</div>
I would like to perform an action on every change event. I thought of using ng-change, which btw. worked just fine with radioBtn/checkBox. I tried two approaches here.
Firstly I tried to pass "changeEvent" function to the directive's isolated scope.
<custom-select item='item' change-event="change()" ng-if="item.component == 'select'" />
changeEvent() function exists in current controller (attached to the view holding custom-select)
scope: {
item: "=",
changeEvent: "&"
}
Sadly expression does not evaluate on change.
Secondly I tried to attach the function directly to directive
link: function ($scope, elems, attrs) {
$scope.changeEvent = function () {
alert("Change Event!");
}
}
but it didn't work either.
I read somewhere that ng-change even used within a custom directive operates on a controllers scope, not isolated scope of directive. However am not quite sure about that.
I'd really appreciate your help.
EDIT!
This is a JSFiddle to the directive I use. If I should provide more details, just let me know, this is the first time I've ever used jsfiddle.
This is how the directive is invoked:
<div class="row" ng-repeat="item in jsonData">
<div class="col-xs-12">
<cbp-select item='item' change="change()" ng-if="item.component == 'select'" />
</div>
</div>
https://jsfiddle.net/60nsfg0L/
EDIT2
When I have uploaded my solution to our local server it started to work properly.(Yes, I cleaned browser's cash, and my locals wamp/iss cache too).
But now I have a different problem.
I have a form built from different directives(like the one above), all have ng-click/change/blur. Everything seems to work fine until I change one of the options in my "select" directive. After selecting one of the values, ng-change expression is triggered (for not its just a simple alert) and from that moment onwards am not able to focus on any other elements. Weirdly enough, if I cause ng-change to trigger on any other element be it radiobtn or checkbox this problem does not occur.

Input checked has not working in side partial in angular

I m beginner in Angular. I m working on a angular project. I have a input checkbox in my partial source. When it checked a popup should appear, not checked state have a popup.
I found the below code on google and it works separately. But when i put it my partial source it doesn't work.
<div>
<div >
<input type="checkbox" ng-true-value="A" ng-false-value="B" ng-model="check"/>
</div>
<div ng-show="check == 'A'">
Checked
</div>
<div ng-show="check == 'B'">
Unchecked
</div>
Can you anyone help me
Assuming You want to access the parent controller value inside partial which has been loaded using ng-controller directive, In that you need to follow dot rule in your code, As a ng-controller create a new child scope whenever it adds the specified template.
For solving issue you should have your data in object structure in-order to follow prototypal inheritance
Code
$scope.model = {};
Markup
<div>
<div >
<input type="checkbox" ng-true-value="A" ng-false-value="B" ng-model="model.check"/>
</div>
<div ng-show="model.check == 'A'">
Checked
</div>
<div ng-show="model.check == 'B'">
Unchecked
</div>
Other way to do it would be is you can simply use $parent before the check scope variable, that will refer to the parent scope of the controller.
Just replace check with $parent.check everywhere in the view, This will work but the first approach is most preferable.
Add single quotes to 'A' and 'B'
<div >
<input type="checkbox" ng-true-value="'A'" ng-false-value="'B'" ng-model="check"/>
</div>

Symfony UniqueEntity Constraint and javascript

i'm new to symfony, and don't know much about javascript!
i created a symfony form, and added a UniqueEntity constraint on the name and firstname, so it's not possible to add a same person twice in the database.
#UniqueEntity(fields={"firstname","name"}, message="this person already exists")
it works pretty well!
but in this case, i would like symfony to show me a javascript window with a message and 2 choices. for example: "joe smiley already exists! would you like to add an homonym? yes / no
does anyone know how to do this?
Much thanks
No, this validation is strictly server-side.
You should try some JS validation libraries like: http://rickharrison.github.io/validate.js/ (just an example)
well you could try to find if is there and error message for that field, and that will depend on how are you displaying your form fields, i strongly strongly recommend you to customize your form rendering to suit your needs, here is and example of what can you do.
<div class="control-group {% if(form_errors(form.descripcion)|length) %} error {% endif %} ">
{{ form_label(form.descripcion, null, { 'label_attr': {'class': 'control-label'} }) }}
<div class="controls">
{{ form_widget(form.descripcion) }}
{{ form_errors(form.descripcion) }}
</div>
</div>
that will render this when the validation shows and error message
<div class="control-group error ">
<label class="control-label required" for="AreaComercio_descripcion">Descripcion</label>
<div class="controls">
<input type="text" id="AreaComercio_descripcion" name="AreaComercio[descripcion]" required="required" >
<span class="help-inline">This value should not be blank.</span>
</div>
</div>
so you have to ask if is there an span sibling with the class help-inline if you dont know how to custimze your form rendering take a look at this and another advice use jquery, there are lots of people using it, jquery have an strong comunity that will help you if you are in the need, that said, i will use jquery to ilustrate what you can do to solve your problem.
if($("#AreaComercio_descripcion ~ .help-inline").length) { // here using sibling selector filter to ask if there a siblig with the class help-inline
var invalid_value = $("#AreaComercio_descripcion").val(); // asking for the field value thru its id attribute
confirm(invalid_value+" already exists! would you like to add an homonym ?");
}
using javascript confirm is the easiest way to do what you want but you can use other more flexible options like jquery dialog or you could try to install twitter bootstrap in your symfony installation, well i guess thats all i hope it will be usefull to you

Categories

Resources