I wanted to have a template which would be used to create dynamic user detail forms. But I need to have different ids for all elements, so as to post the details correctly. My template looks like:-
<div id="user-template" class="hidden">
<div class='lbl-div' id='user(user_number)'>
<label>User(user_number)</label>
</div>
<label class="lbl" id="user(user_number)_firstname">Firstname:</label>
<input type="text" value="" />
<label class="lbl" id="user(user_number)_lastname">Lastname:</label>
<input type="text" value="" />
</div>
Here, user_number is a variable. onclick of a button would pick this template-->replace user_number with a global variable-->increment the global variable-->append the modified template as a child to the parent div.
I replace the variables as:-
template = template.replace(/\(user_number\)/g, count);
count++;
Here count is the global variable.
Is there a better way to achieve this(using the template dynamically with changing ids)?
If you are using html templates, then try using any templating engine, like underscore, mustache. But templating engine will not give you two way binding between template and your data. It just render template with given data.
If you want to use pure two way databinding application, then try using web application frameworks like angularjs or emberjs.
Related
I'm trying to get my head round templating engines and if there is something suitable for my requirements.
I'd like to specify HTML and provide dynamic functionality from within the HTML itself. For example, say I had a check box on a page
<label><input type="checkbox" id="cbox1" value="first_checkbox"> This is my checkbox</label>
I'd like to specify logic within HTML so that I display more content only if that checkbox has been checked, e.g.
// if #cbox1.checked == true
<h1>The check box is checked</h1>
// else
<h1>The check box is not checked</h1>
// end
Now, it is likely that liquid will be used to provide dynamic functionality based on a data store. So it'd also be nice to use the same liquid syntax to make the form dynamic (i.e. use liquid syntax in the if conditional above).
Is it possible to write a 'js engine', perhaps using jquery, that I could include in my web pages that would allow me to use liquid syntax but bind to variables in the 'js engine' as well as the data store to make my content dynamic?
Or, is there a better approach?
I would recommend using Vue.js (https://vuejs.org/).
The template engine is very easy to learn, and provides all the functionality you mention.
Here is a working example of your scenario:
https://jsbin.com/kikaxecogo/edit?html,output
But all you need to do is initialise Vue:
new Vue({
el: '#app',
data: {
showData: false
}
});
and write the template data:
<input type="checkbox" v-model="showData">
<div v-if="showData">
This is visible using v-if
</div>
<div v-else>
The check box is not checked
</div>
I've written a introduction guide to Vue.js here https://steveedson.co.uk/vuejs-intro/
You can also bind to other text inputs, data from ajax sources etc:
<input type="text" v-model="name">
<p>Hello {{ name }}</p>
And everything will update automatically.
As an alternative to Vue.js, there is also:
https://facebook.github.io/react/
https://angularjs.org/
Django has formsets, where multiple forms can be used in one big form. So let's say one can add in a e.g. library formset mulitple books (providing the author and title) using repetitions of the same book form.
How to achieve the same functionality with Angular.js and Django Rest Framework? I'm new to Angular.js and Django Rest Framework and need some guidance how to be able to dynamically add more forms(e.g. for a book) for a given model in one big form (e.g. my library) and save them in Django Backend.
You can achieve this in 2 steps:
On Frontend
Create a <form> on your page that will structure the data entered by the user as you need. Inside that <form> element, you'll need to use the ngForm for multiple forms' validation to behave correctly (here is a nice explanation of how ngForm works). A hypothetical code snippet would look like:
<form name="libraryForm">
<div ng-repeat="book in vm.newBooksToAdd">
<!-- ngForm directive allows to create forms within the parent form -->
<ng-form name="bookForm">
<div>
<label>Book title</label>
<input ng-model="book.title" type="text" name="title" required>
</div>
<div>
<label>Author</label>
<input ng-model="book.author" type="text" name="author" required>
</div>
</ng-form>
</div>
</form>
In your controller, you can initialize the list of books to add as vm.newBooksToAdd = []; and whenever you want to add a new form to your list of forms for new books, just vm.newBooksToAdd.push({}) an empty object. Thus, you will send to the backend an array of objects representing books you want to create.
On Backend
Now here you'll need to overwrite the .create() method of your view to allow creating many instances at once, because by default it expects a single object. Your view might look like this:
class LibraryViewSet(views.ModelViewSet):
...
def create(self, request):
serializer = self.get_serializer(data=request.data, many=True) # notice the `many` keywork argument here
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.data, status=status.HTTP_201_CREATED)
Note: If you would like to allow both a single instance creation and creation in bulk, you'll need to adjust your .create() method to check for the data type of request.data.
Note 2: There is a django-rest-framework-bulk library that achieves what you want on the backend, but I didn't try it, so cannot say anything bad or good about it.
Good luck!
I'm a big fan of angularjs, I started lately to use it in all of my 'coding for fun' projects.
I have a big curiosity:
I have a two inputs, one disabled by a ng-disabled directive and the other disabled with an html tag (A better illustration in this link):
//...
<input type="text" disabled value="This is an html input text disabled" />
<input type="text" ng-disabled="true" value="disabled with angular js directive" />
//...
Using the browser ability I can right click on the input and remove the disabled and ng-disabled tags but only the one with the disabled tag would be editable, the other one will still be tracked by angular even when ng-disabled directives has been removed.
So, When and Why should I prefer using ng directives over native html tags? Which could be the impact of letting angular track all these actions? is it really worth to use it everywhere?
Use the native html 'disabled' if the element should always be disabled. (static, for example if you want to provide an input with text and never let the user change it)
Use angular if it should change based on variables value in the scope.
For example, say a button should change the state of an input.
<input type="button" ng-click="inpDisabled = true" >Disable Input</input>
<input type="text" ng-disabled="inpDisabled" />
live example
No harm will come if you still use ng-disabled="true" but it's redundant.
If you want to make directive static, you shoud use native html
<your-tag disable><your-tag>
against
<your-tag ng-disabled="true"><your-tag>
But AngularJS does not work this way, you shoud initialize your application and controller, then pass a variable as parameter to your directive:
JS:
$scope.isDisabled = true;
HTML:
<your-tag ng-disabled="isDisabled"><your-tag>
You shoud read more tutorials to make things clear
I have an app with many forms. Each field has several HTML elements, so I thought I could extract some directives (one per type of field) to keep my forms tidy.
I've created a sample app to demonstrate the problem, but I'm getting inconsistent behavior. In the sample app, a <link /> element replaces the <input />. In my real app, <input /> just gets removed from the DOM completely. I feel like this should be easy; why doesn't it work?
To answer your stated question, it's because you told it to, with ng-transclude. That replaces the contents of the tag with the original element, which I don't think you wanted; you probably wanted the original contents to be transcluded as the label instead.
This is probably what you're looking for:
<div class="form-group" >
<label for="{{htmlId}}" ng-transclude></label>
<input id="{{htmlId}}" class="form-control" type="text" ng-model="model" />
<span ng-repeat="error in errors">{{error}}</span>
</div>
I've moved the tranclusion into the label. While this works, I would also recommend the style of actually passing a label attribute, rather than transclude it, just for the sake of having a consistent API and simpler code; it's functionally equivalent, though, so don't let me bully you.
Also, you've got a few errors in your .js as well. First, you want to use = in your scope instead of &
scope: {
model: '=',
errors: '='
},
& is used to pass methods, while = is used for objects (this is a simplification). Since your model and errors are objects, you'll want to use = instead.
Finally, in your example, your html template and your directive's template don't have the same name... you've got an extra 's' in your .js, but that's probably just in the plunker and not your real app.
What is a way of finding all hidden input in a scope of a controller? And ideally can this be done when the controller is initizialied?
In my example I have mutliple comments like this:
<div ng-controller="CommentCtrl">
<form method="post">
<label>Leave Comment</label>
<textarea name="comment" ng-bind="comment"></textarea>
<input type="hidden" name="comment_id" value="1" />
<input type="hidden" name="site_id" value="2" />
</form>
</div>
So I the init, I only want to iterate through scope to find the hidden values of that controller and then assign it a value. Is there a way I can do this in AngularJS?
If you really need to access those hidden fields within your controller (which is not good a practice with angular, as #Ye Liu said above) try angular.element("input[type=hidden]"); It will give you a list with all the hidden inputs. You need to have jquery linked up in your html file before angularjs script.
You need to create a directive so you can access the DOM in the proper way.
Option 1
Create a directive that you put on the top DIV. In the link function of this directive, you can access the DOM element and find all hidden inputs.
link: function postLink(scope, iElement, iAttrs) {
angular.forEach(iElement.find('input'),function(inputElement) {
if(inputElement.attr('type') == 'hidden') {
// do something
}
});
}
This is using jqLite, which comes with Angular.
Option 2
Do something similar as Angular does with input and simular directives: create a directive based on standard HTML. So you could create a directive named 'type', restricted to attributes.