Issue dynamically creating inputs with angular attributes in an ng-repeat loop? - json

I'm trying to create a directive which takes some JSON data, and creates a form. Each of the inputs in the form are contained in a <div> wrapper. The wrapper also contains a <div> that appears when ng-show is used. The <div> using ng-show is for dynamically displaying errors.
HTML for the Directive:
<div class="input-wrapper" ng-repeat="inputData in contactCtrl.formInputData">
<input type="text" class="text-input" placeholder="{{ inputData.placeholder }}" ng-model="inputData.model" />
<div class="error-bubble" ng-show="inputData.showFunction">
</div>
</div>
HTML on the Page:
<div id="column-right">
<h2>
Send me an Email
</h2>
<contact-form></contact-form>
</div>
Directive Creation:
spaModule.directive("contactForm", function() {
return {
restrict: "E",
templateUrl: "partials/directives/contact-form.html"
}
});
JSON Data and a Validation Function:
this.formInputData = [
{
placeholder: "Name",
model: "contactCtrl.clientName",
showFunction: "!contactCtrl.validateName()"
},
{
placeholder: "Email",
model: "contactCtrl.email",
showFunction: "!contactCtrl.validateEmail()"
},
{
placeholder: "Subject",
model: "contactCtrl.subject",
showFunction: "!contactCtrl.validateSubject()"
}
];
this.validateName = function() {
if (this.clientName !== "") {
this.nameError = "Name is required!";
return true;
} else {
return false;
}
};
Not a single piece of this is working. The placeholder is not being rendered correctly, ng-show is not doing anything, and the ng-model is not working. I've tried reformatting this as normal HTML in my page and everything works flawlessly.
The issue appears to be declaring ng attributes with ng-repeat. What am I doing wrong?

Related

Exporting VueJS-rendered HTML with checkbox fails to preserve checked state

My component template contains the following checkbox code:
<div ref="htmlData">
<input
type="checkbox"
class="mycb"
:id="uniqID"
:disabled="disabled"
v-model="cbvalue"
>
</div>
(parts removed for simplicity).
I need to create a PDF out of this template (on server). This is what i'm doing in the code:
methods : {
save () {
let saveData = {
'html': this.$refs.htmlData.innerHTML
};
this.$http.post('/api/save',saveData);
}
}
However, the saved HTML doesn't contain checkbox state, so it always saves an unchecked checkbox.
Here's a slightly modified jsfiddle.
My question is: how can I capture the checkbox state in the rendered HTML?
I tried adding :checked="cbvalue" prop - no luck
It looks like there's no way to bind the checked attribute of an input; Vue does everything through the property. (For reference, the property is the internal state, the attribute is what shows up in the HTML.)
To get the attribute to reflect the property, you can add a little directive.
var demo = new Vue({
el: '#demo',
data: () => ({
val: false
}),
methods: {
save() {
console.log(this.$refs.main.innerHTML);
}
},
directives: {
explicitChecked: {
update(el) {
if (el.checked) {
el.setAttribute('checked', 'checked');
} else {
el.removeAttribute('checked');
}
}
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="demo">
<button #click="save">save</button>
<div ref="main">
<input type="checkbox" v-model="val" v-explicit-checked>
</div>
</div>

How to get different ng-model values in ng-include of the same HTML file

There are two html files:
index1.html
<input type="text" ng-model="model.Name">
<!-- Some complex form !-->
index.html
<div>Journalist's Details:
Name:<div ng-include="index1.html"></div>
</div>
<div ng-if="isCompanionNeeded">Journalist's Companion Details:
Name:<div ng-include="index1.html"></div>
</div>
Here ,I'd want "Journalist.Name" in place of the "model.Name" for the "Journalist's Details:" part of the form and "Companion.Name" in place of "model.Name" for the "Journalist's Companion Details:
" part , as using the same model name will just get me same values in both the name fields and any other fields.
If you are using AngularJS v1.5+, you can use component instead of ng-include. This is an example of a component you could create
Component JS
angular
.module('app')
.component('myDetailsComponent', {
bindings: {
data: '<'
},
controller: function () {
// do whatever you need with your input here
},
templateUrl: 'my-details.component.html'
});
Component Template
<input type="text" ng-model="$ctrl.data.Name">
View
<div>
<p>Journalist's Details:</p>
<my-details-component data="companion"></my-details-component>
</div>
Edit
If you are using an older version of AngularJS, you can replicate the same functionality with a directive. For example:
Directive JS
angular
.module('myDetailsDirective')
.directive('myDetailsDirective', myDetailsDirective);
function myDetailsDirective() {
return {
restrict: 'E',
scope: {
data: '='
},
templateUrl: 'my-details.directive.html',
link: function () {
// do whatever you need with your input here
}
};
}
Directive Template
<input type="text" ng-model="data.Name">
View
The usage of the directive is exactly the same as in the case of the component:
<div>
<p>Journalist's Details:</p>
<my-details-directive data="companion"></my-details-directive>
</div>
The ng-model in the index.html needs to have something like a companion.Name object in order to be accessible.
Create a file called index2.html with
<input type="text" ng-model="companion.Name">
and include it in your index.html
<div>Journalist's Details:
Name:<div ng-include="index1.html"></div>
</div>
<div ng-if="isCompanionNeeded">Journalist's Companion Details:
Name:<div ng-include="index2.html"></div>
</div

Angular JS - Add ng-model to directive which is called in form

I'm trying to make a date picker which is filling an input text for a form. I've created a directive for that which is called "datepicker".
But now I've a problem, I can't use "ng-model" on directive and I don't know how to solve this problem.
The input in the datepicker is need to be filled by the scope "projetData.dateConception" and need to be read by the form.
DatePickerDirective.js:
app.directive("datepicker", function(){
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {},
templateUrl: './partials/datepicker.html',
link: function(scope, element, attrs){
scope.datepicker = {};
scope.datepicker.montListShowed = false;
.
.
.
addProjetView.html
<div class = "fiche-line" ng-show="displayField.DateConception">
<span class="fiche-field-title">Date Conception : </span>
<datepicker ng-model="projetData.dateConception" ></datepicker>
</div>
<div class = "fiche-line" ng-show="displayField.DateSolution">
<span class="fiche-field-title">Date Solution : </span>
<input type="date" ng-model="projetData.dateSolution" >
</div>
datepicker.html (template of the directive)
<div class="datepicker-relative datepicker-no-touch">
<input ng-model="datepicker.dateSelected" class="inputDate" ng-focus="datepickerVisible = true" type="text"></input>
<div class="datepicker-popup" ng-show="datepickerVisible">
.
.
.
The inputs in the form:
The datepicker which is filling the input
In conclusion, I want to made the input generated by <datepicker> able to read "projetData.dateConception", modify it. And then the form, on submit, send it to my DB.(already done)
Tanks for your respons, it works now !
This is how is my code now :
DatePickerDirective.js :
app.directive("datepicker", function(){
return {
restrict: 'EA',
replace: true,
require: 'ngModel',
transclude: true,
scope: { ngModel : '='},
templateUrl: './partials/datepicker.html',
link: function(scope, element, attrs){
scope.datepicker = {};
addProjetView.html
<div class = "fiche-line" ng-show="displayField.DateConception">
<span class="fiche-field-title">Date Conception : </span>
<datepicker ng-model="projetData.dateConception" ></datepicker>
</div>
datepicker.html (template of the directive)
<div class="datepicker-relative datepicker-no-touch">
<input ng-model="ngModel" class="datepicker-input-date" type="date"></input>
</div>

How to create a separate scope isolated from ng-repeat in Angular?

I am new to AngularJS and have some trouble understanding the concept of scope in Angular. I have read some posts on stackoverflow as well as online articles, which advise me to create a custom directive to create an isolate scope, but I am getting nowhere...
As for the project I'm working on, I am trying to make a button that when clicked, will trigger a textarea. However, because of ng-repeat, the textarea is triggered for all buttons while I click only one.
My .js file:
angular.module('myApp')
.controller('myCtrl', function ($scope, Question) {
scope.visible = false;
scope.toggle = function() {
scope.visible = !scope.visible;
};
.directive("myDirective", function () {
return {
scope: {
ngClick: '&',
ngShow: '&'
}
}
});
Here is my HTML file:
<ul>
<li ng-repeat="object in objectList">
<button type="text" myDirective ng-click="toggle()">Click</button>
<textarea myDirective ng-show="visible"></textarea>
</li>
</ul>
Angular is creating child (NOT isolated) scope when ng-repeating, try this out, when you ng-init a variable, it is only visible within that repeat div.
<div ng-repeat="i in [0,1,2,3,4,5,6,7,8,9]" ng-init="visible=false">
<button ng-click="visible=!visible">Toggle</button>
<h1 ng-show="visible">look at me!</h1>
</div>
Plunker
There is no need to use a directive. You need to use object in the foreach to refer each item in the loop.
Add visible to each object in objectList:
$scope.objectList = [
{ visible: false },
{ visible: false },
{ visible: false }
];
Then the toggle button will need to pass the object to toggle:
$scope.toggle = function (object) {
object.visible = !object.visible;
};
The ng-show will need to check object.visible and ng-click will need to pass the object:
<button type="text" ng-click="toggle(object)">Click</button>
<textarea ng-show="object.visible"></textarea>
Plunkr

How to control ng-repeat divs from ng-repeat inputs

So, just getting started in Angular and it's pretty tricky, coming from a pretty simple JS and jQuery background. Here's what I'm trying to do. I have a "tag template" that has a couple categories and then some sub-tags contained within. I have defined these as an object, with the idea that the object/file can be called via file request and manipulated, etc.
I have loaded labels and tag category inputs dynamically by using a factory service and a controller with ng-repeat. Likewise, I have deposited the subtags into another div on page2 (using jQuery mobile page swiping). I'd like to use the checkbox state of the category tags to show/hide the sub-tags on page2.
I have tried dozens of things and searched all over stackexchange, the net, etc, but is simple and straightforward and similar enough for me to get it working. If someone can point me in the right direction, that would be great. Keep in mind that my next step is to add a button on page 1 to add a new category, and buttons on page 2 to add sub-tags to the sub-tag categories.
Finally, I have one more weird thing to report. If I only have two pages in my DOM, I have some weird behavior when loading the page. If I load from page 1, the tag checkboxes do not function, and I see a slight fattening of the border of the labels. If I swipe left to page 2 and reload from this page, the borders of the labels are thin and the checkboxes function. Cannot track down why this would be happening. My hacky workaround is to add an empty page zero and then changepage immediately to page one, but this is far from ideal. Any thoughts on that would be appreciated as well.
Here it is:
HTML
<!-- Angular version -->
<button class="ui-btn" onclick="selectTemplate();">My Template</button>
<form>
<div data-role="controlgroup">
<fieldset data-role="controlgroup">
<div ng-controller="templateCtrl">
<label
class="ui-checkbox"
ng-style="{backgroundColor: '{{tagCat.color | bgColor}}'}"
ng-repeat="tagCat in template"><input type="checkbox"
class="ui-checkbox"
id="{{tagCat.name}}"
ng-model="clicked"
ng-click="click();"
/>{{tagCat.name}}</label>
<div ng-repeat="tagCat in template">{{cb}} {{tagCat.name}} hallo</div>
</div>
</fieldset>
</div>
<div style="display:none" class="flashNotification"></div>
</form>
</div>
<div data-role="page" id="two">
<button class="ui-btn" onclick="selectTemplate();">My Template</button>
<form>
<div data-role="controlgroup">
<div ng-controller="templateCtrl">
<div ng-repeat="tagCat in template" ng-show="clicked" class="{{tagCat.name}}">{{tagCat.name}}
<fieldset data-role="controlgroup">
<label class="ui-checkbox"
ng-repeat="item in tagCat.items"
ng-style="{backgroundColor: '{{tagCat.color | bgColor}}'}"
for="item.name">{{tagCat.color | bgColor}}
<input class="ui-checkbox"
name="{{item.name}}"
id='{{item.name}}'
type="checkbox" />{{item.name}}</label>
</fieldset>
</div>
</div>
</div>
<div style="display:none" class="flashNotification"></div>
</form>
</div>
</div>
JS for jQuery Mobile
$(document).ready(function() {
// addTemplateItems(tagTemplate); // not necessary with Angular
// $.mobile.changePage('#two', { transition: 'none' }); // required or checkboxes don't work on load
$.mobile.changePage('#one', { transition: 'none' });
// // $("[data-role=controlgroup]").controlgroup("refresh");
// set up page nav
$(document).delegate('.ui-page', "swipeleft", function(){
var $nextPage = $(this).next('[data-role="page"]');
var $prevPage = $(this).prev('[data-role="page"]');
console.log("binding to swipe-left on "+$(this).attr('id') );
// swipe using id of next page if exists
if ($nextPage.length > 0) {
$.mobile.changePage($nextPage, { transition: 'slide' });
} else {
var message = 'tagged!';
// save tags here
flashNotify(message);
console.log('fire event!');
$('#flashNotification').promise().done(function () {
$('#group1').hide();
$('#group2').hide();
$('.ui-btn').hide();
// addTemplateItems(tagTemplate);
$.mobile.changePage($prevPage, { transition: 'none' });
captureImage();
});
}
}).delegate('.ui-page', "swiperight", function(){
var $prevPage = $(this).prev('[data-role="page"]');
console.log("binding to swipe-right on "+$(this).attr('id') );
// swipe using id of next page if exists
if ($prevPage .length > 0) {
$.mobile.changePage($prevPage, { transition: 'slide', reverse : true });
} else {
alert('no backy backy!');
}
});
// $("input[type='checkbox']").checkboxradio().checkboxradio("refresh");
});
JS for Angular App
var app = angular.module('STL', []);
app.factory('TagTemplate', [function () {
var TagTemplate = {};
var tagTemplate = {
family: {
name: "family",
description: "These are your family members.",
color: "red",
items: [
{
name: "Joe"
},
{
name: "Mary"
},
{
name: "Jim"
}
]
},
design: {
name: "design",
description: "Different types of design notes.",
color: "blue",
items: [
{
name: "inspiring"
},
{
name: "fail"
},
{
name: "wayfinding"
},
{
name: "graphics"
}
]
},
work: {
name: "work",
description: "Stuff for work.",
color: "green",
items: [
{
name: "whiteboard"
},
{
name: "meeting"
},
{
name: "event"
}
]
}
};
TagTemplate = tagTemplate;
return TagTemplate;
}])
// Controller that passes the app factory
function templateCtrl($scope, TagTemplate) {
$scope.template = TagTemplate;
$scope.click = function(model) {
console.log(this.checked, this.tagCat.name);
}
}
app.filter('bgColor', function () {
return function (color) {
// console.log(color, $.Color(color).lightness(.05).toHexString(.05));
// var rgba = $.Color(color).alpha(.05);
return $.Color(color).lightness(.97).toHexString();
}
})
For the main part, success!
I found a jsfiddle that gave me a good base for experimenting. After some playing, I realized that I just have to create a show property within each of the categories in my data service model, and then assign the ng-model to that property to control it.
I had to do it slightly differently in my own code, but the understanding gained from the following jsfiddle led me to the answer:
http://jsfiddle.net/Y43yP/
HTML
<div ng-app ng-controller="Ctrl">
<div class="control-group" ng-repeat="field in customFields">
<label class="control-label">{{field}}</label>
<div class="controls">
<input type="text" ng-model="person.customfields[field]" />
<label><input type="checkbox" ng-model="person.show[field]" /></label>
</div>
</div>
<button ng-click="collectData()">Collect</button><button ng-click="addField()">Add Field</button><br/><br/>
<em>Booleans</em>
<div ng-repeat="field in customFields">
<p>{{field}}: {{person.show[field]}}</p>
</div>
<em>Show/Hide</em>
<div ng-repeat="field in customFields">
<p ng-show="person.show[field]">{{field}}: {{person.customfields[field]}}</p>
</div>
</div>
JS
function Ctrl($scope) {
$scope.customFields = ["Age", "Weight", "Height"];
$scope.person = {
customfields: {
"Age": 0,
"Weight": 0,
"Height": 0
},
show: {
"Age": false,
"Weight": false,
"Height": false
}
};
$scope.collectData = function () {
console.log($scope.person.customfields, $scope.person.show);
}
$scope.addField = function () {
var newField = prompt('Name your field');
$scope.customFields.push(newField);
}
}
Still having the checkbox issue but I'll open a separate issue for that if I can't figure it out.
Thanks.