2011-12-07 9 views
6

Attualmente sto lavorando un plugin con una variabile impostazioni che è abbastanza profonda (3-4 livelli in alcuni posti). Seguendo il modello di jQuery Plugin generalmente accettato ho implementato un modo semplice per gli utenti di modificare le impostazioni al volo usando la seguente notazione:jQuery Plugin - modifica di queste opzioni profonda

$('#element').plugin('option', 'option_name', 'new_value'); 

Ecco il codice simile a quello che sto usando ora il metodo di opzioni.

option: function (option, value) { 
    if (typeof (option) === 'string') { 
     if (value === undefined) return settings[option]; 

     if(typeof(value) === 'object') 
      $.extend(true, settings[option], value); 
     else 
      settings[option] = value; 
    } 

    return this; 
} 

Consideriamo ora che ho una variabile impostazioni in questo modo:

var settings = { 
    opt: false, 
    another: { 
     deep: true 
    } 
}; 

Se voglio cambiare le deep impostazioni devo usare la seguente notazione:

$('#element').plugin('option', 'another', { deep: false }); 

Tuttavia, dato che in pratica le mie impostazioni possono essere di 3-4 livelli di profondità mi sento la seguente notazione sarebbe più utile:

$('#element').plugin('option', 'another.deep', false); 

Tuttavia io non sono sicuro di come fattibile questo è, né come fare per farlo. Come primo tentativo ho provato a "attraversare" l'opzione in questione e impostarla, ma se imposto la mia variabile di attraversamento non imposta ciò che fa riferimento nella variabile delle impostazioni originale.

option: function (option, value) { 
    if (typeof (option) === 'string') { 
     if (value === undefined) return settings[option]; 

     var levels = option.split('.'), 
      opt = settings[levels[0]]; 
     for(var i = 1; i < levels.length; ++i) 
      opt = opt[levels[i]]; 

     if(typeof(value) === 'object') 
      $.extend(true, opt, value); 
     else 
      opt = value; 
    } 

    return this; 
} 

Per dire che un altro modo: Impostando opt dopo aver attraversato, l'impostazione si riferisce in realtà al variabile settings è invariata dopo questo codice viene eseguito.

Mi scuso per la lunga domanda, ogni aiuto è apprezzato. Grazie!


EDIT

Come secondo tentativo posso farlo utilizzando eval() in questo modo:

option: function (option, value) { 
    if (typeof (option) === 'string') { 
     var levels = option.split('.'), 
      last = levels[levels.length - 1]; 

     levels.length -= 1; 

     if (value === undefined) return eval('settings.' + levels.join('.'))[last]; 

     if(typeof(value) === 'object') 
      $.extend(true, eval('settings.' + levels.join('.'))[last], value); 
     else 
      eval('settings.' + levels.join('.'))[last] = value; 
    } 

    return this; 
} 

Ma mi piacerebbe davvero vedere se qualcuno mi può mostrare un modo per non usare eval. Poiché si tratta di una stringa di input dell'utente, preferirei non eseguire eval() su di esso perché potrebbe essere qualsiasi cosa. Oppure fammi sapere se sono paranoico e non dovrebbe causare alcun problema.

risposta

2

Il problema si sta eseguendo in qui si riduce alla differenza tra le variabili che puntano ad oggetti e variabili per altri tipi come Stringhe.2 variabili possono puntare allo stesso Object, ma non nella stessa String:

var a = { foo: 'bar' }; 
    var b = 'bar'; 
    var a2 = a; 
    var b2 = b; 
    a2.foo = 'hello world'; 
    b2 = 'hello world'; 
    console.log(a.foo); // 'hello world' 
    console.log(b); // 'bar' 

Il codice attraversamento funziona grande fino all'ultima iterazione del ciclo, a quel punto opt è una variabile che contiene lo stesso valore come deep all'interno dell'oggetto settings.opt.another. Invece, tagliare il ciclo breve e utilizzare l'ultimo elemento di levels come key, come

var settings = { 
    another: { 
     deep: true 
    } 
    }; 

    var levels = 'another.deep'.split('.') 
    , opt = settings; 

    // leave the last element 
    var i = levels.length-1; 

    while(i--){ 
    opt = opt[levels.shift()]; 
    } 
    // save the last element in the array and use it as a key 
    var k = levels.shift(); 
    opt[k] = 'foobar'; // settings.another.deep is also 'foobar' 

In questa fase opt è un puntatore alla stessa Object come settings.another e k è un String con il valore 'deep'

+0

Fantastico, la confusione sui riferimenti c'era ciò che stava offuscando il problema. Grazie per averlo chiarito; Ho un metodo di lavoro ora grazie alla tua risposta. – Chad

0

ne dite di usare eval piuttosto che attraversamento?

var settings = { 
opt: false, 
    another: { 
deep: true, 

    } 
}; 

var x = "settings.another"; 
eval(x).deep = false; 
alert(settings.another.deep); 
+1

Stavo cercando di evitare l'uso di 'eval' su una stringa di input dell'utente, dal momento che usarlo in questo modo rende il mio plugin accetta javascript iniettato ... – Chad

+0

Non c'è sicurezza sul lato browser. Un utente può eseguire qualsiasi js che desidera e inviare qualsiasi query. –

0

Sarebbe il costruito nel jQuery $().extend() non essere esattamente quello che ti serve?

http://api.jquery.com/jQuery.extend/

* notare la seconda firma del metodo con il primo agument di true esegue un'unione profonda ...

+0

Vero, ma significa che dovrebbe usare la sintassi che spera di sostituire qui. L'obiettivo è quello di accedere a quelle variabili con notazione punto in un argomento stringa alla funzione. –

+0

Come ha detto Daniel, sto usando il metodo di estensione se l'utente passa un oggetto, tuttavia DEVONO passare un oggetto per le impostazioni profonde. Sto cercando di consentire loro anche l'opzione di una stringa. – Chad