I have a HTML form rendered by Django template tag and I want to manipulate it using AngularJS.
In my template (not rendered) I have:
<html>
<body>
{{ form.as_p }}
</body>
</html>
This is how Django renders this template:
<html>
<body>
<form>
<div id="div_id_street" class="ctrlHolder ">
<label for="id_street">
Street<span>*</span>
</label>
<input id="id_street" type="text" class="textInput textinput" name="street" maxlength="32">
</div>
</form>
</body>
</html>
I want to hide the div div_id_street, but I am not able to add the ng-show or ng-hide directive in my HTML because of the way that Django generates the HTML code.
Is it possible inside an AngularJS controller to show/hide this div?
Thank you!
I was wrong. It is possible to change the template of a Django rendered form.
When defining a django form, I am able to change the widget of each fiels, so I can put the Angular directives in the widget.
For example:
# forms.py
class MyForm(forms.Form):
street = forms.CharField(max_length=80,
widget=forms.TextInput(attrs={
'ng-model': 'street',
'ng-show': 'false',
'ng-change': 'validateStreet()',})
)
This will be rendered as:
<div id="div_street" class="ctrlHolder">
<label for="id_street">Street</label>
<input ng-model="street" name="street" maxlength="80" ng-change="validateStreet()"
ng-show="false" type="text" class="textInput textinput ng-valid ng-dirty"
id="id_street">
</div>
So in my controller I can manipulate this whole field.
Is it possible inside an AngularJS controller to show/hide this div?
Yes, but this "goes deep against the Angular way" -- Misko. Inject $element, then use selectors to find and manipulate. $element is set to the element that your controller is defined on.
function MyCtrl($scope, $element) {
$scope.show = false;
$scope.$watch('show', function (value) {
if (value) {
$element.find('#div_id_street').show();
} else {
$element.find('#div_id_street').hide();
}
})
}
Fiddle -- in this fiddle I used ng-controller.
If you can't modify the template, neither add a class to that specific DIV to add it a ng-show, than you should create a directive to handle this DOM manipulation. You shouldn't do DOM manipulation from within a controller. As stated by the DOCs,
Do not use controllers for:
Any kind of DOM manipulation — Controllers should contain only
business logic. DOM manipulation—the presentation logic of an
application—is well known for being hard to test. Putting any
presentation logic into controllers significantly affects testability
of the business logic. Angular offers databinding for automatic DOM
manipulation. If you have to perform your own manual DOM manipulation,
encapsulate the presentation logic in [directives]http://docs.angularjs.org/guide/directive).
...
I would suggest you to $watch the property you need and manually show/hide the element from inside the link function of a directive.
Related
I see that ember has a very nice mechanism for wrapping content in a component using the {{yield}} mechanism documented here.
So, to use the example in the documentation, I can have a blog-post component template defined like so:
<script type="text/x-handlebars" id="components/blog-post">
<h1>{{title}}</h1>
<div class="body">{{yield}}</div>
</script>
I can then embed blog-post into any other template using the form:
{{#blog-post title=title}}
<p class="author">by {{author}}</p>
{{body}}
{{/blog-post}}
My question is, can I specify two different {{yield}} outlets in the components template?
Something like this is possible via Named Outlets in Ember.Route#renderTemplate like so:
Handlebars:
<div class="toolbar">{{outlet toolbar}}</div>
<div class="sidebar">{{outlet sidebar}}</div>
JavaScript:
App.PostsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ outlet: 'sidebar' });
}
});
I'm not sure I can take this path for a component which will not know what route's template would be rendering it.
EDIT 1:
For the sake of clarity, I'm trying to implement the Android Swipe for Action Pattern as an Ember component.
So, I'd like users of this component to be able to specify two different templates:
A template for the normal list item, and
A template for the actions that are revealed when a swipe on (1) is detected.
I want to make this into a component, because quite a lot of javascript goes into handling the touch(start/move/end) events, while still managing smooth touch based scrolling of the list. Users would supply the two templates and this component would manage handling of touch events and necessary animations.
I've managed to get the component working in the block form, where the block's contents are treated like (1). The second template (2) is specified through a parameter (actionPartial below) which is the name of a partial template for the actions:
Component Handlebars Template: sfa-item.handlebars
<div {{bind-attr class=":sfa-item-actions shouldRevealActions:show" }}>
{{partial actionPartial}}
</div>
<div {{bind-attr class=":sfa-item-details isDragging:dragging shouldRevealActions:moveout"}}>
{{yield}}
</div>
Calling Handlebars Template:
{{#each response in controller}}
<div class="list-group-item sf-mr-item">
{{#sfa-item actionPartial="mr-item-action"}}
<h5>{{response.name}}</h5>
{{/sfa-item}}
</div>
{{/each}}
Where the mr-item-action handlebars is defined like so:
mr-item-action.handlebars:
<div class="sf-mr-item-action">
<button class="btn btn-lg btn-primary" {{action 'sfaClickedAction'}}>Edit</button>
<button class="btn btn-lg btn-primary">Delete</button>
</div>
Problem is, actions from the user supplied partial, sfaClickedAction above, are not bubbled up from the component. A fact which is mentioned in the docs in para 4.
So, now I do not know how a user could capture actions that he defined in the supplied actions template. A component cannot catch those actions because it doesn't know about them either.
EDIT 2
I sprung a follow up question here
This blog post describes the most elegant solution for Ember 1.10+: https://coderwall.com/p/qkk2zq/components-with-structured-markup-in-ember-js-v1-10
In your component you pass yield names into {{yield}}s:
<header>
{{yield "header"}}
</header>
<div class="body">
{{yield "body"}}
</div>
<footer>
{{yield "footer"}}
</footer>
When you invoke your component, you accept the yield name as a block param... and use an esleif chain!
{{#my-comp as |section|}}
{{#if (eq section "header")}}
My header
{{else if (eq section "body")}}
My body
{{else if (eq section "footer")}}
My footer
{{/if}}
{{/my-comp}}
PS eq is a subexpression helper from the must-have ember-truth-helpers addon.
PPS Relevant RFC: proposal, discussion.
Since it is not possible to have two {{yield}} helpers within one component (how would the component know where one {{yield}}'s markup stops and the next one begins?) you may be able to approach this problem from a different direction.
Consider the pattern of nested components. Browsers do this already with great success. Take, for example, the <ul> and <li> components. A <ul> wants to take many bits of markup and render each one like a member of a list. In order to accomplish this, it forces you to separate your itemized markup into <li> tags. There are many other examples of this. <table>, <tbody>, <tr>, <td> is another good case.
I think you may have stumbled upon a case where you can implement this pattern. For example:
{{#sfa-item}}
{{#first-thing}}
... some markup
{{/first-thing}}
{{#second-thing}}
... some other markup
{{/second-thing}}
{{/sfa-item}}
Obviously first-thing and second-thing are terrible names for your specialized components that represent the things you'd want to wrap with your first and second templates. You get the idea.
Do be careful since the nested components won't have access to properties within the outer component. You'll have to bind values with both outer and inner components if they are needed in both.
We're building app that allows users to post messages to various social media outlets. Our designer has created a series of interactions which allow users to change various settings in their application by use of sliding panels. I've done a quick screen cap to illustrate:
http://screencast.com/t/tDlyMud7Yb7e
The question I have is one of architecture. I'm not sure whether I should be using a View or a Controller (or both) to store some of the methods these panels will contain. Here's the HTML for the panels. They're not currently in a script tag or view:
<div id="panel-account-settings" class="panel closed">
<div class="panel-inner">
<i class="icon-cancel"></i>close
<h3>Account Settings</h3>
Google Analytics
Link Shortening
Disconnect Account
</div>
<div id="panel-google-analytics" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Google Analytics</h3>
<div class="toggle">
<label>Off</label>
</div>
<p>We <strong>won't</strong> append stuff to your links, so they <strong>won't</strong> be trackable in your Google Analytics account.</p>
<img src="{{ STATIC_URL }}images/ga-addressbar.png" />
</div>
<div id="panel-disconnect" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Disconnect This Account</h3>
<p>If you disconnect this account you will lose all the metrics we tracked for each message. Are you absolute sure you want to get rid of them?</p>
<div class="button-group">
Disconnect
</div>
</div>
</div>
The gear icon shown in the video is contained with the accounts template
<script type="text/x-handlebars" data-template-name="accounts">
{{#each account in controller}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>#{{account.username}}</p>
<i class="icon-cog" {{action "openPanel" Social.SettingsView account }}></i>
</div>
{{/each}}
</script>
which has a bare bones controller
Social.AccountsController = Ember.ArrayController.extend({
openPanel: function(view,account){
console.log(view,account);
$(this).parents(".item-account").addClass("active");
$("#panel-account-settings").prepareTransition().removeClass("closed");
}
});
as well as a Route and a Model. Given the interaction I'm looking to accomplish, my question is where should I be putting the pieces and parts? At a minimum I need to pass in the current Account model so that I know which account I'll be applying changes to. I thought about creating a mainPanel view which would contain the other view...something like this:
<script type="text/x-handlebars" data-template-name="panelView">
<div id="panel-account-settings" class="panel closed">
{{ partial "panelSettingsView" }}
{{ partial "panelAnalyticsView" }}
{{ partial "panelDisconnectView" }}
</div>
</script>
and then the action helper on the gear icon could pass in the account AND the required view. But I'm not sure if that's the right approach. I'd appreciate some input or suggestions. Thanks.
UPDATE 1
Ideally I'd like to eventually load in the content of each panel via AJAX but that's a want to, not a need to.
UPDATE 2
I tried creating a PanelView which would contain the logic on which panels to load:
Social.PanelView = Ember.View.extend({
tagName: 'div',
classNames: ['panel-inner'],
openPanel: function(view,account){
console.log(view,account);
}
});
But when I tried to call it from the gear icon I got an error. This:
<i class="icon-cog" {{action openPanel target="Social.PanelView" }}></i>
Threw this error:
Uncaught Error: assertion failed: The action 'openPanel' did not exist on Social.PanelView
Isn't that the correct syntax?
UPDATE 3
Adding version information:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.1
The best practice is to always put any DOM- or UI-related logic into your view, and leave data representation to the controller (i.e., a reference to a 'selected' item in the controller is a common example).
Your Social.AccountsController.openPanel method has logic that touches the DOM, which is entirely a view concern. A good start would be to move that logic into the view (Social. SettingsView ?).
It'd be a bit easier to understand your goals and offer more suggestions if you had a jsfiddle of what you have so far.
EDIT: Another good practice is to decompose things into very small objects. So you could explore having a selectedAccount ObjectController whose content is the currently chosen Account (and a corresponding View for it).
I am using Twitter Bootstrap's modals throughout a web application. I am also using Mustache templates to generate the information to display inside the modals. The problem is that I find myself creating new modals for nearly every single form that is rendered to the screen and I feel that this violates DRY. I am considering creating a global modal object that is defined in the 'window' object and can be accessed throughout my application. When I want to display a new form I just render the form into the global modal object and then show it. Can anyone give me some advice on how to better handle numerous forms with modals?
I think you have the right idea. If you have a lot of modals, creating new ones can get repetitive. I've done something similar to what you proposed: create a single modal object that can be reused for a variety of modals.
In the past I used jQuery dialog, but the principle is entirely the same. Create a JavaScript module with some boilerplate HTML, that you can use to display any number of forms (essentially HTML content).
I'll try to propose a very basic implementation without knowing too much about your application.
HTML based from the Bootstrap example here:
<!-- Modal -->
<div id="myModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="dynamicHeader">
<!-- Our header will go here -->
</h3>
</div>
<div class="modal-body" id="dynamicBody">
<!-- Our body will go here -->
</div>
<div class="modal-footer">
Close
Save changes
</div>
</div>
Notice the unique id's I've added to the h3 and the div.modal-body. We'll use those in JavaScript to dynamically inject each form's title and content.
JavaScript:
var ModalManager = (function() {
// cache some DOM references
var $dynamicHeader = $('#dynamicHeader');
var $dynamicBody = $('#dynamicBody');
var $myModal = $('#myModal');
var launch = function(header, body) {
$dynamicHeader.html(header);
$dynamicBody.html(body);
$myModal.modal(/* options here */);
};
return {
launch: launch
/* expose more API methods here! */
};
}());
Here is an example usage!
HTML w/ JavaScript:
<div id="form1">
<div class="formHeader">
Form One
</div>
<div class="formBody">
<p>Html and stuff</p>
</div>
</div>
<script type="text/javascript">
// Using a closure to protect globals
// This would probably go in your click handler to launch a given modal
(function() {
var headerHtml = $('form1 .formHeader').html();
var bodyHtml = $('form1 .formBody').html();
ModalManager.launch(headerHtml, bodyHtml );
}());
</script>
Finally, I wrapped all of that up in a jsFiddle which demonstrates the ability to launch two different forms.
Link: jsFiddle
I used jQuery as it should be included with the Bootstrap code for the modal. It will pull out the header and body HTML that are specific to each form, and populate your common modal HTML in the DOM. Then when you launch the modal it will display what looks like a different modal, but you've centralized the common aspects so you're not repeating them anymore!
There's a lot more you can do but that's basically the gist. My own implementation exposed means to configure the buttons dynamically, for example. Depending on what you want configurable, you can add an options parameter that passes on to the modal() function, or has other properties specific to your application that ModalManager can handle. You can definitely use templating to carry out some of these features, it's just not essential to the example I've setup.
I hope that helps!
I'm not quite sure what you are asking. Mustache does have looping functionality, so you could pass in an array of modals, and Mustache should generate all of the code.
I actually just created a video showing how to build a Mustache template for Twitter Bootstrap's Alert component, and implement it via PHP and JavaScript. It also features the looping functionality I spoke of. Maybe that will help? Here is the link: http://mikemclin.net/mustache-templates-for-php-and-javascript/
Given this chunk of HTML:
<div id="email_field" class="control-group">
<label class="control-label" for="account.email">Email</label>
<div id="email_input" class="controls">
<input id="account.email" name="account.email" type="text" placeholder="jpublic#example.com">
<span class="help-block">We just need a valid email address.</span>
</div>
</div>
How do I turn this into a re-usable partial for whatever attribute I want? IE: email, password, password confirmation, etc.
I would assume some sort of view hierarchy but I'm not quite sure.
EDIT: After further exploration I've knocked out {{view}} and {{render}} and figured out exactly what I need:
I want to:
1. Use a specific view (InputView)
2. Use a specific controller (Preferably similarly named: InputController) ({{view}} doesn't do this I think)
3. Be able to use this multiple times ({{render}} can't do this)
4. Be able to pass in values ({{render}} can't do this)
Example:
<!-- templates/application.hbs -->
{{foo "input" name="Email" id="account.email" placeholder="jpublic#email.com"}}
// controllers/input.js
Application.InputController = Ember.ObjectController.extend({
type: "text"
});
// views/input.js
Application.InputView = Ember.View.extend({
templateName: "form/input"
});
<!-- templates/form/input.hbs -->
<input {{bindAttr id="id" name="name" type="type" placeholder="placeholder"}}>
I would create a view that takes all the parameters that are variable. Such as:
{{view App.FormEntity
name="email"
placeholder="My placeholder..."
help="We just need a valid email address."
valueBinding="value"
}}
From there you could extract the label, the various class names, and then use Ember.TextField to bind the value to.
Once you have all of those arguments passed into the view, it should be nice and easy to create the markup using a mixture of bindAttrs, a couple of computed properties, and Ember helpers (such as the Ember.TextField).
I am new to Emberjs and looking for pretty much the same thing, but couldn't you also simply use http://emberjs.com/guides/templates/writing-helpers/ for that?
I will try it myself, so can give more updates if that works out.
Update:
Ok, I got it to work. I created a new Helpers folder with FormgeneratorHelper.js and the following code:
Ember.Handlebars.registerBoundHelper('control-group', function (options) {
var name = options.hash.test.capitalize();
console.log(name);
return new Handlebars.SafeString('<div class="control-group"> \
<label class="control-label" for="input' + name + '">' + name + '</label> \
<div class="controls"> \
<input type="text" id="input' + name + '" placeholder="' + name + '" /> \
</div> \
</div>');
});
An then, no matter in which template you can do:
{{control-group test="email"}}
I really like the idea of using helpers, but if you are using plain Javascript (as opposed to CoffeScript) and have more than one line of code, then it gets a bit ugly unfortunately. But will probably still use that method.
How do I turn this into a re-usable partial for whatever attribute I want? IE: email, password, password confirmation, etc.
What you want is the experimental {{control}} helper. The control helper is currently under development and is considered experimental. To enable it, set ENV.EXPERIMENTAL_CONTROL_HELPER = true before requiring Ember.
I want to:
1. Use a specific view (InputView)
2. Use a specific controller (Preferably similarly named: InputController) ({{view}} doesn't do this I think)
Out-of-box the control helper expects to be passed a template name. That template name is used to lookup a matching view and controller. So for example:
App.InputView = Ember.View.extend()
App.InputController = Ember.Controller.extend()
{{control input}}
See:
A control renders a template with a new instance of the named controller and view
A control's controller and view are lookuped up via template name
Be able to use this multiple times ({{render}} can't do this)
A control can be used multiple times
Be able to pass in values ({{render}} can't do this)
Like the {{view}} helper, {{control}} will accept arbitrary name/value pairs. So as in your example, one could manually pass options to the control helper. Like the {{view}} helper these options become properties on the view instance:
<!-- templates/form/input.hbs -->
<label class="control-label" {{bindAttr for="view.inputId"}}>
{{view.label}}
</label>
<div class="controls">
<input {{bindAttr id="view.inputId" name="view.name" type="type" placeholder="view.placeholder"}}>
<span class="help-block">{{view.help}}</span>
</div>
// controllers/form_input.js
App.FormInputController = Ember.ObjectController.extend({
type: "text"
});
// views/form_input.js
App.FormInputView = Ember.View.extend({
classNames: ["control-group"]
});
<!-- templates/application.hbs -->
{{control "form/input"
inputId="account.email"
name="email"
label="Email"
placeholder="jpublic#email.com"
help="We just need a valid email address."
}}
See this jsbin for working example
Also keep in mind that A control can specify a model to use in its template - with this in place we can bind properties to model data. Also if a controller's model changes, its child controllers are destroyed so the control will reset as expected if the model is swapped out.
I have a {{view Ember.TextField action="foo"}} nested in a <form> tag:
My form:
<form>
{{view Ember.TextField action="foo"}}
</form>
I hoped pressing enter in this textfield will call the action foo, without triggering a submit event on its form (because, by default, Ember.TextField#bubbles is set to false). But it is not the case: the page is reloaded.
For semantic and integration purpose, I would like to keep the <form> tag, and do not write an Ember.Form view.
You can test it in this JSFiddle.
How could I achieve this ?
PS: I'm using ember-latest:
version: v1.0.0-pre.4-31-g16442c5
last commit: 16442c5 (2013-01-23 23:48:09 -0800)
<form {{ action "" on="submit" }}> will prevent the form submission. (This is basically equivalent to the onsubmit="return false;" suggestion in another answer.)
I think the simpliest way is to add onsubmit="return false;" on the form element. Or with jQuery , preventDefault();
I'm sure this is not the best way but it work!