Angularjs bind dynamic form bulit in js - javascript

I have a form that I want to build at run time via js and use it in a form controller in angularjs.
As you can see in the following example, it is not being thrown as html, and i want it to be binded to the model variable. http://jsfiddle.net/g6m09eb7/
<div>
<form ng-controller="TodoCtrl" ng-submit="blabla()">
<div ng-repeat="field in fields">{{field.input}}</div>
</form>
</div>
function TodoCtrl($scope) {
$scope.model = {
'FirstName': 'Test',
'LastName': 'Test Last'
}
$scope.fields = [{
input: '<input type="text" ng-model="model.FirstName">'
}, {
input: '<input type="text" ng-model="model.LastName">'
}, ];
}

First, I'm going to show you how to make this work as you're trying to accomplish it, for the sake of being informative. This is not the approach you should use to solve your overall problem. This example will get the html in the document, but it won't be compiled with Angular. To do that, you would have to have a different directive, like this (click). This is all kinds of a bad approach.
angular.module('myApp', [])
.controller('TodoCtrl', function($scope) {
$scope.fields = [{
input: '<input type="text" ng-model="model.FirstName">'
}, {
input: '<input type="text" ng-model="model.LastName">'
}, ];
})
// filter to make Angular trust the html
.filter('safeHtml', ['$sce', function ($sce) {
return function (text) {
return $sce.trustAsHtml(text);
};
}])
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="myApp" ng-controller="TodoCtrl">
<!-- use ng-bind-html on trusted html to bind it (see the js) -->
<div ng-repeat="field in fields" ng-bind-html="field.input | safeHtml"></div>
</form>
Instead, you can do this naturally. Just use the properties of your object as the criteria for ng-repeat. Simple and clean!
angular.module('myApp', [])
.controller('TodoCtrl', function($scope) {
$scope.model = {
'FirstName': 'Test',
'LastName': 'Test Last'
};
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="myApp" ng-controller="TodoCtrl">
<div ng-repeat="(key,value) in model">
<input type="text" ng-model="model[key]"/>
</div>
</form>
Be sure to avoid concerning your controller with DOM manipulation. If you have html snippets in a controller, your approach is probably off track. DOM manipulation should be done entirely with directives.

Related

How can you add HTML markup in a Javascript array

I need to add a small amount of HTML inside part of my Javascript array for the synopsis. Is there a method to do this as currently it outputs as text?
So far I have the below
app.controller('primaryServiceListController', ['$scope', '$http', function($scope, $http){
$scope.serviceListings = [
{
url: "details.html",
img: "service-01.jpg",
sector: "Business",
synopsis: "Completely synergize resource taxing relationships <strong>via premier niche markets</strong>. Professionally cultivate one-to-one customer service robust."
}
]
}]);
You can add it as a text and then use ng-bind-html in html to render it.
<element ng-bind-html="expression"></element>
You need to include ngSanitize in your app and then, in your template, you can bind the HTML easily using ng-bind-html directive:
<div ng-bind-html="serviceListings[0].synopsis"></div>
Please see the following working demo:
angular.module('app', ['ngSanitize'])
.controller('ctrl', ['$scope', function($scope) {
$scope.serviceListings = [{
url: "details.html",
img: "service-01.jpg",
sector: "Business",
synopsis: "Completely synergize resource taxing relationships <strong>via premier niche markets</strong>. Professionally cultivate one-to-one customer service robust."
}]
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.23/angular-sanitize.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-bind-html="serviceListings[0].synopsis"></div>
</div>
You can render the string as html like this:
<div ng-bind-html="serviceListings[0].synopsis"></div>
You can also create a filter and sanitize the data using $sce if you want:
.filter('trustHtml', [
'$sce',
function($sce) {
return function(value) {
return $sce.trustAs('html', value);
}
}
]);
And to use it in the html file
<div ng-bind-html="serviceListings[0].synopsis | trustHtml"></div>

AngularJS data model architecture

I have the following thing on my Angular app:
$scope.things = [{
title: 'Simple',
type: 1,
form: {
input: [1, 2, 3],
clone: true
}
}];
$scope.clone = function(item) {
item.form.input.push(Math.floor(Math.random() * 999));
};
And on the HTML part:
<div ng-repeat="item in things" class="item">
<h2>{{item.title}}</h2>
<div ng-repeat="input in item.form.input">
<input type="text" />
</div>
<button ng-click="cloneInput(item)">Clone</button>
</div>
When press the Clone button I'm pushing a new element to form.input array and add a new input to the DOM.
I want to $http.post all the values for the inputs.
I know to push something I need to use
$http.post('/path/to/my/api', {my object}).callback()
But I dont know how to make an object from all the .item inputs.
Can someone explain me how or suggest a better solution?
If you use a ng-model for your inputs and set it to an object you can then inject that object to the post, here is a really basic example:
JSFiddle
HTML:
<div ng-app="myApp" ng-controller="dummy">
<div ng-repeat="input in items">
<input type="text" name="{{input.name}}" ng-model="data[input.name]" />
</div>
<button ng-click="submit()">Submit</button>
<p ng-show="displayIt">{{data}}</p>
</div>
JS:
angular.module('myApp', [])
.controller('dummy', ['$scope', function ($scope) {
$scope.items = [{
name: 'test'
}, {
name: 'test2'
}];
$scope.data = {};
$scope.displayIt = false;
$scope.submit = function () {
// This is only to check it
$scope.displayIt = true;
// Do your post here
};
}]);

AngularJS, nested controllers from variable

Generally, what I want to do, is to initialize nested ng-controller inside ng-repeat using variable.
JSFiddle
JS
angular.module('app',[])
.controller('main',function($scope){
angular.extend($scope,{
name:'Parent Controller',
items:[
{name:'nested2'},
{name:'nested1'}
]
});
})
.controller('nested1',function($scope){
$scope.name = "Name1";
})
.controller('nested2',function($scope){
$scope.name = "Name2";
});
I want this:
<div ng-controller="main" ng-app='app'>
Nested: {{name}}
<div ng-controller="nested1">{{name}}</div>
<div ng-controller="nested2">{{name}}</div>
</div>
to become to something like this:
<div ng-controller="main">
Nested: {{name}}
<div ng-repeat="item in items">
<div ng-controller="item.name">{{name}}</div>
</div>
</div>
Problem: it does not work this way. Neither it works any other way, that I've tried after google'ing for an hour or so.
Is there any "legal" and nice way to achieve that at all?
There isn't a real way,using angular features it at this point, i suppose. You could create a directive and use un-documented dynamic controller feature controller:'#', name:'controllerName'. But this approach will not evaluate bindings or expressions that provide the controller name. What i can think of is a hack by instantiating a controller provided and setting it to the element.
Example:-
.directive('dynController', function($controller){
return {
scope:true, //create a child scope
link:function(scope, elm, attrs){
var ctrl =scope.$eval(attrs.dynController); //Get the controller
///Instantiate and set the scope
$controller(ctrl, {$scope:scope})
//Or you could so this well
//elm.data('$ngControllerController', $controller(ctrl, {$scope:scope}) );
}
}
});
And in your view:-
<div ng-controller="main">
<div ng-repeat="item in items">
Nested:
<div dyn-controller="item.name" ng-click="test()">{{name}}</div>
</div>
</div>
Demo
Note that i have changed the position of ng-controller from the element that does ng-repeat, since ng-repeat (1000) has higher priority than ng-controller (500), ng-repeat's scope will prevail and you end up not repeating anything.
While looking at it
Invested more couple hours into it, inspecting angular's sources etc.
The expression, given to ng-controller, is not being evaluated at all.
Here is best approach I've found:
HTML:
<div ng-controller="main">
Nested: {{name}}
<div ng-repeat="item in items">
<div ng-controller="nested" ng-init="init(item)">{{name}}</div>
</div>
</div>
JS:
angular.module('myApp', [])
.controller('main', function ($scope, $controller) {
angular.extend($scope, {
name: 'Parent Controller',
items: [
{name: 'nested2'},
{name: 'nested1'}
]
});
})
.controller('nested', function ($scope, $controller) {
angular.extend($scope, {
init: function (item) {
$controller(item.name, {'$scope': $scope});
}
});
})
.controller('nested1', function ($scope) {
$scope.name = 'test1';
})
.controller('nested2', function ($scope) {
$scope.name = 'test2';
});

Simple ng-change not working properly in angularjs

I might be missing something stupidly as this simple code doesn't work as expected. What is wrong is the $scope.change function in the MainCtrl doesn't work (no alert box popped up).
In a nutshell, the view is (it's in jade, better to view?)
<body ng-app="epfApp">
...
label(ng-repeat="question in questions")
| {{ question.title }}
input(type="{{question.type}}", ng-change="change()")
and in the controller file
angular.module('epfApp')
.controller('MainCtrl', function ($scope, $window) {
$scope.questions = {
'1': {
'title': 'The first question is?',
'type': 'text',
'placeholder': 'first question'
},
'2': {
'title': 'The second question is?',
'type': 'text',
'placeholder': 'second question'
}
};
$scope.change = function() {
$window.alert('text');
};
});
And the route:
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
});
Now what it is doing properly is that it correctly populates the view with the data created (i.e. the questions json). However, What it is not doing properly is the change() function bound to the input textbox doesn't work.
What am I missing here? This obviously is a very basic job.
ng-change requires ng-model as well.
<div ng-controller="MainCtrl">
<label ng-repeat="question in questions">
{{question.title}}
<input type="{{question.type}}" ng-model="question.placeholder" ng-change="change()" />
<br />
</label>
</div>
Check out this JSFiddle.
ng-change not working but it work if you define ng-model="yourValue"... like this
<select id="editSelect" ng-model="Option" ng-change="okChange()" name="Option">
<option ng-repeat="data in Option" value="{{data.Option}}">{{data.Option}}</option>
</select>
<div ng-app ng-controller="myCtrl">
<input type="file" ng-model="image" onchange="angular.element(this).scope().uploadImage()" />
<ul>
<li ng-repeat="item in uploadcollection">
{{item.name}} ({{item.size}})
</li>
</ul>
function myCtrl($scope) {
$scope.uploadImage = function () {
$scope.uploadcollection.push({name: "Income.pdf", size: "10mb"});
$scope.$apply();
}
$scope.uploadcollection = [{name: "Income.pdf", size: "10mb"}, {name: "Expense.pdf", size: "1.5mb"}];
}
I have created a demo here: http://jsfiddle.net/fA968/120/
NOTE: This Example not worked with v1.4.8: https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js
Update
Here is an simple way to do this:
var myApp = angular.module('myApp', []);
myApp.directive('customOnChange', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var onChangeFunc = scope.$eval(attrs.customOnChange);
element.unbind('change').bind('change', function(e) {
onChangeFunc(e);
});
}
};
});
Here is an updated Fiddle
When the expression inside of the ng-change="" is something which errors (like with a spelling error) it will not log the error to the console. You will not know anything is amiss except for the fact that nothing happens.
Make sure to spell things correctly and to prefix the alias you give the "Controller As" syntax, if applicable.

How to render html with AngularJS templates

This is my template:
<div class="span12">
<ng:view></ng:view>
</div>
and this is my view template:
<h1>{{stuff.title}}</h1>
{{stuff.content}}
I am getting the content as html and I want to display that in a view, but all I am getting is raw html code. How can I render that HTML?
Use-
<span ng-bind-html="myContent"></span>
You need to tell angular to not escape it.
To do this, I use a custom filter.
In my app:
myApp.filter('rawHtml', ['$sce', function($sce){
return function(val) {
return $sce.trustAsHtml(val);
};
}]);
Then, in the view:
<h1>{{ stuff.title}}</h1>
<div ng-bind-html="stuff.content | rawHtml"></div>
In angular 4+ we can use innerHTML property instead of ng-bind-html.
In my case, it's working and I am using angular 5.
<div class="chart-body" [innerHTML]="htmlContent"></div>
In.ts file
let htmlContent = 'This is the `<b>Bold</b>` text.';
You shoud follow the Angular docs and use $sce - $sce is a service that provides Strict Contextual Escaping services to AngularJS. Here is a docs: http://docs-angularjs-org-dev.appspot.com/api/ng.directive:ngBindHtmlUnsafe
Let's take an example with asynchroniously loading Eventbrite login button
In your controller:
someAppControllers.controller('SomeCtrl', ['$scope', '$sce', 'eventbriteLogin',
function($scope, $sce, eventbriteLogin) {
eventbriteLogin.fetchButton(function(data){
$scope.buttonLogin = $sce.trustAsHtml(data);
});
}]);
In your view just add:
<span ng-bind-html="buttonLogin"></span>
In your services:
someAppServices.factory('eventbriteLogin', function($resource){
return {
fetchButton: function(callback){
Eventbrite.prototype.widget.login({'app_key': 'YOUR_API_KEY'}, function(widget_html){
callback(widget_html);
})
}
}
});
So maybe you want to have this in your index.html to load the library, script, and initialize the app with a view:
<html>
<body ng-app="yourApp">
<div class="span12">
<div ng-view=""></div>
</div>
<script src="http://code.angularjs.org/1.2.0-rc.2/angular.js"></script>
<script src="script.js"></script>
</body>
</html>
Then yourView.html could just be:
<div>
<h1>{{ stuff.h1 }}</h1>
<p>{{ stuff.content }}</p>
</div>
scripts.js could have your controller with data $scope'd to it.
angular.module('yourApp')
.controller('YourCtrl', function ($scope) {
$scope.stuff = {
'h1':'Title',
'content':"A paragraph..."
};
});
Lastly, you'll have to config routes and assign the controller to view for it's $scope (i.e. your data object)
angular.module('yourApp', [])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/yourView.html',
controller: 'YourCtrl'
});
});
I haven't tested this, sorry if there's a bug but I think this is the Angularish way to get data

Categories

Resources