Сохранение свойств на лету

0

Я работаю над профилем для пользователей на моем веб-сайте ASP.NET MVC. И у меня есть много свойств (флажки, выпадающие списки и т.д.), Например:

  • Часовой пояс
  • DST
  • Настройки электронной почты

Я могу сделать свою собственную модель для обработки всех из них следующим образом:

public class ProfileViewModel
{
    public TimeZoneOptions TimeZone { get; set; }
    public DstOptions Dst { get; set; } // Daylight Saving Time
    public bool EmailSettings1 { get; set; }
    public bool EmailSettings2 { get; set; }
    // ...
    public bool EmailSettings22 { get; set; }
}

Мои свойства модели просмотра не привязаны друг к другу. Поэтому мне не нужно вызывать ModelState.IsValid. Кажется, моя модель всегда будет действительной.

Если я буду следовать стандартным способам сохранения модели в ASP.NET MVC, это заставит моего клиента дважды щелкнуть:

  1. Измените свойство
  2. Нажмите кнопку "Сохранить".

Но я хочу, чтобы они сделали это одним щелчком мыши. Как на этом скриншоте:

Изображение 174551

Я хочу, чтобы каждое свойство было отмечено "Изменения сохранены", когда пользователь нажимает на них. И я хочу достичь этого с минимальным количеством кода. Кажется, что это легко сделать с:

  • Только jQuery

или

  • Knockout.js

Но я не могу решить, что лучше. В jQuery я могу напрямую манипулировать с DOM. Но, похоже, с нокаутом он может быть более элегантным, и я могу сделать мой код более удобным.

Я не знаю, как это сделать с нокаутом, может быть какой-то привязкой, необходимой для обновления элемента, когда он сохраняется следующим образом:

<span data-bind="display-when-saved-for-property: EmailSettings1"></span>

???

Возможно, есть и другие способы сделать это. Любая помощь будет высоко оценен.

  • 0
    Просто создайте событие onchange для ваших флажков, которое вызовет ajax вашему контроллеру. Ваш контроллер сохранит данные, а обратный вызов AJAX отобразит сообщение об изменениях. Вот как бы я это сделал. Дайте мне знать, если вы хотите больше деталей
  • 0
    @heymega, спасибо. И как определить, какое свойство было изменено? Я должен передать всю модель контроллеру, верно? Так что это уникальный URL-адрес AJAX для сохранения модели для всех свойств. Как я могу отобразить «Изменения сохранены» для определенного свойства?
Показать ещё 1 комментарий
Теги:
asp.net-mvc
knockout.js

3 ответа

1
Лучший ответ

Я понимаю, что у вас есть список пользовательских настроек, которые будут отображаться как checkbox или dropdownlist список.
Если это так, вы правы, что KnockoutJS более изящный способ (у меня была такая же проблема, прежде чем у меня была кнопка сохранения, мне повезло). Посмотрим, что у нас есть:
У вас есть модель с большей частью свойствами bool, то, без сомнения, вы должны использовать плагин отображения KnockoutJS. Зачем? все, что мы собираемся сделать, это:

  1. Написать view HTML - разметку в обычном режиме.
  2. Для первой загрузки нашего view сделайте запрос AJAX, чтобы получить конфигурацию профиля пользователя, а затем используя mapping plugin преобразования, преобразуйте возвращенные данные в ViewModel и привяжите его к нашему view (все это без необходимости писать ViewModel самим, что означает, что если ViewModel на сервере изменилось, нам просто нужно изменить только view).
  3. Для каждого checkbox или dropdownlist добавьте обработчик события change (используя jQuery).
  4. Функция change просто преобразует нашу привязанную ViewModel в строку JSON снова, используя mapping plugin (всего одну строку кода)
  5. Наконец, был сделан еще один запрос AJAX для сохранения изменений.

Чтобы показать сообщение "Изменения сохранены", вы можете просто передать refrence входного сигнала вызова и использовать немного магии jQuery, чтобы добавить ваше сообщение.

Обновить:
Ваш может выглядеть так (или, может быть, лучше моего):

var UserProfile;
$(document).ready(function() {
    $('#ViewContainer input:checkbox').change(function (event) {
        SaveUserProfile(event);
    });
    IntializeUserProfile();
});

function IntializeUserProfile() {
    // Show spinner
    $.ajax({
        type: "POST",
        url: URL_To_Get_User_Profile,
        data: UserId, // in case of you need to pass data.
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(data) {
            UserProfile = ko.mapping.fromJSON(data); //ko.mapping.fromJS($.parseJSON(msg));
            ko.applyBindings(UserProfile, selector);
            // Hide Spinner
        }
    });
}

function SaveUserProfile(event) {
    // Show Spinner
    var model = ko.toJSON(ko.mapping.toJS(UserProfile));
    event = event || window.event;
    target = event.target || event.srcElement;
    $.ajax({
        type: "POST",
        url: URL_To_Save_UserProfile,
        data: model,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(data) {
            // Hide Spinner

            // here you have the caller input ==>target.
            // for example, you can get it parent to append your message
            // or just show message in the page as notification.
        }
    });
}
  • 0
    Спасибо, это именно то, как я думаю, это должно работать. Но! Если я использую плагин сопоставления, как я могу передать ссылку на вызывающий вход? Есть ли способ сделать это автоматически? И как я могу отобразить «Изменения сохранены» только в случае успеха?
  • 0
    @RomanPushkin проверь мое обновление
3

Я думаю, что это может быть реализовано с нокаутом + jQuery. С нокаутом очень легко создать приложение с изменением макета (как в вашем случае). JQuery в вашем случае требуется для асинхронных запросов.

Итак, viewmodel:

var ViewModel = function(data) {
    var self = this;
    var _isCurrentSavingProp = null;

    self.EmailSettings1 = ko.observable(data.EmailSettings1);
    self.IsEmailSettings1Saved = ko.observable();
    ... another props

    self.EmailSettings1.subscribe(function(val) {            
        isCurrentSavingProp = self.IsEmailSettings1Saved;
        self.save();
    });
    ... another subscribers

    self.save = function() {
        isCurrentSavingProp(false);
        $.post(url, ko.toJSON(self), function() {
            isCurrentSavingProp(true);
        })
    }  
}

Html:

<span data-bind="visible: IsEmailSettings1Saved" style="display: none"></span>
<input type="checkbox" data-bind="value: EmailSettings1" />
... etc.. 
  • 0
    +1, но в этом случае мы должны написать минимум 5 строк кода для каждого свойства. Это нормально, если у меня их мало. Но что, если у меня их много? Что если у меня много форм, и не только эта? Мой код очень скоро станет неуправляемым. Поэтому я не могу принять это как решение, извините. Я думаю, что это может быть сделано через какое-то связывание, но я не уверен, если это возможно.
  • 0
    Я предложил решение для вашей текущей реализации. Если бы вы спросили, как улучшить ваше решение, я бы посоветовал не создавать поле bool для каждого параметра, а сохранять его в массиве. В этом случае размер кода сервера и клиента будет минимизирован.
Показать ещё 2 комментария
1

Хорошо, что ваша модель должна быть чем-то вроде этого.

public class MyModel
{

    public bool? Friends { get; set; }
    public bool? Photos { get; set; }
    public string Car { get; set; }

}

Ваш метод контроллера принимает экземпляр вашей модели. Пока ваш запрос ajax имеет имя данных, которое соответствует имени свойства в вашей модели, оно будет связываться.

    public void UpdateValues(MyModel model)
    {

        if(model.Friends != null)
        {
            //Save
        }
        if (model.Photos != null)
        { 
            //Save
        }
        if (model.Car != null)
        { 
            //Save
        }


    }

Допустим, ваша html-разметка похожа на эту

<div id="checkbox-container">
    <div>    
        <label>
            <input type="checkbox" name="friends" />
        My Friends</label>
    </div>
    <div>
        <label><input type="checkbox" name="photos" />My Photos</label>
    </div>
    <select id="car-dropdown">
      <option value="volvo">Volvo</option>
      <option value="saab">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select> 
</div>

И вот jQuery....

$('#checkbox-container input:checkbox').change(function () {
    //Gets the property name we will bind
    var field = $(this).attr("name");
    var isChecked = $(this).prop("checked");

    var data = {};
    data[field] = isChecked;

    requestBuilder(data);

 });

//New method for our dropdown
$('#car-dropdown').change(function()
{


    var data = {};
    data['Car'] = $(this).val();

    requestBuilder(data);


});


function requestBuilder(data)
{

    //Ajax Request
    $.ajax({
    //Change url to match your controller
    url:"/Controller/UpdateValues/",
    data:data,
    type:"POST",
    success:function(response)
    {
       alert("Change Saved")
    }
    });

 }

Обратите внимание, как мы передаем атрибут Name = "". Это соответствует имени, которое у нас есть в нашей модели, поэтому оно будет связываться :)

Дайте мне знать, если вам нужно, чтобы я расширил любой элемент. Я также создал jsfiddle для кода переднего конца - http://jsfiddle.net/8tKBx/

Удачи!

  • 0
    Хорошо спасибо! И теперь я хочу, чтобы «Изменения сохранены» отображались рядом с выбранным свойством. Например, если я нажимаю «Мои друзья», я хочу, чтобы «Изменения сохранялись» отображались сразу после этого элемента. Но только в случае успеха. А что если у меня есть выпадающий список? Я думаю, что мы не можем отправить только одно свойство, было бы проще отправить всю модель на сервер.
  • 0
    Есть несколько разных способов сделать это. Вот некоторые изменения, которые я внес, но вы можете поэкспериментировать и сделать это самостоятельно. jsfiddle.net/8tKBx/4 . Я отредактирую свой оригинальный ответ, чтобы включить раскрывающийся список. дай мне 5 минут

Ещё вопросы

Сообщество Overcoder
Наверх
Меню