如何将js中的字符串作为模板求值

时间:2018-11-01 09:12:49

标签: javascript

我想以集中控制的方式进行路由,为此我定义了一些视图,并且希望对其进行评估,就像它是以下格式的模板一样:

`this is a  ${variable} in the template`

这是我的代码:

const Views = Object.freeze({
    Home: {path:"/"},
    Kind:{path:"/${kind}", values:{kind:{notNull:true}}},
    SearchEntity:{path:"/${kind}/search/${name}", values:{kind:{notNull:true}, name:{}}},
    Entity:{path:"/${kind}/shop/${eid}", values:{kind:{notNull:true}, eid:{notNull:true}}},
    Issue:{path:"/${kind}/shop/${eid}/issue/${issueId}", values:{kind:{notNull:true}, eid:{notNull:true}, issueId:{notNull:true}}},
    NewShop:{path:"/${kind}/newShop/${name}", values:{kind:{notNull:true}, name:{}}},
})

eval.call({kind:"myKind", eid:"myeid", issueId:"myIssueId"}, Views.Issue.path)

这显然行不通,这就是为什么我问这个问题:)

eval方法无法理解上下文:

undefined:1
/${kind}/shop/${eid}/issue/${issueId}
^

SyntaxError: Invalid regular expression flags
    at Object.eval (<anonymous>)
    at Object.<anonymous> (C:\Users\Zied\work\weally\src\util.js:10:6)
    at Module._compile (internal/modules/cjs/loader.js:702:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
    at Module.load (internal/modules/cjs/loader.js:612:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
    at Function.Module._load (internal/modules/cjs/loader.js:543:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:744:10)
    at startup (internal/bootstrap/node.js:240:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)

自然地,我可以使用函数而不是模板来解决问题,但是我想拥有一个更清晰易读的代码,并且我想在中间插入更多控件(例如notNull验证检查等...) )集中。所以我想使声明与执行分开

1 个答案:

答案 0 :(得分:2)

不需要eval-将path属性设置为 function ,而该函数返回模板文字,并调用该函数:

const Views = Object.freeze({
  Issue: {
    path: ({kind, eid, issueId }) => `/${kind}/shop/${eid}/issue/${issueId}`,
    values: {
      kind: {
        notNull: true
      },
      eid: {
        notNull: true
      },
      issueId: {
        notNull: true
      }
    }
  },
});
console.log(Views.Issue.path({
  kind: "myKind",
  eid: "myeid",
  issueId: "myIssueId"
}));

如果您不能在path属性中放置函数,则可以使用正则表达式匹配字符串中的${varName}并将其替换为输入对象中的相同属性:< / p>

const Views = Object.freeze({
  Issue: {
    path: '/${kind}/shop/${eid}/issue/${issueId}',
    values: {
      kind: {
        notNull: true
      },
      eid: {
        notNull: true
      },
      issueId: {
        notNull: true
      }
    }
  },
});

const replace = (template, obj) => template.replace(/\${(\w+)}/g, (_, varName) => obj[varName]);
console.log(replace(Views.Issue.path, {
  kind: "myKind",
  eid: "myeid",
  issueId: "myIssueId"
}));

如果您也想验证:

const Views = Object.freeze({
  Issue: {
    path: '/${kind}/shop/${eid}/issue/${issueId}',
    values: {
      kind: {
        notNull: true
      },
      eid: {
        notNull: true
      },
      issueId: {
        notNull: true
      }
    }
  },
});

const replace = (template, conditions, obj) => {
  const required = Object.entries(conditions)
    .filter(([, { notNull }]) => notNull)
    .map(([key]) => key);
  if (!required.every(prop => obj.hasOwnProperty(prop))) throw new Error('required missing');
  return template.replace(/\${(\w+)}/g, (_, varName) => obj[varName]);
};


console.log(replace(
  Views.Issue.path,
  Views.Issue.values,
  {
  kind: "myKind",
  eid: "myeid",
  issueId: "myIssueId"
}));


console.log(replace(
  Views.Issue.path,
  Views.Issue.values,
  {
  kind: "myKind",
  eid: "myeid",
}));