0

I'm trying to write CRUD operations using ajax. Here some code: These are my View classes:

//PhotoSummary
@model PhotoAlbum.WEB.Models.PhotoViewModel
<div class="well">
    <h3>
        <strong>@Model.Name</strong>
        <span class="pull-right label label-primary">@Model.AverageRaiting.ToString("# stars")</span>
    </h3>
    <span class="lead">@Model.Description</span>
    @Html.DialogFormLink("Update", Url.Action("UpdatePhoto", new {photoId = @Model.PhotoId}), "Update Photo", @Model.PhotoId.ToString(), Url.Action("Photo"))
</div>

//Main View
@model PhotoAlbum.WEB.Models.PhotoListViewModel
@{
    ViewBag.Title = "My Photos";
}
@foreach (var p in @Model.Photos)
{
    <div [email protected]>
        @Html.Action("Photo", new {photo = p})
    </div>
}

The sript:

$('.dialogLink').on('click', function () {
    var element = $(this);       
    var dialogTitle = element.attr('data-dialog-title');
    var updateTargetId = '#' + element.attr('data-update-target-id');
    var updateUrl = element.attr('data-update-url');
    var dialogId = 'uniqueName-' + Math.floor(Math.random() * 1000)
    var dialogDiv = "<div id='" + dialogId + "'></div>";

    $(dialogDiv).load(this.href, function () {
        $(this).dialog({
            modal: true,
            resizable: false,
            title: dialogTitle,
            close: function () { $(this).empty(); },
            buttons: {
                "Save": function () {
                    // Manually submit the form                        
                    var form = $('form', this);
                    $(form).submit();
                },
                "Cancel": function () { $(this).dialog('close'); }
            }
        });

        $.validator.unobtrusive.parse(this);
        wireUpForm(this, updateTargetId, updateUrl);
    });
    return false;
});});


function wireUpForm(dialog, updateTargetId, updateUrl) {
    $('form', dialog).submit(function () {
        if (!$(this).valid())
            return false;
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {

                if (result.success) {
                    $(dialog).dialog('close');
                    $(updateTargetId).load(updateUrl);
                } else {             
                    $(dialog).html(result);
                    $.validator.unobtrusive.parse(dialog);

                    wireUpForm(dialog, updateTargetId, updateUrl);
                }
            }
        });
        return false;
    });
}

And here my Tag builder:

    public static MvcHtmlString DialogFormLink(this HtmlHelper htmlHelper, string linkText, string dialogContentUrl,
         string dialogTitle, string updateTargetId, string updateUrl)
    {
        TagBuilder builder = new TagBuilder("a");
        builder.SetInnerText(linkText);
        builder.Attributes.Add("href", dialogContentUrl);
        builder.Attributes.Add("data-dialog-title", dialogTitle);
        builder.Attributes.Add("data-update-target-id", updateTargetId);
        builder.Attributes.Add("data-update-url", updateUrl);
        builder.AddCssClass("dialogLink");
        return new MvcHtmlString(builder.ToString());
    }

So, I have major problem if the dialog was called twice without the calling page being refreshed: it just redirects me to the action page. The question is how to update @Html.Action without reloading the page? Could anyone help me?

7
  • You have not given enough information to be sure (you need to show the html your generating). Assuming your custom @Html.DialogFormLink() method is generating a <a> element with class=".dialogLink", then I'm guessing its replacing the contents of <div [email protected]> with a new partial view containing a @Html.DialogFormLink() method. If that is the case, you need to use event delegation, so the script needs to be $(document).on('click', '.dialogLink', function() { .... Commented Dec 19, 2015 at 3:27
  • Although its best to replace document with the closest ancestor which exists when you first render the page - e.g. <div id="xxx">@foreach (var p in @Model.Photos) { ... }</div> and then $(#xxx).on(... Commented Dec 19, 2015 at 3:28
  • @StephenMuecke Thank you very much! Using 'document' works fine! If I replasce it by the ancestor I have System.Web.HttpException: "Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'." Commented Dec 19, 2015 at 10:13
  • If that's the case, you have made an error somewhere :) Commented Dec 19, 2015 at 10:15
  • @StephenMuecke Yep! My fault. Made a misprint. Could you explain what's going on? I don't understand why do I need to add one more id or use #'document'? Also you can post your comments as an answer! Commented Dec 19, 2015 at 10:19

1 Answer 1

1

Your @foreach loop in the main view is generating a partial view for each Photo which in turn is creating a link with class="dialogLink".

Your script handles the click event of these links and replaces it with a new link with class="dialogLink". But the new link does not have a .click() handler so clicking on the new (replacement) link does not activate your script.

Instead you need to use event delegation to handle events for dynamically generated content using the .on() method (refer also here for more information on event delegation). Note also that your current use of $('.dialogLink').on('click', function () { is the equivalent of $('.dialogLink').click(function () { and is not using event delegation. It attaches a handler to elements that exist in the DOM at the time the page is loaded, not to elements that might be added in the future.

Change your html to

<div id="photos">
    @foreach (var p in @Model.Photos)
    {
        <div class="photo">@Html.Action("Photo", new { photo = p })</div>
    }
</div>

and then modify the script to

$('#photos').on('click', '.dialogLink', function() { 
    ....
});

Side note: There is no real need to add an [email protected] to the containing div element and you could use <div class="photo"> as per above, and then reference it by using var updateTargetId = $(this).closest('.photo'); and delete the builder.Attributes.Add("data-update-target-id", updateTargetId); line of code from your DialogFormLink() method

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.