How to validate form when the forms are inserted using ng-include - angularjs

I have 3 different form pages which are inserted using ng-include into DOM within a bootstrap modal window. What is the best way to do validation in every form and do a complete form submit(for all the 3 forms) in scenario like this?
HTML
<div ng-switch on="page">
<div ng-switch-when="Games">
<div ng-include="'Games.html'"></div>
</div>
<div ng-switch-when="Music">
<div ng-include="'Music.html'"></div>
</div>
<div ng-switch-when="Videos">
<div ng-include="'Videos.html'"></div>
</div>
</div>
Demo : http://plnkr.co/edit/D1tMRpxVzn51g18Adnp8?p=preview

There is to find a way to validate data yet
(may be with jqueryvalidation) but can be a starting point.
I think there is no way to get the value of games.$valid
so I've thought of
var app = angular.module("myApp", [])
app.controller("FormsCtrl", function($scope) {
//console.log($scope);
// $scope.items = ['Games', 'Music', 'Videos'];
$scope.$on('someEvent',function(e,a){
console.log(a);
})
});
app.directive("myform", function() {
return {
restrict: "A",
link:function(scope,element,attrs){
element.bind('submit',function(e){
var isValid = false; // TO DO :)
scope.$emit('someEvent', [attrs.fname,isValid]);
});
}
}
});
<div ng-controller="FormsCtrl">
<div ng-switch on="page">
<div ng-switch-when="Games">
<div ng-include="'Games.html'"></div>
</div>
<div ng-switch-when="Music">
<div ng-include="'Music.html'"></div>
</div>
<div ng-switch-when="Videos">
<div ng-include="'Videos.html'"></div>
</div>
</div>
</div>
<form name="games" class="simple-form" myform fname="games">
<input type="text" ng-model="prefix.games.name" name="uName" required /><br>
</form>
EDIT
A smarter quicker way :)
app.controller("FormsCtrl", function($scope) {
$scope.mySubmit = function(isValid){
console.log(isValid);
}
});
<form name="games" class="simple-form" ng-submit="mySubmit(games.$valid)">
<input type="text" ng-model="prefix.games.name" name="uName" required /><br>
</form>

Related

ng-show not watching variable change

I have a progress bar I want to show after I click a button.
I set my variable to true on click, yet it's not working.
The ng-show in question is on the bottom of the html, and the button i click is on a different html page but i didn't include because it uses the successOnClick function in this same controller. I console logged the isEmailing variable inside the onclick and it is assigned true. Doesn't work for ng-if either
What gives?
module.exports = app => {
app.controller('ContactController', ($scope, $http) => {
$scope.isEmailing = false;
$scope.email = (e) => {
$scope.isEmailing = true;
const requestBody = {};
const id = e.target.id;
requestBody.name = document.getElementById(`${id}-name`).value;
requestBody.email = document.getElementById(`${id}-email`).value;
requestBody.subject = document.getElementById(`${id}-subject`).value;
requestBody.body = document.getElementById(`${id}-body`).value;
$http.post('/email', JSON.stringify(requestBody), {
'Content-Type': 'application/json'
})
.then(res => {
console.log('Success!');
document.getElementById(`${id}-name`).value = '';
document.getElementById(`${id}-email`).value = '';
document.getElementById(`${id}-subject`).value = '';
document.getElementById(`${id}-body`).value = '';
$scope.isEmailing = false;
})
.catch(err => {
console.log('Error!');
$scope.isEmailing = false;
})
}
$scope.successOnClick = () => {
$scope.isEmailing = true;
}
})
}
<footer class="footer" ng-controller="ContactController">
<div class="footer__block social-media-container">
<div class="social-media">
<img src="http://i.imgur.com/bVqv5Kk.png" alt="fb-icon">
<img src="http://i.imgur.com/sJWiCHV.png" alt="twitter-icon">
<img src="http://i.imgur.com/o7yTVyL.png" alt="insta-icon">
</div>
</div>
<div class="footer__block">
<form class="footer__form" ng-submit="email($event)" id="footer">
<textarea placeholder="Message" id="footer-body" required></textarea>
<input type="text" placeholder="Name" id="footer-name" required>
<input type="email" placeholder="Email" id="footer-email" required>
<input type="text" placeholder="Subject" id="footer-subject" required>
<input type="submit" placeholder="Email">
</form>
</div>
<div class="footer__block mailing-list">
<span>Join our Mailing List!</span>
<form>
<input type="email" placeholder="Email" required>
<input type="submit">
</form>
</div>
<!-- <div class="grey-screen">
<div class="success">
<h1>Success!</h1>
</div>
</div> -->
<div class="progress-bar" ng-show="isEmailing">
</div>
</footer>
If you have several controller of same type it is not mean, that they all are same the instance. AngularJS creates controllers not via singleton pattern. You should synchronize them by yourself by means of events:
angular.module('app', [])
.controller('MyController', ['$scope', '$rootScope', function($scope, $rootScope) {
$rootScope.$on('Changed', function(event, data){
$scope.isEmailing = data;
})
$scope.successOnClick = function(){
$scope.$emit('Changed', !$scope.isEmailing);
}
}]);
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MyController">
<h3 ng-style="{'background-color' : isEmailing ? 'red' : 'white'}">First controller</h3>
<input type='button' ng-click='successOnClick()' value='Change color'/>
</div>
<div ng-controller="MyController">
<h3 ng-style="{'background-color' : isEmailing ? 'red' : 'white'}">Second controller</h3>
<input type='button' ng-click='successOnClick()' value='Change color'/>
</div>
</div>
If one controller located inside another you can try to use $scope.$parent.isEmailing(where .$parent can be typed several times, depending on nesting level), but it is very uncomfortable. If your controllers located at different routes, you should pass data from one controler to another via route parameters or custom AngularJS service or $rootScope.

validate dynamic form before submitting angular

I'm dynamically creating forms with ng-repeat and they have some validation attributes (simplified version):
<div class="row" ng-repeat="defect in model.defects">
<form name="form_{{defect.id}}" novalidate>
<input ng-model="defect.name" required/>
<input type="submit" ng-click="saveDefect(defect)"/>
</form>
</div>
Basically what I want to do is this:
$scope.saveDefect = function (defect) {
if ($scope.<how to get the form name here>.$invalid) {
return;
}
}
Since the form name has been created dynamically with an id how do I access it? Other ways of doing the same are also welcome ofcourse :)
You can use the bracket notation to access it :
$scope["form_"+defect.id]
What I advise you to do is :
var app = angular.module("App", []);
app.controller("Ctrl", function($scope) {
$scope.forms = {};
$scope.list = [{id: 1}, {id: 2}];
$scope.save = function(item) {
if ($scope.forms["form_" + item.id].$invalid) {
alert("error on form_" + item.id);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="App" ng-controller="Ctrl">
<div class="row" ng-repeat="item in list">
<form name="forms.form_{{item.id}}" novalidate>
<input ng-model="item.name" required/>
<input type="submit" ng-click="save(item)" />
</form>
</div>
</body>

Convert user input into a label using angular.js

What I'am trying to do is convert every user input into a label using angular. I believe that I'am doing the right thing, but is not working. I will appreciate if somebody take a look at this code. Thank you
here is a plunker
<div class="row" ng-controller="tagForm">
<div ng-click="addEntry()">
<div class="col-xs-12 col-sm-12 col-md-10 ">
<input type="text" placeholder="What are your area of expertise" ng-model="newEntry.name" class="form-control border" />
</div>
<div class="col-xs-12 col-md-2 center form-button">
<input type="button" value="Add" class="btn btn-orange btn-add" />
</div>
<div class="col-md-8 col-sm-offset-2" id="up">
<br />
<span class="label label-primary" ng-repeat="entry in entries">{{entry.name}}</span>
</div>
</div>
</div>
app.controller('tagForm', ['$scope', function($scope) {
return $scope.addEntry = function() {
$scope.entries.push($scope.newEntry);
return $scope.newEntry = {};
};
}]);
You have a few things wrong in your Plunk, but here's some stuff to start with:
You need to wire up a click event on your Add button. Right now, its not doing anything when you click it
Bind to an ng-model on the scope just using 'newEntry'. All you're typing is a name so thats all you need to save on the scope.
Loop over entries, printing out just 'entry' instead of 'entry.name'
And your controller should look like this (no need for the returns)
app.controller('tagForm', ['$scope', function($scope) {
$scope.entries = [];
$scope.addEntry = function() {
$scope.entries.push($scope.newEntry);
};
}]);
See this fiddle:
http://jsfiddle.net/smaye81/anrv2qms/1/

Angular- Creating a directive from existing html content/

Assume
You have html that you can not modify the inner contents of- but you CAN modify the element attributes.
Your goal is to perform 3d transformations on the element that are bidirectionally linked to the value of 3 range inputs.
One input for X rotation, Y Rotation and Z Rotation.
In order to do that, I think I need to use a directive-- but if I use a directive it will erase the existing html...
see this codepen for the current version.
HTML
<html ng-app="truck">
<head></head>
<body>
<section ng-controller="TruckCtl">
<section class="controls">
<fieldset>
<legend>Rotation</legend>
<div>
<label for="xRotation">X:</label>
<input id="xRotation" ng-model="Rotation.x" type="range" min="0" max="360">
<span ng-bind="Rotation.x"></span>
</div>
<div>
<label for="yRotation">Y:</label>
<input name="yRotation" ng-model="Rotation.y" type="range" min="0" max="360">
<span ng-bind="Rotation.y"></span>
</div>
<div>
<label for="zRotation">Z:</label>
<input name="zRotation" ng-model="Rotation.z" type="range" min="0" max="360">
<span ng-bind="Rotation.z"></span>
</div>
</fieldset>
</section>
<section class="wrapper">
<div id="truck" ng-model="Rotation">
</div>
</section>
</section>
</body>
</html>
JS
(function(){
"use strict";
var app = angular.module('truck', []);
app.controller("TruckCtl", function($scope){
$scope.Rotation = {
x: 0,
y: 0,
z: 0
};
});
//no dice v
app.filter("rotate", function() {
return function(input) {
return model.style({
"-webkit-transform" : "rotateX(" + Rotation.x + "deg)"
});
console.log(model);
}
});
//Directives are where I ge lost.
app.directive("Rotation", function(){
return function(scope, element, attrs){
//what do?
}
});
})();
also:
I have no idea why this fiddle doesn't work.
I would recommend to get it working by keeping things simple first. Once you have code that works, you can refactor it out into a filter and directive. The angular docs cover how to implement a directive pretty well, you can basically just copy, paste, and modify. If you have specific questions I'm sure you'll find answers here or elsewhere. As far as simple code to achieve your goal; your controller along with this HTML will rotate as specified:
<div id="truck" style="-webkit-transform: rotateX({{Rotation.x}}deg) rotateY({{Rotation.y}}deg) rotateZ({{Rotation.z}}deg);"></div>
Also, BTW - js convention is to use camelCasing. $scope.Rotation should be $scope.rotation (lowercase r). Use PascalCase for constructors. Although it is purely a preference, you'll find most libraries adhere to this convention.
So the TLDR version is that my nesting was off so my scopes were wrong.
Also I should've been using a factory rather than a filter or a directive.
Working example can be found: here
--one caveat is that it doesn't work until you adjust all the values first.
so just move all of the range sliders to 0 and then change them as you please
html
<section ng-app="truck">
<section id="wrapper" ng-controller="Truck">
<section class="controls">
<fieldset>
<legend>Rotation</legend>
<div>
<label for="xRotation">X:</label>
<input id="xRotation" ng-model="Rotation.x" type="range" min="-100" max="100">
[[Rotation.x]]
</div>
<div>
<label for="yRotation">Y:</label>
<input id="yRotation" ng-model="Rotation.y" type="range" min="-100" max="100">
[[Rotation.y]]
</div>
<div>
<label for="zRotation">Z:</label>
<input id="zRotation" ng-model="Rotation.z" type="range" min="-100" max="100">
[[Rotation.z]]
</div>
</fieldset>
<fieldset>
<legend>Translation</legend>
<div>
<label for="xTranslation">X:</label>
<input id="xTranslation" ng-model="Translation.x" type="range" min="-100" max="100">
[[Translation.x]]
</div>
<div>
<label for="yTranslation">Y:</label>
<input id="yTranslation" ng-model="Translation.y" type="range" min="-100" max="100">
[[Translation.y]]
</div>
<div>
<label for="zTranslation">Z:</label>
<input id="zTranslation" ng-model="Translation.z" type="range" min="-100" max="100">
[[Translation.z]]
</div>
</fieldset>
</section>
<div id="zderp">
<div id="truck" style="-webkit-transform: rotateX([[Rotation.x]]deg) rotateY([[Rotation.y]]deg) rotateZ([[Rotation.z]]deg) translateX([[Translation.x]]px) translateY([[Translation.y]]px) translateZ([[Translation.z]]px)">
</div>
</div>
</section>
</section>
js
var app = angular.module('truck', []).config(function($interpolateProvider){
"use strict";
$interpolateProvider.startSymbol('[[').endSymbol(']]');
});
app.factory('Rotation', function(){
return {
x : '1',
y : '1',
z : '1'
}
});
function TruckCtl($scope, Rotation){
$scope.x = Rotation.x;
$scope.x = Rotation.y;
$scope.x = Rotation.z;
}
function Truck($scope, Rotation){
$scope.x = Rotation.x;
$scope.x = Rotation.y;
$scope.x = Rotation.z;
}

Advice on the correct use of ngModel

I' new to AngularJS and have a following ambiguity with usage of ngModel. I want to give to the user possibility to generate unlimited number of "name": "value" pairs. So I generating div with ng-repeat for every element from pair. Here is my html:
<div ng-app>
<div ng-controller="TestCtrl">
<input type="button" value="+" ng-click="addNewRow();"/>
<div ng-repeat="a in range(itemsNumber)"><input type="text" name="key"/> : <input type="text" name="value"/></div>
</div>
</div>
And the JavaScript:
function TestCtrl($scope) {
$scope.itemsNumber = 1;
$scope.range = function() {
return new Array($scope.itemsNumber);
};
$scope.addNewRow = function () {
$scope.itemsNumber++;
}
};
Here is working js fiddle:
http://jsfiddle.net/zono/RCW2k/
I want to have model for this generating items but not sure how to do it.
I would appreciate any ideas and tips.
Best regards.
Edit:
I have create other solution. It can be viewed in this fiddle
http://jsfiddle.net/zono/RCW2k/8/
But is this solution is good idea?
Here is a fiddle: http://jsfiddle.net/RCW2k/13/
You should just create an array on the scope and it's also your model:
controller:
function TestCtrl($scope) {
$scope.items = [{key:"hello",value:"world"}]
$scope.addNewRow = function () {
$scope.items.push({key:"",value:""});
}
};
html:
<div ng-controller="TestCtrl">
<input type="button" value="+" ng-click="addNewRow();"/>
<div ng-repeat="item in items">
<input type="text" name="key" ng-model="item.key"/> :
<input type="text" name="value" ng-model="item.value"/>
</div>
</div>

Resources