1

I have a profile with a series of cards. Each card has a type (e.g. gallery card, quote card, etc) and the profile can have 1 or more of each type. I'm building a profile editor and want to be able to open an edit form in a modal. Each card has its own type of form with unique inputs, but there is only one form per card type, since we can't know in advance how many cards of each type a profile will have.

For each form, I have a number of ng-init directives to prepopulate the edit form with the current model's attributes. This works great except that if I have more than one card of a type, each card's edit form will have the data from the last card in the set. Is there a way I can trigger the ng-init directives to run again when I open the form (e.g. with an ng-click) so that the user will always see the current model's attributes?

Here is what one of my card forms looks like:

<% if c.class.name == 'QuoteCard' %>
<div class="row-scrollable" ng-show="view === 'quote-card'">
    <div class="col-lg-12">
        <form accept-charset="UTF-8" name="editQuoteCardForm" ng-submit="update({card: editQuoteCard, type: 'quote_card'})" novalidate>
            <input name="utf8" type="hidden" value="✓">
            <input name="authenticity_token" type="hidden" ng-model="editQuoteCard.token" ng-init="editQuoteCard.token='<%= form_authenticity_token %>'">
            <input name="id" type="hidden" ng-model="editQuoteCard.id" ng-init="editQuoteCard.id='<%= c.id %>'">

            <div class="form-group">
                <label>Title</label>
                <br style="clear:both;" />
                <input type="text" class="form-control" name="title" ng-init='editQuoteCard.title = "<%= c.title %>"' ng-model="editQuoteCard.title" required>
            </div>

            <div class="form-group">
                <label>Attribution</label>
                <br style="clear:both;" />
                <input type="text" class="form-control" name="attribution" ng-init='editQuoteCard.title = "<%= c.attribution %>"' ng-model="editQuoteCard.attribution">
            </div>

            <div class="form-group">
                <label>Quote</label>
                <br style="clear:both;" />
                <textarea rows="3" class="form-control" name="quote" ng-init='editQuoteCard.title = "<%= c.quote %>"' ng-model="editQuoteCard.quote" required></textarea>
            </div>

            <div class="form-group">
                <label>Media</label>
                <br style="clear:both;" />
                <input type="hidden" class="form-control" name="photo" ng-model="editQuoteCard.image">
                <input type="hidden" class="form-control" name="video" ng-model="editQuoteCard.video">
                <%= photo = Photo.find_by(quote_card_id: c.id) %>
                <%= video = Video.find_by(quote_card_id: c.id) %>
                <div class="profile-builder attachment"
                    ng-init='<%= "editQuoteCard.image = #{{ image_url: photo.image.url, id: photo.id }.to_json}" unless photo.nil? %>'
                    ng-init='<%= "editQuoteCard.video = #{{ media_url: video.media_url, media_type: video.media_type, id: video.id }.to_json}" unless video.nil? %>'>
                    <div ng-show="editQuoteCard.video" class="content video-content result iframe">
                        <div class="caption delete-btn" ng-click="editQuoteCard.video = undefined;">
                            <i class="fa fa-times"></i>
                        </div>
                        <iframe ng-if="editQuoteCard.video.media_type === 'Youtube'" ng-src="{{editQuoteCard.video.media_url + '?showinfo=0&autohide=1' | trustAsResourceUrl}}" frameborder="0" id="ytplayer" type="text/html"></iframe>
                        <iframe ng-if="editQuoteCard.video.media_type === 'Vimeo'" ng-src="{{editQuoteCard.video.media_url + '?badge=0&byline=0&title=0' | trustAsResourceUrl}}" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
                    </div>
                    <div ng-show="editQuoteCard.image" class="content result">
                        <div class="delete-btn" ng-click="editQuoteCard.image = undefined;">
                            <i class="fa fa-times"></i>
                        </div>

                        <div class="image-container" ng-style="{'background-image': 'url(' + editQuoteCard.image.image_url + ')'}"></div>
                    </div>
                </div>
            </div>
            <br style="clear:both;" />

            <div class="form-group">
                <input class="btn btn-primary" style="float:right;" name="commit" type="submit" value="Update Card" ng-disabled="editQuoteCardForm.$invalid">
                <div class="btn btn-default" style="float:right;margin-right:10px;" ng-click="openMediaBrowser({type: '<%= c.class %>', id: <%= c.id %>, index: <%= i %>});" ng-show="!editQuoteCard.image && !editQuoteCard.video">Add Media</div>
            </div>
        </form>
    </div>
</div>
<% end %>

And in my controller, I have an edit function that opens the modal with the correct form:

$scope.edit = function(options) {
    modal.open({
        content: $('#edit_card_' + options.index + '_form'),
        elem: $('#edit_card_' + options.index + '_form_container')
    })
};
Daniel Bonnell
  • 4,817
  • 9
  • 48
  • 88
  • It looks like you are trying to do a mix of server-side rendering and client-side rendering. Might I ask why? Most AngularJS apps are SPAs, and do all rendering on the client, using the server mainly for data. – GregL Dec 01 '15 at 01:20
  • `ng-init` triggers when the markup is "compiled" by AngularJS. If you are only inserting this markup once, then it will only evaluate the `ng-init` expressions once as well. `ng-init` is an anti-pattern in Angular, only useful in limited cases. You would be better off creating a directive for this. – GregL Dec 01 '15 at 01:27
  • you could use `ng-change` in addition to your `ng-init` in the inputs – floribon Dec 01 '15 at 01:33
  • @GregL Long story short I was trying to leverage some of Angular's functionality on an existing feature that wasn't built in Angular to save time, but it backfired. I've since gone back and refactored the whole profile builder into an SPA. Now I've gotten around the the initial problem except I'm still not sure how to handle form names. I have 3 types of forms (eg. `editQuoteCardForm`) but the names are all the same, so I can't validate them. – Daniel Bonnell Dec 02 '15 at 02:02

0 Answers0