Say I have the following directive called foo:
<div>
<label>Enter foo: </label>
<input ng-model="myModel"/>
</div>
And I use it like so:
<foo></foo>
<button>Add foo: </button>
What I am trying to do is dynamically add the foo directive on each button click and be able to have access to the new model variable that was created in the corresponding controller for this page.
Can this be done using angularJS?
First of all, if you plan to re-use <foo>, then you should create an isolated scope:
.directive("foo", function(){
return {
restrict: "E",
scope: {
data: "="
},
template: "<div><label>Enter foo: </label><input ng-model='data'/></div>"
}
});
There is no difference between creating a custom directive or other tags. Without knowing too much about what you're doing, I could suggest the following:
app.controller("MainCtrl", function($scope)){
$scope.fooModels = [];
$scope.addFoo = function(){
$scope.fooModels.push(new FooModel());
};
}
FooModel() here is just a placehold for whatever the data model you need to represent foo data. You could also just do: $scope.fooModels.push({});.
Then in the view, simply ng-repeat your fooModels:
<div ng-repeat="fooModel in fooModels">
<foo data="fooModel.data"></foo>
</div>
<button ng-click="addFoo()">Add Foo</button>
Here's a plunker to play with.
Related
I am updating and modifying a project using Angular JS 1.2.25.
I have my controller where I have a value called vm.stageValue which is then called in template with an ng-if, so when the vm.stageValue increments it shows different containers. But whhen I define a value on the vm object that I want to interpolate on the template, eg a string that will be used and will not change on the template, I cannot seem to get it display.
This has makes me think I have not set up my controller correctly using the vm method.
It seems weird that I can use the ng-if and call function from the controller using ng-click on the template but I cannot interpolate a string or send it to another child component
Code is below, thank you in advance. Any help would be hugely appreciated
Controller
angular
.module('formModule')
.controller('NewBusinessFormCtrl', [
function() {
let vm = this;
// Methods used in controller
vm.methods = {
incrementStageValue: incrementStageValue,
decrementStageValue: decrementStageValue,
canIncrement: canIncrement,
canDecrement: canDecrement
};
//Initial stage values
vm.stageValue = 1;
vm.maxStageValue = 7;
// This is the string that I want to interpolate below
vm.contactFormCategory = 'New Business';
}
]);
Template of Controller
<div class="new_busines_cf" ng-controller="NewBusinessFormCtrl as vm">
<div class="form_wrapper">
<div ng-if="vm.stageValue == 1">
<input-text
class="form_input"
ng-model="ngModel"
input-text-label="This is the label">
</input-text>
// I want to send the vm.contactFormCategory into the component
// Value is sending but the component display 'vm.contactFormCategory'
// Not the value set in the controller
<form-headline
form-headline-sup-title="vm.contactFormCategory"
form-headline-text="This is a form headline text">
</form-headline>
</div>
// Trying to interpolate value here into template, but nothing display
{{vm.contactFormCategory}}
<div ng-if="vm.stageValue == 2">
<input-text
class="form_input"
ng-model="ngModel"
input-text-label="This is the label of stage 2">
</input-text>
<form-headline
form-headline-sup-title="vm.contactFormCategory"
form-headline-text="This is a form headline text">
</form-headline>
</div>
<button ng-click="vm.methods.incrementStageValue()">Increment Value</button>
<button ng-click="vm.methods.decrementStageValue()">decrement Value</button>
</div>
</div>
** Form Headline **
angular
.module('formModule')
.directive('formHeadline', function() {
return {
restrict: 'E',
templateUrl: '/partials/form/form-headline.component.html',
scope: {
formHeadlineText: '#',
formHeadlineSupTitle: '#'
},
link: function () {
}
};
});
Change your ng-if to
<div ng-if="vm.stageValue === '2'">
I'm new to Angular and can't figure out what I am doing wrong while trying to get ng-model to display an object value.
The goal:
I want to create a directive that reads an Object and generates radio buttons based on Object data. Inside of this Object I have {logAs: 'radioGroupName'}. This should get stored into another object called formData.
Example:
{logAs: 'dog', options: [{option: 'poodle'}, {option: 'pitbull'}]}
This should create two radio buttons, one for pittbull and another for poodle. Both should have ng-model set to dog. If user selects Poodle then final output in the formData Object should be:
{dog: 'poodle'}
The Issue:
I believe I am close, everything works fine except for ng-model. Inside of ng-model the actual Object value (dog) wont show up as I'd like, instead I get the Object path that I typed in.
This is my html:
<div ng-app="test">
<div ng-controller="myController">
<p>formData: {{formData}}</p>
<h3>Static (updates formData)</h3>
<input type="radio" ng-model="formData.dog" value="pitbull"> Pitbull<br/>
<input type="radio" ng-model="formData.dog" value="Bull Dog"> Bull Dog<br/>
<input type="radio" ng-model="formData.dog" value="Poodle"> Poodle
<h3>Dynamic (wont update formData)</h3>
<directive form="formInfo"></directive>
</div>
This is the Javascript:
angular.module('test', [])
.controller('myController', function($scope){
$scope.formData = {};
$scope.formInfo = {
title: 'What type of dog do you have?',
logAs: 'dog',
options: [{option:'Pitbull'}, {option: 'Bull Dog'}, {option:'Poodle'}]
}
})
.directive('directive', function(){
return {
restrict: 'E',
template: '{{form.title}}<div ng-repeat="option in form.options"> <input type="radio" ng-model="form.logAs" value="jack" > {{option.option}}</div>',
scope: {
form: '='
}
}
});
How can I get ng-model to show the output of the object ('dog') instead of the object path?
Here is a fiddle
In your fiddle, it's because on line 3 of your html, you have {{formData}} when it should be {{formData.dog}}
Here i want to update controller scope value as per change in directive scope but its only working outside the ng-repeat and its not working inside ng-repeat..
HTML
<div ng-app="app">
<div ng-controller="MainCtrl">
<div>
<h3>
outside repeat
</h3>
<br> Name <strong>{{name}}</strong>
<div class="directive" my-directive name="name"></div>
<button ng-click="run()">See changes</button>
<br>
<br>
<h3>
Inside repeat
</h3>
<br>
<div ng-repeat="(k,v) in rawdata">
{{k}} {{v.key}} Name <strong>{{name}}</strong>
<div class="directive" my-directive name="name"></div>
<button ng-click="run()">See changes</button>
<br>
</div>
</div>
</div>
JS
var app = angular.module("app", []);
app.controller("MainCtrl", function($scope) {
$scope.name = "HappyNewYear2016";
$scope.run = function() {
alert($scope.name);
}
$scope.rawdata = [{
key: "event1",
}, {
key: "event2",
}];
});
app.directive("myDirective", function() {
return {
restrict: "EA",
scope: {
name: "="
},
template: [
"<div>",
"Name : <strong>{{name}}</strong>; Change name:<input type='text' ng-model='name' /><br/>",
].join("")
};
});
JSFiddle Link
Please help me in updating controller value from directive inside this ngrepeat..
Basically the problem is the $scope.name inside the controller is not the same as you are passing to directive inside the ng-repeat element, because ng-repeat does create a child scope which is prototypically inherited from the parent scope while looping through rawdata object.
There are several ways to solve this problem.
If you wanted to solved this child and parent scope related issue just by using $parent annotation before name which will refers to parent scope.
Plunkr With $parent in directive
Cons:-
But after certain point $parent will make you crazy. Like suppose if you have two or three hierarchy of child scope it will become like $parent.$parent.$parent.name which looks very wiered.
In your case name is of primitive datatype, so while creating child scope the primitive datatypes values of parent scope aren't accessible inside the child scope. That is the reason why you were using $parent to indicates that name belongs to parent scope. You could overcome this problem just by following do annotation while declaring object. Which will help you to make parent scope property available inside the child by following prototypal inheritance.
HTML
<div class="directive" my-directive name="model.name"></div>
Code
$scope.model = {
name: 'HappyNewYear2016'
};
Plunkr with Dot Notation
You could solve this problem just by passing name value from the run function on ng-click="run(name)"
Html
<div ng-repeat="(k,v) in rawdata">
{{k}} {{v.key}} Name <strong>{{name}}</strong>
<div class="directive" my-directive name="name"></div>
<button ng-click="run(name)">See changes</button>
<br>
</div>
Code
$scope.run = function(name) {
alert(name);
}
You could use controllerAs syntax while declaring controller with its alias and the pass the controller name property by its alias.
Working Plunkr
It's all because of how prototypical inheritance works as both scopes directive and controller have same name for model. Child directive shadows the controller model.
Instead of storing controller model directly as variable use it with object.
$scope.data = {
name : 'HappyNewYear2016'
}
Then use it asdata.name to setup in ng-repeat. It will reflect it in parent as well.
I am trying to create buttons (using ng-repeat) that when clicked will create other buttons that when these are clicked will display the information I am looking for. I was told that an Angular Directive would do the trick. I have created a custom directive and am trying to incorporate the ng-repeat directive inside of my new directive. I have already looked at this StackOverflow discussion StackOverflow Discussion 2, but I am still having some confusion on how to best get his implemented. As it stands the new directive is being made, but no text is being appended to the button. Also only one button is being generated instead of two in this case. Below is my code (HTML and JavaScript)
HTML:
<div ng-app="anniversaries" class="row" ng-controller="annList">
<yearbuttons></yearbuttons>
</div>
JavaScript:
var annApp = angular.module('anniversaries', []);
annApp.controller('annList', function ($scope) {
$scope.anns = [
//January
{'date':'January 2015','year': '45',
'names': ['Sample Name']},
{'date':'January 2015','year': '34',
'names': ['Sample Name2']}
];
});
annApp.directive('yearbuttons',function(){
return {
restrict: "E",
compile: function compile(element, attrs)
{
element.attr('ng-repeat', 'years in anns');
element.attr('class', 'btn btn-default');
element.append('{{year}} Years');
}
}
});
I personally wouldn't use a directive for this at all unless it needed to be used repeatedly. But if it does, this could easily be converted to a directive just by sticking the template and controller into a directive. Directives can take controllers instead of link functions.
JavaScript
(function(){
angular.module('myApp', [])
.controller('BunchOfButtonsController', BunchOfButtonsController);
BunchOfButtonsController.$inject = ['$scope', '$log'];
function BunchOfButtonsController($scope, $log){
$scope.buttons = [];
$scope.nextLabel = "Some Text";
$scope.firstButtonClick = function(labelValue){
$scope.buttons.push({
label: labelValue,
click: function(){
$log.info(labelValue + " was clicked.");
}
});
$scope.nextLabel = "Another " + $scope.nextLabel;
};
}
})();
HTML
<div ng-app="myApp" ng-controller="BunchOfButtonsController">
Label: <input type="text" ng-model="nextLabel"/>
<button ng-click="firstButtonClick(nextLabel)">Click Me!</button>
<button ng-repeat="button in buttons" ng-click="button.click()">{{button.label}}</button>
</div>
I have a bit of a strange scenario that is a little different to the other childscope and two way binding issues I have seen on Stackoverflow.
I have a field generation directive that receives a configuration object and some data and dynamically creates the correct type of field on screen and populates the data.
directive.js
.directive('myField', function () {
var stringTemplate = "scripts/directives/templates/my-string.tpl.html";
var textTemplate = "scripts/directives/templates/my-text.tpl.html";
var selectTemplate = "scripts/directives/templates/my-select.tpl.html";
var linker = function ($scope, elem, attrs) {
// Function to dynamically select the correct template
$scope.getTemplateUrl = function () {
var template = '';
if ($scope.options) {
if ($scope.options.optionList) {
template = selectTemplate;
} else {
switch ($scope.options.type) {
case 'String':
template = stringTemplate;
break;
case 'Text':
template = textTemplate;
break;
}
}
return template;
}
};
return {
restrict: 'E',
replace: true,
scope: {
options: '=',
data: '=',
fieldName: '#',
fieldWidth: '#',
labelWidth: '#',
},
link: linker,
template: '<ng-include src="getTemplateUrl()"/>'
}
});
I then have the corresponding template... I'm showing just the string template in this case.
my-string.tpl.html
<div class="form-group col-md-12">
<label for="{{fieldName}}" class="{{labelWidth}}">
{{options.label}}
</label>
<div class="{{fieldWidth}}">
<input type="text" class="form-control input-sm" id="{{fieldName}}" placeholder="{{options.watermark}}" ng-model="data" tooltip="{{options.tipText}}" ng-disabled="options.editable === false">
</div>
</div>
An example of how this might then be used would be
controller.js
$scope.person.firstName = "John";
$scope.person.lastName = "Doe";
$scope.options.person.firstName.type = "String";
index.html
<div class="row">
<my-field options="options.person.firstName" data="person.firstName" field-name="firstName" label-width="small" field-width="medium"></my-field>
The problem is the usual one, my-field directive has an isolated scope with a "data" property that is two-way bound to the controller. Because I am then using ng-include to dynamically load the correct template I am creating a further child scope that due to prototypical inheritance still populates correctly as it doesn't have its own data property so reaches to the parent. However when I modify the field, a shadow property is created on my child scope called data that doesn't propagate upwards the way that two way binding should.
I hope you are still with me
controller > my-field
ng-include causes the following scopes to exist
controller > my-field > ng-include
From reading around I understand that what I need to do to rectify this is pass an object rather than a primitive, however as there is effectively an intermediate layer between my controller and my final directive this is not straightforward.
I thought about changing the isolate scope in my-field to look like this
scope: {
....
data: {value: '=data'}
....
}
and then updating the template to refer to the object
my-string.tpl.html
<div class="form-group col-md-12">
<label for="{{fieldName}}" class="{{labelWidth}}">
{{options.label}}
</label>
<div class="{{fieldWidth}}">
<input type="text" class="form-control input-sm" id="{{fieldName}}" placeholder="{{options.watermark}}" **ng-model="data.value"** tooltip="{{options.tipText}}" ng-disabled="options.editable === false">
</div>
</div>
but this kills angular.
I have successfully got it to work by reaching back to the controller scope for binding by using
ng-model="$parent.$parent.data"
but I am not really happy with this as a solution as A it is ugly and B it involves knowing the depth of scope you are at which could vary.
Really stumped with this. Any help would be appreciated.