AngularJS Dynamic Template with indexed scope variable arrays - javascript

I'm using AngularJS and trying to create a form where I can dynamically add new inputs, similar to this fiddle: http://jsfiddle.net/V4BqE/ (Main HTML below, working code in fiddle).
<div ng-app="myApp" ng-controller="MyCtrl">
<div add-input>
<button>add input</button>
</div>
</div>
I would like to be able to use a HTML template for my form since the input I'm adding is ~300 lines long. My issue is I cannot figure out how to index the scope variable containing the data when used in a template. I've tried to make my own modified version of the above code on plnkr http://plnkr.co/edit/4zeaFoDeX0sGTuBMCQP2?p=info . However, when I click the button no form elements appear.
Online (plnkr) I get a 404 not found for my template.html, but I think that is just a plnkr limitation. On my machine with a Python HttpServer I get an Error: [$parse:syntax] for the $templateRequest and a TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. when using the $http.get method.
Any advice for getting the indexed scope variable array to work with an external html file?
Thanks, JR
Edit: Update plnkr link

You can do it without directive & external template
what you are trying to do does not require a directive (it actually much simple with the basic angularjs tools)
http://plnkr.co/edit/LVzGN8D2WSL2nR1W7vJB?p=preview
html
<body>
<div class="container" ng-app="myApp" ng-controller="MyCtrl">
<button class="btn btn-primary" type="button" ng-click="addPhoneInput()">add input</button>
<form>
<div ng-repeat="item in telephoneNumbers">
<hr>
<input type="text" ng-model="item.phone">
</div>
</form>
<hr>
<div class="well">
<h4>Phone Numbers,</h4>
<p ng-repeat="item in telephoneNumbers">{{item.phone}}</p>
</div>
</div>
</body>
js
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope', function($scope) {
// Define $scope.telephone as an array
$scope.telephoneNumbers = [];
$scope.addPhoneInput = function() {
$scope.telephoneNumbers.push({});
};
// This is just so you can see the array values changing and working! Check your console as you're typing in the inputs :)
$scope.$watch('telephoneNumbers', function(value) {
console.log(value);
}, true);
}]);
If you insist using a directive,
http://plnkr.co/edit/BGLqqTez2k9lUO0HZ5g1?p=preview
phone-number.template.html
<div>
<hr>
<input type="text" ng-model="ngModel" >
</div>
html
<body>
<div class="container" ng-app="myApp" ng-controller="MyCtrl">
<button class="btn btn-primary" type="button" ng-click="addPhoneInput()">add input</button>
<form>
<phone-number ng-repeat="item in telephoneNumbers" ng-model="item.phone"></phone-number>
</form>
<hr>
<div class="well">
<h4>Phone Numbers,</h4>
<p ng-repeat="item in telephoneNumbers">{{item.phone}}</p>
</div>
</div>
</body>
js
var app = angular.module('myApp', []);
app.controller('MyCtrl', ['$scope', function($scope) {
// Define $scope.telephone as an array
$scope.telephoneNumbers = [];
$scope.addPhoneInput = function() {
$scope.telephoneNumbers.push({});
};
// This is just so you can see the array values changing and working! Check your console as you're typing in the inputs :)
$scope.$watch('telephoneNumbers', function(value) {
console.log(value);
}, true);
}]);
app.directive('phoneNumber', function(){
return {
replace:true,
scope: {
ngModel: '=',
},
templateUrl: "phone-number.template.html"
}
});

Related

Angular Clicking on Text to show div tag

I was wondering how to accomplish this with Angular as it seems that ng-click is something to use, then ng-model seems like that could be used.
I want to click on Text and then have a div show its contents and it is not working
My fiddle https://jsfiddle.net/gdxwtoL7/
<div class="well" ng-controller="MyController">
<a class="btn btn-primary" ng-model="selMe" ng-click="handleAnchorClick()">Enter Address</a>
</div>
<br>
<br>
<div ng-if="selMe">
adfadf
</div>
simple module and controller
angular.module('myapp', []);
angular.module('myapp').controller('MyController', MyController)
function MyController($scope) {
}
You're not doing anything inside the ng-click function, and you have the ng-if outside of the controller linked to the variable inside it.
https://jsfiddle.net/gdxwtoL7/1/
HTML
<div class="well" ng-controller="MyController">
<a class="btn btn-primary" ng-click="handleAnchorClick()">Enter Address</a>
<br>
<br>
<div ng-if="selMe">
adfadf
</div>
</div>
JS
angular.module('myapp', []);
angular.module('myapp').controller('MyController', MyController)
function MyController($scope) {
$scope.handleAnchorClick = function () {
$scope.selMe = true
}
}
The controller has to be aware of the div you want it to show.
the ng-if is waiting for the value of the selme which you can alter from the controller.
The ng-model binds your data to your controller in adding two-way data binding.
I made a little enhancement to your code to toggle the div when the text is clicked multiple times.
https://jsfiddle.net/gdxwtoL7/2/
<div class="well" ng-controller="MyController">
<a class="btn btn-primary" ng-model="selMe" ng-click="handleAnchorClick(selMe)">Enter Address</a>
<br>
<br>
<div ng-if="selMe">
adfadf
</div>
</div>
angular.module('myapp', []);
angular.module('myapp').controller('MyController', MyController)
function MyController($scope) {
$scope.handleAnchorClick = function (selMe) {
$scope.selMe = !selMe
}
}
The ngModel directive binds an input,select, textarea (or > custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive.
ngModel is responsible for:
Binding the view into the model, which other directives such as input, textarea or select require.
Providing validation behavior (i.e. required, number, email, url).
Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
Setting related css classes on the element (ng-valid, ng-invalid, ng-dirty, ng-pristine, ng-touched, ng-untouched, ng-empty, ng-not-empty) including animations.
Registering the control with its parent form.
The ngClick directive allows you to specify custom behavior when an element is clicked.
Note : we need ng-click to capture the event and manipulate the data stored in ng-model.
Here is simple code without the need of controller:
<div class="well">
<a class="btn btn-primary" href="" ng-click="show=true">Enter Address</a>
</div>
<br>
<br>
<div ng-show="show">
adfadf
</div>
As #MikeHughesIII already pointed out, outside of your controller you can't reach $scope variables.
I am adding a quick snippet made after Mike's answer for completeness sake, showing a show/hide (toggle) approach, where the function sets the visibility variable to the opposite of its current status (true or false) when the function is invoked.
Hope that helps to clarify the issue.
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="myController">
<h1>Hello {{hello}}!</h1>
<a href ng-click="toggleDivVisibility()">Enter your address</a>
<br>
<textarea ng-if="visible" name="address" id="address" cols="30" rows="5"></textarea>
</div>
<script>
angular.module('myApp', []);
angular.module('myApp')
.controller('myController', myController);
function myController($scope) {
$scope.hello = "world";
$scope.visible = false;
$scope.toggleDivVisibility = function() {
$scope.hello = 'mondo';
$scope.visible = !$scope.visible;
}
}
</script>
</body>
</html>

AngularJS 1 - ng-repeat not re-rendering after array push

I'm trying to make a simpliest chat app in Angular 1.
Controller code:
app.controller('mainCtrl', function ($scope, $rootScope, $routeParams, $location, $http) {
$scope.messages=[{'name':'user','text':'test'}];
$scope.submitMessage = function () {
$scope.messages.push({'name':'newuser','text':$scope.mymessage});
}
});
Template:
<div class="page-content" ng-controller="mainCtrl" ng-init="init()">
<div class="messages">
<p class="chat-message" ng-repeat="message in messages"><span class="username">{{message.name}}: </span>{{message.text}}</p>
</div>
<div style="clear:both"></div>
<form ng-submit="submitMessage()" ng-controller="mainCtrl">
<input type="text" ng-model="mymessage" class="message-input" ></input>
</form>
When trying to console.log the $scope.messages array, it shows the new values, but the list itself does not change at all. What can be the reason?
You've got ng-controller="mainCtrl" defined twice, this will actually instantiate 2 different instances of the controller, so the messages you push will be on the array in the second instance of your controller while you're repeating over the messages that are on the first instance of your controller.
You only need it on the surrounding div, everything that is nested inside this tag will be able to access the $scope on your controller.
<div class="page-content" ng-controller="mainCtrl" ng-init="init()">
<div class="messages">
<p class="chat-message" ng-repeat="message in messages"><span class="username">{{message.name}}: </span>{{message.text}}</p>
</div>
<div style="clear:both"></div>
<form ng-submit="submitMessage()">
<input type="text" ng-model="mymessage" class="message-input"/>
</form>
</div>

Insert $index into name attribute in an ng-repeat angularJS

In order for ASP.NET MVC to correctly bind a list of items on a form post, the name attribute has be along the lines of
name='Show.Days[0].OpenHour'
name='Show.Days[1].OpenHour'
The user can enter the number of days the show will be into a form field, which then updates the model and the ng-repeat.
I'd like to be able to insert the appropriate index into the name field, something like
name='Show.Days[$index].OpenHour'
Is this possible with angular?
Use name="Show.Days[{{$index}}].OpenHour". With this, AngularJS evaluates $index and replaces it with the correct value.
It seems that you forgot to wrap the expression with {{ and }} in the view. Do you need something like this?
var app = angular.module('myApp', []);
app.controller("myCtrl", function ($scope) {
$scope.Show = {
Days: [
{OpenHour: '8am'},
{OpenHour: '10am'}
]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div ng-repeat="day in Show.Days">
<input type="text" ng-model="Show.Days[$index].OpenHour" name="{{Show.Days[$index].OpenHour}}">
</div>
</div>
</div>
or like this?
var app = angular.module('myApp', []);
app.controller("myCtrl", function ($scope) {
$scope.Show = {
Days: [
{OpenHour: '8am'},
{OpenHour: '10am'}
]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div ng-repeat="day in Show.Days">
<input type="text" ng-model="Show.Days[$index].OpenHour" name="Show.Days[{{$index}}].OpenHour">
</div>
</div>
</div>

Is there an angular directive that can restrict scope for an element in the DOM

Hi I was just wondering if there is some built-in angular directive (ng-some-directive) that allows you to restrict the scope to a particular model for the desired DOM element.
I belive I have seen something similar done before, but maybe I am thinking of knockout.js
<script>
angular.module('example', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.heading = 'Some Value';
$scope.complexModel = {...}
}]);
</script>
<div ng-controller="ExampleController">
<h1>{{ heading }}</h1>
<div ng-some-directive='complexModel.to.long.really.annoying'>
<input type='text' ng-model='variable' /> // Actually coming from complexModel.to.long.really.annoying.variable
</div>
</div>
This is a drastically simplified version
Though its not meant for it
<div ng-init='sn = complexModel.to.long.really.annoying'>
<input type='text' ng-model='sn' />
</div>

Dynamic forms in AngularJs

Im working on some dynamic forms using AngularJs and appear to have gotten a bit stuck with validation and the form "name" registering on the scope.
What I'm looking to achieve is following.
<my-form-helper form="someObjectWhichRepresentsFormItems" handle-submit="true">
When the handel-submit is true, I would like the directive to include a wrapper around the elements the directive normally creates (a list of inputs, etc)
The problem is, when ever I dynamically add the <form> in the template, the scope does not see the form, however if I simply put in the form tag every time it does..
Also, my ideal solution was to use the directive to wrap the element in a <form> tag using the link function however the result was the same and I can re-look at this once I have it working from within the template.
E.g
<form name="testForm" ng-if="handelSubmit" >
Vs
<form name="testForm">
in the my-form-helper directives controller function the scope
.directive('myFormHelper', function ($http, $compile) {
return {
controller: function($scope){
$scope.submit = function(){
// Trying to access $scope.testForm
// only works if I take out the ng-if,
// even when handelSubmit == true
console.log($scope);
alert('Form submitted..');
$scope.form.submitted = true;
}
$scope.cancel = function(){
alert('Form canceled..');
}
},
templateUrl: 'app/forms/directive-templates/form/form.html',
restrict: 'E',
scope: {
form:'=',
handelSubmit:'='
}
};
});
Html tempalte is
<form name="testForm" ng-if="handelSubmit" >
<!-- The for name derly should be name="{{form.name}}" -->
<div ng-repeat="field in form.fields">
<form-field field="field">
</div>
<div class="form-actions" ng-show="handelSubmit">>
<button type="button" ng-click="submit()">Submit Form</button>
<button type="button" ng-click="cancel()">Cancel</button>
</div>
</form>
<div ng-if="!handelSubmit">
<div ng-repeat="field in form.fields">
<form-field field="field">
</div>
</div>

Categories

Resources