I'm working on the React.js tutorial here: https://facebook.github.io/react/docs/tutorial.html
I have everything working as expected, but for some reason, when I apply additional HTML to my form with classes, the styling doesn't apply. I have checked out a bit of additional documentation, but can't find a reason why the additional HTML won't render.
The form elements will render, but nothing with the 'large-XX columns' classes will render.
Any thoughts?
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var creator = this.refs.creator.value.trim();
var mr = this.refs.mr.value.trim();
var message = this.refs.message.value.trim();
var csrfmiddlewaretoken = this.refs.csrfmiddlewaretoken.value.trim();
if (!creator || !mr || !message || !csrfmiddlewaretoken ) {
return;
}
this.props.onCommentSubmit({creator:creator, mr:mr, message:message, csrfmiddlewaretoken: csrfmiddlewaretoken})
this.refs.creator.value = '';
this.refs.mr.value = '';
this.refs.message.value = '';
return;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<div class="row">
<div class="large-10 columns">
<input type="hidden" ref="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<input type="hidden" ref="creator" value="{{ globalEmployee.id }}" />
<input type="hidden" ref="mr" value="{{ mr.id }}" />
<input type="text" ref="message" placeholder="Add a comment..." />
</div>
<div class="large-12 columns">
</div>
<div class="large-2 columns">
<input type="submit" value="Post" class="button tiny expand" />
</div>
</div>
</form>
);
}
});
Instead of class use className as you did in the form element.
From HTML Tags vs. React Components:
Since JSX is JavaScript, identifiers such as class and for are
discouraged as XML attribute names. Instead, React DOM components
expect DOM property names like className and htmlFor, respectively.
Related
I have the code below (or see the fiddle) and I try to reset form and clear all input fields too for each form.
With AngularJS version 1.2.1 is working fine! but in my app I using version 1.5.5 because I have other libraries for separating the nested forms in <md-tab> tags using material framework who need this version.
The problem is when any field it's invalid then reset does not work as expected and these fields remain unchanged instead to be clear.
There is another way to clearing all fields (of nested form) when I click reset button ?
angular.module("main", [])
.controller("register", function($scope) {
$scope.data = {
A: {},
B: {}
};
$scope.reset = function(form) {
form.$setPristine();
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<body ng-app="main">
<div ng-controller="register" class="form">
<form name="form" novalidate role="form">
<div class="form">
<h1>TAB1 - Form A:</h1>
<ng-form name="A">
A1:
<input type="text" ng-model="data.A.A1" ng-minlength="4" ng-maxlength="15" />A2:
<input type="text" ng-model="data.A.A2" ng-minlength="4" ng-maxlength="15" />
<button type="button" ng-disabled="A.$pristine" ng-click="reset(A); data.A=null;">Reset</button>
<br/>
<strong>A.$pristine =</strong> {{A.$pristine}}
<strong>A.$valid =</strong> {{A.$valid}}
</ng-form>
</div>
<br/>
<br/>
<div class="form">
<h1>TAB2 - Form B:</h1>
<ng-form name="B">
B1:
<input type="text" ng-model="data.B.B1" ng-minlength="4" ng-maxlength="15" />B2
<input type="text" ng-model="data.B.B2" ng-minlength="4" ng-maxlength="15" />
<button type="button" ng-disabled="B.$pristine" ng-click="reset(B); data.B=null;">Reset</button>
<br/>
<strong>B.$pristine =</strong> {{B.$pristine}}
<strong>B.$valid =</strong> {{B.$valid}}
</ng-form>
</div>
</form>
<h1>data:</h1>
<pre>{{data | json}}</pre>
</div>
</body>
It's because ng-model binds to the A and B objects by reference in $scope.data. If you remove the $scope.data.A = null from your ng-click and reset the object without creating a new one, it works:
https://jsfiddle.net/utwf604r/15/
$scope.reset = function (form)
{
// don't change the reference to A
// $scope.data.A = {} wont work!!
angular.extend($scope.data, {A:{A1:'',A2:''}, B:{B1:'',B2:''}});
form.$setPristine();
};
I'm trying to build a directive for my Angular to help with the integration of form fields. I've implemented Scott Allens solution from his Angular playbook, and it works fine for a normal stacked form.
I need however to adapt it to a horizontal form instead. Here's my code:
Markup
<div form-group>
<label for="name">Name</label>
<input type="text" id="name" ng-model="vm.name">
</div>
formGroup directive
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
var label = element.querySelector("label");
label.classList.add("control-label");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
}
element.classList.add("form-group");
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
The output becomes:
<div form-group="" class="form-group">
<label for="name" class="control-label">Name</label>
<input type="text" id="name" ng-model="vm.name" class="form-control">
</div>
And that's fine for stacked form. Since I need a horizontal form, my output needs to look like this:
<div form-group="" class="form-group">
<label for="name" class="control-label col-sm-3">Name</label>
<div class="col-sm-9">
<input type="text" id="name" ng-model="vm.name" class="form-control">
</div>
</div>
I've tried many solutions and I can get it work with single elements like an input, textarea or a select. It becomes much more tricky when I have something like two radio buttons inside my markup like this:
<div form-group>
<label>Active</label>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
</label>
</div>
</div>
The desired output of the above mentioned code should be:
<div form-group class="form-group">
<label class="control-label col-sm-3">Active</label>
<div class="col-sm-9">
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
</label>
</div>
</div>
</div>
Please notice that the input(s) in the form-group is not fixed. It can be either a single input, textarea, select, a group of radio buttons or checkboxes. I'm lost for how I can make that happen. Any help is appreciated. Thanks!
UPDATE
I made some small changes to Mark Veenstra's code to make it (sort of) working:
function setupDom(element) {
element.classList.add("form-group");
var label = element.querySelector("label");
label.classList.add("control-label", "col-sm-3");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
angular.element(input).wrap(angular.element('<div class="col-sm-9"></div>'));
}
var div_radio = element.querySelector("div[class='radio']");
angular.element(div_radio).wrap(angular.element('<div class="col-sm-9"></div>'));
}
This does not work completely as intended with multiple radio inputs since it only wraps the <div> on the first radio input element.
The output from radio button example in my original post using Marks code is:
<div form-group="" class="form-group">
<label class="control-label col-sm-3">Active</label>
<div class="col-sm-9">
<div class="radio">
<label>
<input type="radio" name="active" ng-value="true" ng-model="vm.active" value="true"> Yes
</label>
</div>
</div>
<div class="radio">
<label>
<input type="radio" name="active" ng-value="false" ng-model="vm.active" value="false"> No
</label>
</div>
</div>
SOLUTION
Check out the Plunker with the final result: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview
In the angular.element documentation you can find the method wrap() to be able to wrap HTML around a selected element. Or see this direct link.
So what you could do in your directive is change the setupDom() function to match your requirements per type of form element.
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
element.classList.add("form-group");
var label = element.querySelector("label");
label.classList.add("control-label col-sm-3");
var input = element.querySelector("input, textarea, select");
var type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox"){
input.classList.add("form-control");
input.wrap(angular.element('<div class="col-sm-9"></div>'));
}
var div_radio = element.querySelectorAll("div[class='radio']");
div_radio.wrap(angular.element('<div class="col-sm-9"></div>'));
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
NOTE: This code is not tested, maybe there are some minor mistakes, but I guess you'll get the point now.
Mark's suggestion came close, but it didn't solve my problem completely. I ended up using the following code in my formGroup directive:
(function (module) {
"use strict";
function link(scope, element) {
setupDom(element[0]);
}
function setupDom(element) {
element.classList.add("form-group");
var children = angular.element(element).children();
var labels = children.splice(0, 1);
// Set label classes
labels[0].classList.add("control-label", "col-sm-3");
// Wrap children in div
angular.element(children).wrapAll(angular.element("<div class='col-sm-9'></div>"));
// Handle inputs
var inputs = element.querySelectorAll("input, textarea, select");
for (var i = 0, len = inputs.length; i < len; i++) {
var input = inputs[i],
type = input.getAttribute("type");
if (type !== "radio" && type !== "checkbox") {
input.classList.add("form-control");
}
}
}
function formGroup() {
return {
restrict: "A",
link: link
}
}
module.directive("formGroup", formGroup);
}(angular.module("app.core")));
Check out this Plunker to see it in action: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview
I've got a form with a text field and a few checkboxes. It works perfectly fine if I fill out the <input type="text"> field first, but if I mark the checkboxes first, the checkboxes aren't passed to my function on ng-submit().
HTML
<form ng-submit="createService(service)">
<input type="text" ng-model="service.name" required>
<div ng-repeat="provider in providers">
<md-checkbox id="{{ provider }}" ng-model="service.providers[provider.$id]">{{ provider.name }}</md-checkbox>
</div>
<button type="submit" class="h4 button button-big mb4" aria-label="Add Service">Add Service</button>
</form>
JS
$scope.createService = function(service) {
var checkedProviders = {};
var serviceProviders = service.providers;
for (var checked in serviceProviders) {
if (serviceProviders[checked]) {
checkedProviders[checked] = true;
}
}
services.$add({
name: service.name,
providers: checkedProviders
});
};
It's very weird and I can't work out why it is doing this. Any help on why this is happening is appreciated.
Thanks in advance.
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;
}
HTML:
<ul>
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
<form>
<input type="text" value="{{ selectedUser.firstname }}" ng-model="selectedUser.firstname">
<input type="text" value="{{ selectedUser.lastname }}" ng-model="selectedUser.lastname">
</form>
I am dealing with user objects fetched from my REST API. So basically there is a list of users. On click the above form is revealed.
function UserController($scope, User){
$scope.users = User.query();
$scope.selectedUser = null;
$scope.select = function(user){
$scope.selectedUser = user;
}
}
I want to display the save link only when form values have changed. Any ideas how to do this with angular.js?
Give your form a name, such as:
<form name="dataForm">
<input type="text" name="firstname" ng-model="data.firstname" />
<input type="text" name="lastname" ng-model="data.lastname" />
</form>
The form will now be a named model in your scope and you can hide/show the save button based on whether the form is pristine:
<ul ng-hide="dataForm.$pristine">
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
This approach has the advantage of showing the save button if you change any of the form elements inside the form and the drawback of not checking the input values against their original values, just the fact that they have been edited.
Here is an example of showing your element only when both fields have data:
<div ng-controller="TestCtrl" ng-app>
<ul ng-show="enableSave(data)">
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
<form>
<input type="text" name="firstname" ng-model="data.firstname" />
<input type="text" name="lastname" ng-model="data.lastname" />
</form>
</div>
And here is your controller:
function TestCtrl($scope) {
$scope.data = {firstname: "", lastname: ""};
$scope.enableSave = function(data) {
return data.firstname.length > 1 && data.lastname.length > 1;
};
}
You can put any logic you want into enableSave. I've chosen to require that they both have at least two characters... you can do whatever you need.
Here is a jsFiddle that illustrates it: http://jsfiddle.net/nDCXY/1/
EDIT by OP: my solutions
$scope.enableSave = function(user) {
if(!angular.equals(user, oldUser)){
return true
}else{
return false;
}
};